Roo/bootstrap/SecurePass.js
[roojs1] / roojs-bootstrap-debug.js
1 /**
2  * set the version of bootstrap based on the stylesheet...
3  *
4  */
5
6 Roo.bootstrap.version = ( function() {
7     var ret=3;
8     Roo.each(document.styleSheets, function(s) {
9         if ( s.href  && s.href.match(/css-bootstrap4/)) {
10             ret=4;
11         }
12     });
13     if (ret > 3) {
14          Roo.Element.prototype.visibilityMode = Roo.Element.DISPLAY;
15     }
16     return ret;
17 })(); /*
18  * Based on:
19  * Ext JS Library 1.1.1
20  * Copyright(c) 2006-2007, Ext JS, LLC.
21  *
22  * Originally Released Under LGPL - original licence link has changed is not relivant.
23  *
24  * Fork - LGPL
25  * <script type="text/javascript">
26  */
27
28
29 /**
30  * @class Roo.Shadow
31  * Simple class that can provide a shadow effect for any element.  Note that the element MUST be absolutely positioned,
32  * and the shadow does not provide any shimming.  This should be used only in simple cases -- for more advanced
33  * functionality that can also provide the same shadow effect, see the {@link Roo.Layer} class.
34  * @constructor
35  * Create a new Shadow
36  * @param {Object} config The config object
37  */
38 Roo.Shadow = function(config){
39     Roo.apply(this, config);
40     if(typeof this.mode != "string"){
41         this.mode = this.defaultMode;
42     }
43     var o = this.offset, a = {h: 0};
44     var rad = Math.floor(this.offset/2);
45     switch(this.mode.toLowerCase()){ // all this hideous nonsense calculates the various offsets for shadows
46         case "drop":
47             a.w = 0;
48             a.l = a.t = o;
49             a.t -= 1;
50             if(Roo.isIE){
51                 a.l -= this.offset + rad;
52                 a.t -= this.offset + rad;
53                 a.w -= rad;
54                 a.h -= rad;
55                 a.t += 1;
56             }
57         break;
58         case "sides":
59             a.w = (o*2);
60             a.l = -o;
61             a.t = o-1;
62             if(Roo.isIE){
63                 a.l -= (this.offset - rad);
64                 a.t -= this.offset + rad;
65                 a.l += 1;
66                 a.w -= (this.offset - rad)*2;
67                 a.w -= rad + 1;
68                 a.h -= 1;
69             }
70         break;
71         case "frame":
72             a.w = a.h = (o*2);
73             a.l = a.t = -o;
74             a.t += 1;
75             a.h -= 2;
76             if(Roo.isIE){
77                 a.l -= (this.offset - rad);
78                 a.t -= (this.offset - rad);
79                 a.l += 1;
80                 a.w -= (this.offset + rad + 1);
81                 a.h -= (this.offset + rad);
82                 a.h += 1;
83             }
84         break;
85     };
86
87     this.adjusts = a;
88 };
89
90 Roo.Shadow.prototype = {
91     /**
92      * @cfg {String} mode
93      * The shadow display mode.  Supports the following options:<br />
94      * sides: Shadow displays on both sides and bottom only<br />
95      * frame: Shadow displays equally on all four sides<br />
96      * drop: Traditional bottom-right drop shadow (default)
97      */
98     /**
99      * @cfg {String} offset
100      * The number of pixels to offset the shadow from the element (defaults to 4)
101      */
102     offset: 4,
103
104     // private
105     defaultMode: "drop",
106
107     /**
108      * Displays the shadow under the target element
109      * @param {String/HTMLElement/Element} targetEl The id or element under which the shadow should display
110      */
111     show : function(target){
112         target = Roo.get(target);
113         if(!this.el){
114             this.el = Roo.Shadow.Pool.pull();
115             if(this.el.dom.nextSibling != target.dom){
116                 this.el.insertBefore(target);
117             }
118         }
119         this.el.setStyle("z-index", this.zIndex || parseInt(target.getStyle("z-index"), 10)-1);
120         if(Roo.isIE){
121             this.el.dom.style.filter="progid:DXImageTransform.Microsoft.alpha(opacity=50) progid:DXImageTransform.Microsoft.Blur(pixelradius="+(this.offset)+")";
122         }
123         this.realign(
124             target.getLeft(true),
125             target.getTop(true),
126             target.getWidth(),
127             target.getHeight()
128         );
129         this.el.dom.style.display = "block";
130     },
131
132     /**
133      * Returns true if the shadow is visible, else false
134      */
135     isVisible : function(){
136         return this.el ? true : false;  
137     },
138
139     /**
140      * Direct alignment when values are already available. Show must be called at least once before
141      * calling this method to ensure it is initialized.
142      * @param {Number} left The target element left position
143      * @param {Number} top The target element top position
144      * @param {Number} width The target element width
145      * @param {Number} height The target element height
146      */
147     realign : function(l, t, w, h){
148         if(!this.el){
149             return;
150         }
151         var a = this.adjusts, d = this.el.dom, s = d.style;
152         var iea = 0;
153         s.left = (l+a.l)+"px";
154         s.top = (t+a.t)+"px";
155         var sw = (w+a.w), sh = (h+a.h), sws = sw +"px", shs = sh + "px";
156  
157         if(s.width != sws || s.height != shs){
158             s.width = sws;
159             s.height = shs;
160             if(!Roo.isIE){
161                 var cn = d.childNodes;
162                 var sww = Math.max(0, (sw-12))+"px";
163                 cn[0].childNodes[1].style.width = sww;
164                 cn[1].childNodes[1].style.width = sww;
165                 cn[2].childNodes[1].style.width = sww;
166                 cn[1].style.height = Math.max(0, (sh-12))+"px";
167             }
168         }
169     },
170
171     /**
172      * Hides this shadow
173      */
174     hide : function(){
175         if(this.el){
176             this.el.dom.style.display = "none";
177             Roo.Shadow.Pool.push(this.el);
178             delete this.el;
179         }
180     },
181
182     /**
183      * Adjust the z-index of this shadow
184      * @param {Number} zindex The new z-index
185      */
186     setZIndex : function(z){
187         this.zIndex = z;
188         if(this.el){
189             this.el.setStyle("z-index", z);
190         }
191     }
192 };
193
194 // Private utility class that manages the internal Shadow cache
195 Roo.Shadow.Pool = function(){
196     var p = [];
197     var markup = Roo.isIE ?
198                  '<div class="x-ie-shadow"></div>' :
199                  '<div class="x-shadow"><div class="xst"><div class="xstl"></div><div class="xstc"></div><div class="xstr"></div></div><div class="xsc"><div class="xsml"></div><div class="xsmc"></div><div class="xsmr"></div></div><div class="xsb"><div class="xsbl"></div><div class="xsbc"></div><div class="xsbr"></div></div></div>';
200     return {
201         pull : function(){
202             var sh = p.shift();
203             if(!sh){
204                 sh = Roo.get(Roo.DomHelper.insertHtml("beforeBegin", document.body.firstChild, markup));
205                 sh.autoBoxAdjust = false;
206             }
207             return sh;
208         },
209
210         push : function(sh){
211             p.push(sh);
212         }
213     };
214 }();/*
215  * - LGPL
216  *
217  * base class for bootstrap elements.
218  * 
219  */
220
221 Roo.bootstrap = Roo.bootstrap || {};
222 /**
223  * @class Roo.bootstrap.Component
224  * @extends Roo.Component
225  * Bootstrap Component base class
226  * @cfg {String} cls css class
227  * @cfg {String} style any extra css
228  * @cfg {Object} xattr extra attributes to add to 'element' (used by builder to store stuff.)
229  * @cfg {Boolean} can_build_overlaid  True if element can be rebuild from a HTML page
230  * @cfg {string} dataId cutomer id
231  * @cfg {string} name Specifies name attribute
232  * @cfg {string} tooltip  Text for the tooltip
233  * @cfg {string} container_method method to fetch parents container element (used by NavHeaderbar -  getHeaderChildContainer)
234  * @cfg {string|object} visibilityEl (el|parent) What element to use for visibility (@see getVisibilityEl())
235  
236  * @constructor
237  * Do not use directly - it does not do anything..
238  * @param {Object} config The config object
239  */
240
241
242
243 Roo.bootstrap.Component = function(config){
244     Roo.bootstrap.Component.superclass.constructor.call(this, config);
245        
246     this.addEvents({
247         /**
248          * @event childrenrendered
249          * Fires when the children have been rendered..
250          * @param {Roo.bootstrap.Component} this
251          */
252         "childrenrendered" : true
253         
254         
255         
256     });
257     
258     
259 };
260
261 Roo.extend(Roo.bootstrap.Component, Roo.BoxComponent,  {
262     
263     
264     allowDomMove : false, // to stop relocations in parent onRender...
265     
266     cls : false,
267     
268     style : false,
269     
270     autoCreate : false,
271     
272     tooltip : null,
273     /**
274      * Initialize Events for the element
275      */
276     initEvents : function() { },
277     
278     xattr : false,
279     
280     parentId : false,
281     
282     can_build_overlaid : true,
283     
284     container_method : false,
285     
286     dataId : false,
287     
288     name : false,
289     
290     parent: function() {
291         // returns the parent component..
292         return Roo.ComponentMgr.get(this.parentId)
293         
294         
295     },
296     
297     // private
298     onRender : function(ct, position)
299     {
300        // Roo.log("Call onRender: " + this.xtype);
301         
302         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
303         
304         if(this.el){
305             if (this.el.attr('xtype')) {
306                 this.el.attr('xtypex', this.el.attr('xtype'));
307                 this.el.dom.removeAttribute('xtype');
308                 
309                 this.initEvents();
310             }
311             
312             return;
313         }
314         
315          
316         
317         var cfg = Roo.apply({},  this.getAutoCreate());
318         
319         cfg.id = this.id || Roo.id();
320         
321         // fill in the extra attributes 
322         if (this.xattr && typeof(this.xattr) =='object') {
323             for (var i in this.xattr) {
324                 cfg[i] = this.xattr[i];
325             }
326         }
327         
328         if(this.dataId){
329             cfg.dataId = this.dataId;
330         }
331         
332         if (this.cls) {
333             cfg.cls = (typeof(cfg.cls) == 'undefined') ? this.cls : cfg.cls + ' ' + this.cls;
334         }
335         
336         if (this.style) { // fixme needs to support more complex style data.
337             cfg.style = this.style;
338         }
339         
340         if(this.name){
341             cfg.name = this.name;
342         }
343         
344         this.el = ct.createChild(cfg, position);
345         
346         if (this.tooltip) {
347             this.tooltipEl().attr('tooltip', this.tooltip);
348         }
349         
350         if(this.tabIndex !== undefined){
351             this.el.dom.setAttribute('tabIndex', this.tabIndex);
352         }
353         
354         this.initEvents();
355         
356     },
357     /**
358      * Fetch the element to add children to
359      * @return {Roo.Element} defaults to this.el
360      */
361     getChildContainer : function()
362     {
363         return this.el;
364     },
365     /**
366      * Fetch the element to display the tooltip on.
367      * @return {Roo.Element} defaults to this.el
368      */
369     tooltipEl : function()
370     {
371         return this.el;
372     },
373         
374     addxtype  : function(tree,cntr)
375     {
376         var cn = this;
377         
378         cn = Roo.factory(tree);
379         //Roo.log(['addxtype', cn]);
380            
381         cn.parentType = this.xtype; //??
382         cn.parentId = this.id;
383         
384         cntr = (typeof(cntr) == 'undefined' ) ? 'getChildContainer' : cntr;
385         if (typeof(cn.container_method) == 'string') {
386             cntr = cn.container_method;
387         }
388         
389         
390         var has_flexy_each =  (typeof(tree['flexy:foreach']) != 'undefined');
391         
392         var has_flexy_if =  (typeof(tree['flexy:if']) != 'undefined');
393         
394         var build_from_html =  Roo.XComponent.build_from_html;
395           
396         var is_body  = (tree.xtype == 'Body') ;
397           
398         var page_has_body = (Roo.get(document.body).attr('xtype') == 'Roo.bootstrap.Body');
399           
400         var self_cntr_el = Roo.get(this[cntr](false));
401         
402         // do not try and build conditional elements 
403         if ((has_flexy_each || has_flexy_if || this.can_build_overlaid == false ) && build_from_html) {
404             return false;
405         }
406         
407         if (!has_flexy_each || !build_from_html || is_body || !page_has_body) {
408             if(!has_flexy_if || typeof(tree.name) == 'undefined' || !build_from_html || is_body || !page_has_body){
409                 return this.addxtypeChild(tree,cntr, is_body);
410             }
411             
412             var echild =self_cntr_el ? self_cntr_el.child('>*[name=' + tree.name + ']') : false;
413                 
414             if(echild){
415                 return this.addxtypeChild(Roo.apply({}, tree),cntr);
416             }
417             
418             Roo.log('skipping render');
419             return cn;
420             
421         }
422         
423         var ret = false;
424         if (!build_from_html) {
425             return false;
426         }
427         
428         // this i think handles overlaying multiple children of the same type
429         // with the sam eelement.. - which might be buggy..
430         while (true) {
431             var echild =self_cntr_el ? self_cntr_el.child('>*[xtype]') : false;
432             
433             if (!echild) {
434                 break;
435             }
436             
437             if (echild && echild.attr('xtype').split('.').pop() != cn.xtype) {
438                 break;
439             }
440             
441             ret = this.addxtypeChild(Roo.apply({}, tree),cntr);
442         }
443        
444         return ret;
445     },
446     
447     
448     addxtypeChild : function (tree, cntr, is_body)
449     {
450         Roo.debug && Roo.log('addxtypeChild:' + cntr);
451         var cn = this;
452         cntr = (typeof(cntr) == 'undefined' ) ? 'getChildContainer' : cntr;
453         
454         
455         var has_flexy = (typeof(tree['flexy:if']) != 'undefined') ||
456                     (typeof(tree['flexy:foreach']) != 'undefined');
457           
458     
459         
460         skip_children = false;
461         // render the element if it's not BODY.
462         if (!is_body) {
463             
464             // if parent was disabled, then do not try and create the children..
465             if(!this[cntr](true)){
466                 tree.items = [];
467                 return tree;
468             }
469            
470             cn = Roo.factory(tree);
471            
472             cn.parentType = this.xtype; //??
473             cn.parentId = this.id;
474             
475             var build_from_html =  Roo.XComponent.build_from_html;
476             
477             
478             // does the container contain child eleemnts with 'xtype' attributes.
479             // that match this xtype..
480             // note - when we render we create these as well..
481             // so we should check to see if body has xtype set.
482             if (build_from_html && Roo.get(document.body).attr('xtype') == 'Roo.bootstrap.Body') {
483                
484                 var self_cntr_el = Roo.get(this[cntr](false));
485                 var echild =self_cntr_el ? self_cntr_el.child('>*[xtype]') : false;
486                 if (echild) { 
487                     //Roo.log(Roo.XComponent.build_from_html);
488                     //Roo.log("got echild:");
489                     //Roo.log(echild);
490                 }
491                 // there is a scenario where some of the child elements are flexy:if (and all of the same type)
492                 // and are not displayed -this causes this to use up the wrong element when matching.
493                 // at present the only work around for this is to nest flexy:if elements in another element that is always rendered.
494                 
495                 
496                 if (echild && echild.attr('xtype').split('.').pop() == cn.xtype) {
497                   //  Roo.log("found child for " + this.xtype +": " + echild.attr('xtype') );
498                   
499                   
500                   
501                     cn.el = echild;
502                   //  Roo.log("GOT");
503                     //echild.dom.removeAttribute('xtype');
504                 } else {
505                     Roo.debug && Roo.log("MISSING " + cn.xtype + " on child of " + (this.el ? this.el.attr('xbuilderid') : 'no parent'));
506                     Roo.debug && Roo.log(self_cntr_el);
507                     Roo.debug && Roo.log(echild);
508                     Roo.debug && Roo.log(cn);
509                 }
510             }
511            
512             
513            
514             // if object has flexy:if - then it may or may not be rendered.
515             if (build_from_html && has_flexy && !cn.el &&  cn.can_build_overlaid) {
516                 // skip a flexy if element.
517                 Roo.debug && Roo.log('skipping render');
518                 Roo.debug && Roo.log(tree);
519                 if (!cn.el) {
520                     Roo.debug && Roo.log('skipping all children');
521                     skip_children = true;
522                 }
523                 
524              } else {
525                  
526                 // actually if flexy:foreach is found, we really want to create 
527                 // multiple copies here...
528                 //Roo.log('render');
529                 //Roo.log(this[cntr]());
530                 // some elements do not have render methods.. like the layouts...
531                 /*
532                 if(this[cntr](true) === false){
533                     cn.items = [];
534                     return cn;
535                 }
536                 */
537                 cn.render && cn.render(this[cntr](true));
538                 
539              }
540             // then add the element..
541         }
542          
543         // handle the kids..
544         
545         var nitems = [];
546         /*
547         if (typeof (tree.menu) != 'undefined') {
548             tree.menu.parentType = cn.xtype;
549             tree.menu.triggerEl = cn.el;
550             nitems.push(cn.addxtype(Roo.apply({}, tree.menu)));
551             
552         }
553         */
554         if (!tree.items || !tree.items.length) {
555             cn.items = nitems;
556             //Roo.log(["no children", this]);
557             
558             return cn;
559         }
560          
561         var items = tree.items;
562         delete tree.items;
563         
564         //Roo.log(items.length);
565             // add the items..
566         if (!skip_children) {    
567             for(var i =0;i < items.length;i++) {
568               //  Roo.log(['add child', items[i]]);
569                 nitems.push(cn.addxtype(Roo.apply({}, items[i])));
570             }
571         }
572         
573         cn.items = nitems;
574         
575         //Roo.log("fire childrenrendered");
576         
577         cn.fireEvent('childrenrendered', this);
578         
579         return cn;
580     },
581     
582     /**
583      * Set the element that will be used to show or hide
584      */
585     setVisibilityEl : function(el)
586     {
587         this.visibilityEl = el;
588     },
589     
590      /**
591      * Get the element that will be used to show or hide
592      */
593     getVisibilityEl : function()
594     {
595         if (typeof(this.visibilityEl) == 'object') {
596             return this.visibilityEl;
597         }
598         
599         if (typeof(this.visibilityEl) == 'string') {
600             return this.visibilityEl == 'parent' ? this.parent().getEl() : this.getEl();
601         }
602         
603         return this.getEl();
604     },
605     
606     /**
607      * Show a component - removes 'hidden' class
608      */
609     show : function()
610     {
611         if(!this.getVisibilityEl()){
612             return;
613         }
614          
615         this.getVisibilityEl().removeClass(['hidden','d-none']);
616         
617         this.fireEvent('show', this);
618         
619         
620     },
621     /**
622      * Hide a component - adds 'hidden' class
623      */
624     hide: function()
625     {
626         if(!this.getVisibilityEl()){
627             return;
628         }
629         
630         this.getVisibilityEl().addClass(['hidden','d-none']);
631         
632         this.fireEvent('hide', this);
633         
634     }
635 });
636
637  /*
638  * - LGPL
639  *
640  * element
641  * 
642  */
643
644 /**
645  * @class Roo.bootstrap.Element
646  * @extends Roo.bootstrap.Component
647  * Bootstrap Element class
648  * @cfg {String} html contents of the element
649  * @cfg {String} tag tag of the element
650  * @cfg {String} cls class of the element
651  * @cfg {Boolean} preventDefault (true|false) default false
652  * @cfg {Boolean} clickable (true|false) default false
653  * 
654  * @constructor
655  * Create a new Element
656  * @param {Object} config The config object
657  */
658
659 Roo.bootstrap.Element = function(config){
660     Roo.bootstrap.Element.superclass.constructor.call(this, config);
661     
662     this.addEvents({
663         // raw events
664         /**
665          * @event click
666          * When a element is chick
667          * @param {Roo.bootstrap.Element} this
668          * @param {Roo.EventObject} e
669          */
670         "click" : true
671     });
672 };
673
674 Roo.extend(Roo.bootstrap.Element, Roo.bootstrap.Component,  {
675     
676     tag: 'div',
677     cls: '',
678     html: '',
679     preventDefault: false, 
680     clickable: false,
681     
682     getAutoCreate : function(){
683         
684         var cfg = {
685             tag: this.tag,
686             // cls: this.cls, double assign in parent class Component.js :: onRender
687             html: this.html
688         };
689         
690         return cfg;
691     },
692     
693     initEvents: function() 
694     {
695         Roo.bootstrap.Element.superclass.initEvents.call(this);
696         
697         if(this.clickable){
698             this.el.on('click', this.onClick, this);
699         }
700         
701     },
702     
703     onClick : function(e)
704     {
705         if(this.preventDefault){
706             e.preventDefault();
707         }
708         
709         this.fireEvent('click', this, e);
710     },
711     
712     getValue : function()
713     {
714         return this.el.dom.innerHTML;
715     },
716     
717     setValue : function(value)
718     {
719         this.el.dom.innerHTML = value;
720     }
721    
722 });
723
724  
725
726  /*
727  * - LGPL
728  *
729  * dropable area
730  * 
731  */
732
733 /**
734  * @class Roo.bootstrap.DropTarget
735  * @extends Roo.bootstrap.Element
736  * Bootstrap DropTarget class
737  
738  * @cfg {string} name dropable name
739  * 
740  * @constructor
741  * Create a new Dropable Area
742  * @param {Object} config The config object
743  */
744
745 Roo.bootstrap.DropTarget = function(config){
746     Roo.bootstrap.DropTarget.superclass.constructor.call(this, config);
747     
748     this.addEvents({
749         // raw events
750         /**
751          * @event click
752          * When a element is chick
753          * @param {Roo.bootstrap.Element} this
754          * @param {Roo.EventObject} e
755          */
756         "drop" : true
757     });
758 };
759
760 Roo.extend(Roo.bootstrap.DropTarget, Roo.bootstrap.Element,  {
761     
762     
763     getAutoCreate : function(){
764         
765          
766     },
767     
768     initEvents: function() 
769     {
770         Roo.bootstrap.DropTarget.superclass.initEvents.call(this);
771         this.dropZone = new Roo.dd.DropTarget(this.getEl(), {
772             ddGroup: this.name,
773             listeners : {
774                 drop : this.dragDrop.createDelegate(this),
775                 enter : this.dragEnter.createDelegate(this),
776                 out : this.dragOut.createDelegate(this),
777                 over : this.dragOver.createDelegate(this)
778             }
779             
780         });
781         this.dropZone.DDM.useCache = false // so data gets refreshed when we resize stuff
782     },
783     
784     dragDrop : function(source,e,data)
785     {
786         // user has to decide how to impliment this.
787         Roo.log('drop');
788         Roo.log(this);
789         //this.fireEvent('drop', this, source, e ,data);
790         return false;
791     },
792     
793     dragEnter : function(n, dd, e, data)
794     {
795         // probably want to resize the element to match the dropped element..
796         Roo.log("enter");
797         this.originalSize = this.el.getSize();
798         this.el.setSize( n.el.getSize());
799         this.dropZone.DDM.refreshCache(this.name);
800         Roo.log([n, dd, e, data]);
801     },
802     
803     dragOut : function(value)
804     {
805         // resize back to normal
806         Roo.log("out");
807         this.el.setSize(this.originalSize);
808         this.dropZone.resetConstraints();
809     },
810     
811     dragOver : function()
812     {
813         // ??? do nothing?
814     }
815    
816 });
817
818  
819
820  /*
821  * - LGPL
822  *
823  * Body
824  *
825  */
826
827 /**
828  * @class Roo.bootstrap.Body
829  * @extends Roo.bootstrap.Component
830  * Bootstrap Body class
831  *
832  * @constructor
833  * Create a new body
834  * @param {Object} config The config object
835  */
836
837 Roo.bootstrap.Body = function(config){
838
839     config = config || {};
840
841     Roo.bootstrap.Body.superclass.constructor.call(this, config);
842     this.el = Roo.get(config.el ? config.el : document.body );
843     if (this.cls && this.cls.length) {
844         Roo.get(document.body).addClass(this.cls);
845     }
846 };
847
848 Roo.extend(Roo.bootstrap.Body, Roo.bootstrap.Component,  {
849
850     is_body : true,// just to make sure it's constructed?
851
852         autoCreate : {
853         cls: 'container'
854     },
855     onRender : function(ct, position)
856     {
857        /* Roo.log("Roo.bootstrap.Body - onRender");
858         if (this.cls && this.cls.length) {
859             Roo.get(document.body).addClass(this.cls);
860         }
861         // style??? xttr???
862         */
863     }
864
865
866
867
868 });
869 /*
870  * - LGPL
871  *
872  * button group
873  * 
874  */
875
876
877 /**
878  * @class Roo.bootstrap.ButtonGroup
879  * @extends Roo.bootstrap.Component
880  * Bootstrap ButtonGroup class
881  * @cfg {String} size lg | sm | xs (default empty normal)
882  * @cfg {String} align vertical | justified  (default none)
883  * @cfg {String} direction up | down (default down)
884  * @cfg {Boolean} toolbar false | true
885  * @cfg {Boolean} btn true | false
886  * 
887  * 
888  * @constructor
889  * Create a new Input
890  * @param {Object} config The config object
891  */
892
893 Roo.bootstrap.ButtonGroup = function(config){
894     Roo.bootstrap.ButtonGroup.superclass.constructor.call(this, config);
895 };
896
897 Roo.extend(Roo.bootstrap.ButtonGroup, Roo.bootstrap.Component,  {
898     
899     size: '',
900     align: '',
901     direction: '',
902     toolbar: false,
903     btn: true,
904
905     getAutoCreate : function(){
906         var cfg = {
907             cls: 'btn-group',
908             html : null
909         };
910         
911         cfg.html = this.html || cfg.html;
912         
913         if (this.toolbar) {
914             cfg = {
915                 cls: 'btn-toolbar',
916                 html: null
917             };
918             
919             return cfg;
920         }
921         
922         if (['vertical','justified'].indexOf(this.align)!==-1) {
923             cfg.cls = 'btn-group-' + this.align;
924             
925             if (this.align == 'justified') {
926                 console.log(this.items);
927             }
928         }
929         
930         if (['lg','sm','xs'].indexOf(this.size)!==-1) {
931             cfg.cls += ' btn-group-' + this.size;
932         }
933         
934         if (this.direction == 'up') {
935             cfg.cls += ' dropup' ;
936         }
937         
938         return cfg;
939     },
940     /**
941      * Add a button to the group (similar to NavItem API.)
942      */
943     addItem : function(cfg)
944     {
945         var cn = new Roo.bootstrap.Button(cfg);
946         //this.register(cn);
947         cn.parentId = this.id;
948         cn.onRender(this.el, null);
949         return cn;
950     }
951    
952 });
953
954  /*
955  * - LGPL
956  *
957  * button
958  * 
959  */
960
961 /**
962  * @class Roo.bootstrap.Button
963  * @extends Roo.bootstrap.Component
964  * Bootstrap Button class
965  * @cfg {String} html The button content
966  * @cfg {String} weight (default|primary|secondary|success|info|warning|danger|link|light|dark) default
967  * @cfg {String} badge_weight (default|primary|secondary|success|info|warning|danger|link|light|dark) default (same as button)
968  * @cfg {Boolean} outline default false (except for weight=default which emulates old behaveiour with an outline)
969  * @cfg {String} size (lg|sm|xs)
970  * @cfg {String} tag (a|input|submit)
971  * @cfg {String} href empty or href
972  * @cfg {Boolean} disabled default false;
973  * @cfg {Boolean} isClose default false;
974  * @cfg {String} glyphicon depricated - use fa
975  * @cfg {String} fa fontawesome icon - eg. 'comment' - without the fa/fas etc..
976  * @cfg {String} badge text for badge
977  * @cfg {String} theme (default|glow)  
978  * @cfg {Boolean} inverse dark themed version
979  * @cfg {Boolean} toggle is it a slidy toggle button
980  * @cfg {Boolean} pressed   default null - if the button ahs active state
981  * @cfg {String} ontext text for on slidy toggle state
982  * @cfg {String} offtext text for off slidy toggle state
983  * @cfg {Boolean} preventDefault  default true (stop click event triggering the URL if it's a link.)
984  * @cfg {Boolean} removeClass remove the standard class..
985  * @cfg {String} target (_self|_blank|_parent|_top|other) target for a href. 
986  * @cfg {Boolean} grpup if parent is a btn group - then it turns it into a toogleGroup.
987  * 
988  * @constructor
989  * Create a new button
990  * @param {Object} config The config object
991  */
992
993
994 Roo.bootstrap.Button = function(config){
995     Roo.bootstrap.Button.superclass.constructor.call(this, config);
996     
997     this.addEvents({
998         // raw events
999         /**
1000          * @event click
1001          * When a button is pressed
1002          * @param {Roo.bootstrap.Button} btn
1003          * @param {Roo.EventObject} e
1004          */
1005         "click" : true,
1006         /**
1007          * @event dblclick
1008          * When a button is double clicked
1009          * @param {Roo.bootstrap.Button} btn
1010          * @param {Roo.EventObject} e
1011          */
1012         "dblclick" : true,
1013          /**
1014          * @event toggle
1015          * After the button has been toggles
1016          * @param {Roo.bootstrap.Button} btn
1017          * @param {Roo.EventObject} e
1018          * @param {boolean} pressed (also available as button.pressed)
1019          */
1020         "toggle" : true
1021     });
1022 };
1023
1024 Roo.extend(Roo.bootstrap.Button, Roo.bootstrap.Component,  {
1025     html: false,
1026     active: false,
1027     weight: '',
1028     badge_weight: '',
1029     outline : false,
1030     size: '',
1031     tag: 'button',
1032     href: '',
1033     disabled: false,
1034     isClose: false,
1035     glyphicon: '',
1036     fa: '',
1037     badge: '',
1038     theme: 'default',
1039     inverse: false,
1040     
1041     toggle: false,
1042     ontext: 'ON',
1043     offtext: 'OFF',
1044     defaulton: true,
1045     preventDefault: true,
1046     removeClass: false,
1047     name: false,
1048     target: false,
1049     group : false,
1050      
1051     pressed : null,
1052      
1053     
1054     getAutoCreate : function(){
1055         
1056         var cfg = {
1057             tag : 'button',
1058             cls : 'roo-button',
1059             html: ''
1060         };
1061         
1062         if (['a', 'button', 'input', 'submit'].indexOf(this.tag) < 0) {
1063             throw "Invalid value for tag: " + this.tag + ". must be a, button, input or submit.";
1064             this.tag = 'button';
1065         } else {
1066             cfg.tag = this.tag;
1067         }
1068         cfg.html = '<span class="roo-button-text">' + (this.html || cfg.html) + '</span>';
1069         
1070         if (this.toggle == true) {
1071             cfg={
1072                 tag: 'div',
1073                 cls: 'slider-frame roo-button',
1074                 cn: [
1075                     {
1076                         tag: 'span',
1077                         'data-on-text':'ON',
1078                         'data-off-text':'OFF',
1079                         cls: 'slider-button',
1080                         html: this.offtext
1081                     }
1082                 ]
1083             };
1084             // why are we validating the weights?
1085             if (Roo.bootstrap.Button.weights.indexOf(this.weight) > -1) {
1086                 cfg.cls +=  ' ' + this.weight;
1087             }
1088             
1089             return cfg;
1090         }
1091         
1092         if (this.isClose) {
1093             cfg.cls += ' close';
1094             
1095             cfg["aria-hidden"] = true;
1096             
1097             cfg.html = "&times;";
1098             
1099             return cfg;
1100         }
1101              
1102         
1103         if (this.theme==='default') {
1104             cfg.cls = 'btn roo-button';
1105             
1106             //if (this.parentType != 'Navbar') {
1107             this.weight = this.weight.length ?  this.weight : 'default';
1108             //}
1109             if (Roo.bootstrap.Button.weights.indexOf(this.weight) > -1) {
1110                 
1111                 var outline = this.outline || this.weight == 'default' ? 'outline-' : '';
1112                 var weight = this.weight == 'default' ? 'secondary' : this.weight;
1113                 cfg.cls += ' btn-' + outline + weight;
1114                 if (this.weight == 'default') {
1115                     // BC
1116                     cfg.cls += ' btn-' + this.weight;
1117                 }
1118             }
1119         } else if (this.theme==='glow') {
1120             
1121             cfg.tag = 'a';
1122             cfg.cls = 'btn-glow roo-button';
1123             
1124             if (Roo.bootstrap.Button.weights.indexOf(this.weight) > -1) {
1125                 
1126                 cfg.cls += ' ' + this.weight;
1127             }
1128         }
1129    
1130         
1131         if (this.inverse) {
1132             this.cls += ' inverse';
1133         }
1134         
1135         
1136         if (this.active || this.pressed === true) {
1137             cfg.cls += ' active';
1138         }
1139         
1140         if (this.disabled) {
1141             cfg.disabled = 'disabled';
1142         }
1143         
1144         if (this.items) {
1145             Roo.log('changing to ul' );
1146             cfg.tag = 'ul';
1147             this.glyphicon = 'caret';
1148             if (Roo.bootstrap.version == 4) {
1149                 this.fa = 'caret-down';
1150             }
1151             
1152         }
1153         
1154         cfg.cls += this.size.length ? (' btn-' + this.size) : '';
1155          
1156         //gsRoo.log(this.parentType);
1157         if (this.parentType === 'Navbar' && !this.parent().bar) {
1158             Roo.log('changing to li?');
1159             
1160             cfg.tag = 'li';
1161             
1162             cfg.cls = '';
1163             cfg.cn =  [{
1164                 tag : 'a',
1165                 cls : 'roo-button',
1166                 html : this.html,
1167                 href : this.href || '#'
1168             }];
1169             if (this.menu) {
1170                 cfg.cn[0].html = this.html  + ' <span class="caret"></span>';
1171                 cfg.cls += ' dropdown';
1172             }   
1173             
1174             delete cfg.html;
1175             
1176         }
1177         
1178        cfg.cls += this.parentType === 'Navbar' ?  ' navbar-btn' : '';
1179         
1180         if (this.glyphicon) {
1181             cfg.html = ' ' + cfg.html;
1182             
1183             cfg.cn = [
1184                 {
1185                     tag: 'span',
1186                     cls: 'glyphicon glyphicon-' + this.glyphicon
1187                 }
1188             ];
1189         }
1190         if (this.fa) {
1191             cfg.html = ' ' + cfg.html;
1192             
1193             cfg.cn = [
1194                 {
1195                     tag: 'i',
1196                     cls: 'fa fas fa-' + this.fa
1197                 }
1198             ];
1199         }
1200         
1201         if (this.badge) {
1202             cfg.html += ' ';
1203             
1204             cfg.tag = 'a';
1205             
1206 //            cfg.cls='btn roo-button';
1207             
1208             cfg.href=this.href;
1209             
1210             var value = cfg.html;
1211             
1212             if(this.glyphicon){
1213                 value = {
1214                     tag: 'span',
1215                     cls: 'glyphicon glyphicon-' + this.glyphicon,
1216                     html: this.html
1217                 };
1218             }
1219             if(this.fa){
1220                 value = {
1221                     tag: 'i',
1222                     cls: 'fa fas fa-' + this.fa,
1223                     html: this.html
1224                 };
1225             }
1226             
1227             var bw = this.badge_weight.length ? this.badge_weight :
1228                 (this.weight.length ? this.weight : 'secondary');
1229             bw = bw == 'default' ? 'secondary' : bw;
1230             
1231             cfg.cn = [
1232                 value,
1233                 {
1234                     tag: 'span',
1235                     cls: 'badge badge-' + bw,
1236                     html: this.badge
1237                 }
1238             ];
1239             
1240             cfg.html='';
1241         }
1242         
1243         if (this.menu) {
1244             cfg.cls += ' dropdown';
1245             cfg.html = typeof(cfg.html) != 'undefined' ?
1246                     cfg.html + ' <span class="caret"></span>' : '<span class="caret"></span>';
1247         }
1248         
1249         if (cfg.tag !== 'a' && this.href !== '') {
1250             throw "Tag must be a to set href.";
1251         } else if (this.href.length > 0) {
1252             cfg.href = this.href;
1253         }
1254         
1255         if(this.removeClass){
1256             cfg.cls = '';
1257         }
1258         
1259         if(this.target){
1260             cfg.target = this.target;
1261         }
1262         
1263         return cfg;
1264     },
1265     initEvents: function() {
1266        // Roo.log('init events?');
1267 //        Roo.log(this.el.dom);
1268         // add the menu...
1269         
1270         if (typeof (this.menu) != 'undefined') {
1271             this.menu.parentType = this.xtype;
1272             this.menu.triggerEl = this.el;
1273             this.addxtype(Roo.apply({}, this.menu));
1274         }
1275
1276
1277         if (this.el.hasClass('roo-button')) {
1278              this.el.on('click', this.onClick, this);
1279              this.el.on('dblclick', this.onDblClick, this);
1280         } else {
1281              this.el.select('.roo-button').on('click', this.onClick, this);
1282              this.el.select('.roo-button').on('dblclick', this.onDblClick, this);
1283              
1284         }
1285         // why?
1286         if(this.removeClass){
1287             this.el.on('click', this.onClick, this);
1288         }
1289         
1290         if (this.group === true) {
1291              if (this.pressed === false || this.pressed === true) {
1292                 // nothing
1293             } else {
1294                 this.pressed = false;
1295                 this.setActive(this.pressed);
1296             }
1297             
1298         }
1299         
1300         this.el.enableDisplayMode();
1301         
1302     },
1303     onClick : function(e)
1304     {
1305         if (this.disabled) {
1306             return;
1307         }
1308         
1309         Roo.log('button on click ');
1310         if(this.preventDefault){
1311             e.preventDefault();
1312         }
1313         
1314         if (this.group) {
1315             if (this.pressed) {
1316                 // do nothing -
1317                 return;
1318             }
1319             this.setActive(true);
1320             var pi = this.parent().items;
1321             for (var i = 0;i < pi.length;i++) {
1322                 if (this == pi[i]) {
1323                     continue;
1324                 }
1325                 if (pi[i].el.hasClass('roo-button')) {
1326                     pi[i].setActive(false);
1327                 }
1328             }
1329             this.fireEvent('click', this, e);            
1330             return;
1331         }
1332         
1333         if (this.pressed === true || this.pressed === false) {
1334             this.toggleActive(e);
1335         }
1336         
1337         
1338         this.fireEvent('click', this, e);
1339     },
1340     onDblClick: function(e)
1341     {
1342         if (this.disabled) {
1343             return;
1344         }
1345         if(this.preventDefault){
1346             e.preventDefault();
1347         }
1348         this.fireEvent('dblclick', this, e);
1349     },
1350     /**
1351      * Enables this button
1352      */
1353     enable : function()
1354     {
1355         this.disabled = false;
1356         this.el.removeClass('disabled');
1357     },
1358     
1359     /**
1360      * Disable this button
1361      */
1362     disable : function()
1363     {
1364         this.disabled = true;
1365         this.el.addClass('disabled');
1366     },
1367      /**
1368      * sets the active state on/off, 
1369      * @param {Boolean} state (optional) Force a particular state
1370      */
1371     setActive : function(v) {
1372         
1373         this.el[v ? 'addClass' : 'removeClass']('active');
1374         this.pressed = v;
1375     },
1376      /**
1377      * toggles the current active state 
1378      */
1379     toggleActive : function(e)
1380     {
1381         this.setActive(!this.pressed); // this modifies pressed...
1382         this.fireEvent('toggle', this, e, this.pressed);
1383     },
1384      /**
1385      * get the current active state
1386      * @return {boolean} true if it's active
1387      */
1388     isActive : function()
1389     {
1390         return this.el.hasClass('active');
1391     },
1392     /**
1393      * set the text of the first selected button
1394      */
1395     setText : function(str)
1396     {
1397         this.el.select('.roo-button-text',true).first().dom.innerHTML = str;
1398     },
1399     /**
1400      * get the text of the first selected button
1401      */
1402     getText : function()
1403     {
1404         return this.el.select('.roo-button-text',true).first().dom.innerHTML;
1405     },
1406     
1407     setWeight : function(str)
1408     {
1409         this.el.removeClass(Roo.bootstrap.Button.weights.map(function(w) { return 'btn-' + w; } ) );
1410         this.el.removeClass(Roo.bootstrap.Button.weights.map(function(w) { return 'btn-outline-' + w; } ) );
1411         this.weight = str;
1412         var outline = this.outline ? 'outline-' : '';
1413         if (str == 'default') {
1414             this.el.addClass('btn-default btn-outline-secondary');        
1415             return;
1416         }
1417         this.el.addClass('btn-' + outline + str);        
1418     }
1419     
1420     
1421 });
1422 // fixme - this is probably generic bootstrap - should go in some kind of enum file.. - like sizes.
1423
1424 Roo.bootstrap.Button.weights = [
1425     'default',
1426     'secondary' ,
1427     'primary',
1428     'success',
1429     'info',
1430     'warning',
1431     'danger',
1432     'link',
1433     'light',
1434     'dark'              
1435    
1436 ];/*
1437  * - LGPL
1438  *
1439  * column
1440  * 
1441  */
1442
1443 /**
1444  * @class Roo.bootstrap.Column
1445  * @extends Roo.bootstrap.Component
1446  * Bootstrap Column class
1447  * @cfg {Number} xs colspan out of 12 for mobile-sized screens or 0 for hidden
1448  * @cfg {Number} sm colspan out of 12 for tablet-sized screens or 0 for hidden
1449  * @cfg {Number} md colspan out of 12 for computer-sized screens or 0 for hidden
1450  * @cfg {Number} lg colspan out of 12 for large computer-sized screens or 0 for hidden
1451  * @cfg {Number} xsoff colspan offset out of 12 for mobile-sized screens or 0 for hidden
1452  * @cfg {Number} smoff colspan offset out of 12 for tablet-sized screens or 0 for hidden
1453  * @cfg {Number} mdoff colspan offset out of 12 for computer-sized screens or 0 for hidden
1454  * @cfg {Number} lgoff colspan offset out of 12 for large computer-sized screens or 0 for hidden
1455  *
1456  * 
1457  * @cfg {Boolean} hidden (true|false) hide the element
1458  * @cfg {String} alert (success|info|warning|danger) type alert (changes background / border...)
1459  * @cfg {String} fa (ban|check|...) font awesome icon
1460  * @cfg {Number} fasize (1|2|....) font awsome size
1461
1462  * @cfg {String} icon (info-sign|check|...) glyphicon name
1463
1464  * @cfg {String} html content of column.
1465  * 
1466  * @constructor
1467  * Create a new Column
1468  * @param {Object} config The config object
1469  */
1470
1471 Roo.bootstrap.Column = function(config){
1472     Roo.bootstrap.Column.superclass.constructor.call(this, config);
1473 };
1474
1475 Roo.extend(Roo.bootstrap.Column, Roo.bootstrap.Component,  {
1476     
1477     xs: false,
1478     sm: false,
1479     md: false,
1480     lg: false,
1481     xsoff: false,
1482     smoff: false,
1483     mdoff: false,
1484     lgoff: false,
1485     html: '',
1486     offset: 0,
1487     alert: false,
1488     fa: false,
1489     icon : false,
1490     hidden : false,
1491     fasize : 1,
1492     
1493     getAutoCreate : function(){
1494         var cfg = Roo.apply({}, Roo.bootstrap.Column.superclass.getAutoCreate.call(this));
1495         
1496         cfg = {
1497             tag: 'div',
1498             cls: 'column'
1499         };
1500         
1501         var settings=this;
1502         var sizes =   ['xs','sm','md','lg'];
1503         sizes.map(function(size ,ix){
1504             //Roo.log( size + ':' + settings[size]);
1505             
1506             if (settings[size+'off'] !== false) {
1507                 cfg.cls += ' col-' + size + '-offset-' + settings[size+'off'] ;
1508             }
1509             
1510             if (settings[size] === false) {
1511                 return;
1512             }
1513             
1514             if (!settings[size]) { // 0 = hidden
1515                 cfg.cls += ' hidden-' + size + ' hidden-' + size + '-down';
1516                 // bootsrap4
1517                 for (var i = ix; i > -1; i--) {
1518                     cfg.cls +=  ' d-' + sizes[i] + '-none'; 
1519                 }
1520                 
1521                 
1522                 return;
1523             }
1524             cfg.cls += ' col-' + size + '-' + settings[size] + (
1525                 size == 'xs' ? (' col-' + settings[size] ) : '' // bs4 col-{num} replaces col-xs
1526             );
1527             
1528         });
1529         
1530         if (this.hidden) {
1531             cfg.cls += ' hidden';
1532         }
1533         
1534         if (this.alert && ["success","info","warning", "danger"].indexOf(this.alert) > -1) {
1535             cfg.cls +=' alert alert-' + this.alert;
1536         }
1537         
1538         
1539         if (this.html.length) {
1540             cfg.html = this.html;
1541         }
1542         if (this.fa) {
1543             var fasize = '';
1544             if (this.fasize > 1) {
1545                 fasize = ' fa-' + this.fasize + 'x';
1546             }
1547             cfg.html = '<i class="fa fa-'+this.fa + fasize + '"></i>' + (cfg.html || '');
1548             
1549             
1550         }
1551         if (this.icon) {
1552             cfg.html = '<i class="glyphicon glyphicon-'+this.icon + '"></i>' +  (cfg.html || '');
1553         }
1554         
1555         return cfg;
1556     }
1557    
1558 });
1559
1560  
1561
1562  /*
1563  * - LGPL
1564  *
1565  * page container.
1566  * 
1567  */
1568
1569
1570 /**
1571  * @class Roo.bootstrap.Container
1572  * @extends Roo.bootstrap.Component
1573  * Bootstrap Container class
1574  * @cfg {Boolean} jumbotron is it a jumbotron element
1575  * @cfg {String} html content of element
1576  * @cfg {String} well (lg|sm|md) a well, large, small or medium.
1577  * @cfg {String} panel (default|primary|success|info|warning|danger) render as panel  - type - primary/success.....
1578  * @cfg {String} header content of header (for panel)
1579  * @cfg {String} footer content of footer (for panel)
1580  * @cfg {String} sticky (footer|wrap|push) block to use as footer or body- needs css-bootstrap/sticky-footer.css
1581  * @cfg {String} tag (header|aside|section) type of HTML tag.
1582  * @cfg {String} alert (success|info|warning|danger) type alert (changes background / border...)
1583  * @cfg {String} fa font awesome icon
1584  * @cfg {String} icon (info-sign|check|...) glyphicon name
1585  * @cfg {Boolean} hidden (true|false) hide the element
1586  * @cfg {Boolean} expandable (true|false) default false
1587  * @cfg {Boolean} expanded (true|false) default true
1588  * @cfg {String} rheader contet on the right of header
1589  * @cfg {Boolean} clickable (true|false) default false
1590
1591  *     
1592  * @constructor
1593  * Create a new Container
1594  * @param {Object} config The config object
1595  */
1596
1597 Roo.bootstrap.Container = function(config){
1598     Roo.bootstrap.Container.superclass.constructor.call(this, config);
1599     
1600     this.addEvents({
1601         // raw events
1602          /**
1603          * @event expand
1604          * After the panel has been expand
1605          * 
1606          * @param {Roo.bootstrap.Container} this
1607          */
1608         "expand" : true,
1609         /**
1610          * @event collapse
1611          * After the panel has been collapsed
1612          * 
1613          * @param {Roo.bootstrap.Container} this
1614          */
1615         "collapse" : true,
1616         /**
1617          * @event click
1618          * When a element is chick
1619          * @param {Roo.bootstrap.Container} this
1620          * @param {Roo.EventObject} e
1621          */
1622         "click" : true
1623     });
1624 };
1625
1626 Roo.extend(Roo.bootstrap.Container, Roo.bootstrap.Component,  {
1627     
1628     jumbotron : false,
1629     well: '',
1630     panel : '',
1631     header: '',
1632     footer : '',
1633     sticky: '',
1634     tag : false,
1635     alert : false,
1636     fa: false,
1637     icon : false,
1638     expandable : false,
1639     rheader : '',
1640     expanded : true,
1641     clickable: false,
1642   
1643      
1644     getChildContainer : function() {
1645         
1646         if(!this.el){
1647             return false;
1648         }
1649         
1650         if (this.panel.length) {
1651             return this.el.select('.panel-body',true).first();
1652         }
1653         
1654         return this.el;
1655     },
1656     
1657     
1658     getAutoCreate : function(){
1659         
1660         var cfg = {
1661             tag : this.tag || 'div',
1662             html : '',
1663             cls : ''
1664         };
1665         if (this.jumbotron) {
1666             cfg.cls = 'jumbotron';
1667         }
1668         
1669         
1670         
1671         // - this is applied by the parent..
1672         //if (this.cls) {
1673         //    cfg.cls = this.cls + '';
1674         //}
1675         
1676         if (this.sticky.length) {
1677             
1678             var bd = Roo.get(document.body);
1679             if (!bd.hasClass('bootstrap-sticky')) {
1680                 bd.addClass('bootstrap-sticky');
1681                 Roo.select('html',true).setStyle('height', '100%');
1682             }
1683              
1684             cfg.cls += 'bootstrap-sticky-' + this.sticky;
1685         }
1686         
1687         
1688         if (this.well.length) {
1689             switch (this.well) {
1690                 case 'lg':
1691                 case 'sm':
1692                     cfg.cls +=' well well-' +this.well;
1693                     break;
1694                 default:
1695                     cfg.cls +=' well';
1696                     break;
1697             }
1698         }
1699         
1700         if (this.hidden) {
1701             cfg.cls += ' hidden';
1702         }
1703         
1704         
1705         if (this.alert && ["success","info","warning", "danger"].indexOf(this.alert) > -1) {
1706             cfg.cls +=' alert alert-' + this.alert;
1707         }
1708         
1709         var body = cfg;
1710         
1711         if (this.panel.length) {
1712             cfg.cls += ' panel panel-' + this.panel;
1713             cfg.cn = [];
1714             if (this.header.length) {
1715                 
1716                 var h = [];
1717                 
1718                 if(this.expandable){
1719                     
1720                     cfg.cls = cfg.cls + ' expandable';
1721                     
1722                     h.push({
1723                         tag: 'i',
1724                         cls: (this.expanded ? 'fa fa-minus' : 'fa fa-plus') 
1725                     });
1726                     
1727                 }
1728                 
1729                 h.push(
1730                     {
1731                         tag: 'span',
1732                         cls : 'panel-title',
1733                         html : (this.expandable ? '&nbsp;' : '') + this.header
1734                     },
1735                     {
1736                         tag: 'span',
1737                         cls: 'panel-header-right',
1738                         html: this.rheader
1739                     }
1740                 );
1741                 
1742                 cfg.cn.push({
1743                     cls : 'panel-heading',
1744                     style : this.expandable ? 'cursor: pointer' : '',
1745                     cn : h
1746                 });
1747                 
1748             }
1749             
1750             body = false;
1751             cfg.cn.push({
1752                 cls : 'panel-body' + (this.expanded ? '' : ' hide'),
1753                 html : this.html
1754             });
1755             
1756             
1757             if (this.footer.length) {
1758                 cfg.cn.push({
1759                     cls : 'panel-footer',
1760                     html : this.footer
1761                     
1762                 });
1763             }
1764             
1765         }
1766         
1767         if (body) {
1768             body.html = this.html || cfg.html;
1769             // prefix with the icons..
1770             if (this.fa) {
1771                 body.html = '<i class="fa fa-'+this.fa + '"></i>' + body.html ;
1772             }
1773             if (this.icon) {
1774                 body.html = '<i class="glyphicon glyphicon-'+this.icon + '"></i>' + body.html ;
1775             }
1776             
1777             
1778         }
1779         if ((!this.cls || !this.cls.length) && (!cfg.cls || !cfg.cls.length)) {
1780             cfg.cls =  'container';
1781         }
1782         
1783         return cfg;
1784     },
1785     
1786     initEvents: function() 
1787     {
1788         if(this.expandable){
1789             var headerEl = this.headerEl();
1790         
1791             if(headerEl){
1792                 headerEl.on('click', this.onToggleClick, this);
1793             }
1794         }
1795         
1796         if(this.clickable){
1797             this.el.on('click', this.onClick, this);
1798         }
1799         
1800     },
1801     
1802     onToggleClick : function()
1803     {
1804         var headerEl = this.headerEl();
1805         
1806         if(!headerEl){
1807             return;
1808         }
1809         
1810         if(this.expanded){
1811             this.collapse();
1812             return;
1813         }
1814         
1815         this.expand();
1816     },
1817     
1818     expand : function()
1819     {
1820         if(this.fireEvent('expand', this)) {
1821             
1822             this.expanded = true;
1823             
1824             //this.el.select('.panel-body',true).first().setVisibilityMode(Roo.Element.DISPLAY).show();
1825             
1826             this.el.select('.panel-body',true).first().removeClass('hide');
1827             
1828             var toggleEl = this.toggleEl();
1829
1830             if(!toggleEl){
1831                 return;
1832             }
1833
1834             toggleEl.removeClass(['fa-minus', 'fa-plus']).addClass(['fa-minus']);
1835         }
1836         
1837     },
1838     
1839     collapse : function()
1840     {
1841         if(this.fireEvent('collapse', this)) {
1842             
1843             this.expanded = false;
1844             
1845             //this.el.select('.panel-body',true).first().setVisibilityMode(Roo.Element.DISPLAY).hide();
1846             this.el.select('.panel-body',true).first().addClass('hide');
1847         
1848             var toggleEl = this.toggleEl();
1849
1850             if(!toggleEl){
1851                 return;
1852             }
1853
1854             toggleEl.removeClass(['fa-minus', 'fa-plus']).addClass(['fa-plus']);
1855         }
1856     },
1857     
1858     toggleEl : function()
1859     {
1860         if(!this.el || !this.panel.length || !this.header.length || !this.expandable){
1861             return;
1862         }
1863         
1864         return this.el.select('.panel-heading .fa',true).first();
1865     },
1866     
1867     headerEl : function()
1868     {
1869         if(!this.el || !this.panel.length || !this.header.length){
1870             return;
1871         }
1872         
1873         return this.el.select('.panel-heading',true).first()
1874     },
1875     
1876     bodyEl : function()
1877     {
1878         if(!this.el || !this.panel.length){
1879             return;
1880         }
1881         
1882         return this.el.select('.panel-body',true).first()
1883     },
1884     
1885     titleEl : function()
1886     {
1887         if(!this.el || !this.panel.length || !this.header.length){
1888             return;
1889         }
1890         
1891         return this.el.select('.panel-title',true).first();
1892     },
1893     
1894     setTitle : function(v)
1895     {
1896         var titleEl = this.titleEl();
1897         
1898         if(!titleEl){
1899             return;
1900         }
1901         
1902         titleEl.dom.innerHTML = v;
1903     },
1904     
1905     getTitle : function()
1906     {
1907         
1908         var titleEl = this.titleEl();
1909         
1910         if(!titleEl){
1911             return '';
1912         }
1913         
1914         return titleEl.dom.innerHTML;
1915     },
1916     
1917     setRightTitle : function(v)
1918     {
1919         var t = this.el.select('.panel-header-right',true).first();
1920         
1921         if(!t){
1922             return;
1923         }
1924         
1925         t.dom.innerHTML = v;
1926     },
1927     
1928     onClick : function(e)
1929     {
1930         e.preventDefault();
1931         
1932         this.fireEvent('click', this, e);
1933     }
1934 });
1935
1936  /*
1937  *  - LGPL
1938  *
1939  *  This is BS4's Card element.. - similar to our containers probably..
1940  * 
1941  */
1942 /**
1943  * @class Roo.bootstrap.Card
1944  * @extends Roo.bootstrap.Component
1945  * Bootstrap Card class
1946  *
1947  *
1948  * possible... may not be implemented..
1949  * @cfg {String} header_image  src url of image.
1950  * @cfg {String|Object} header
1951  * @cfg {Number} header_size (0|1|2|3|4|5) H1 or H2 etc.. 0 indicates default
1952  * 
1953  * @cfg {String} title
1954  * @cfg {String} subtitle
1955  * @cfg {String|Boolean} html -- html contents - or just use children.. use false to hide it..
1956  * @cfg {String} footer
1957  
1958  * @cfg {String} weight (primary|warning|info|danger|secondary|success|light|dark)
1959  * 
1960  * @cfg {String} margin (0|1|2|3|4|5|auto)
1961  * @cfg {String} margin_top (0|1|2|3|4|5|auto)
1962  * @cfg {String} margin_bottom (0|1|2|3|4|5|auto)
1963  * @cfg {String} margin_left (0|1|2|3|4|5|auto)
1964  * @cfg {String} margin_right (0|1|2|3|4|5|auto)
1965  * @cfg {String} margin_x (0|1|2|3|4|5|auto)
1966  * @cfg {String} margin_y (0|1|2|3|4|5|auto)
1967  *
1968  * @cfg {String} padding (0|1|2|3|4|5)
1969  * @cfg {String} padding_top (0|1|2|3|4|5)next_to_card
1970  * @cfg {String} padding_bottom (0|1|2|3|4|5)
1971  * @cfg {String} padding_left (0|1|2|3|4|5)
1972  * @cfg {String} padding_right (0|1|2|3|4|5)
1973  * @cfg {String} padding_x (0|1|2|3|4|5)
1974  * @cfg {String} padding_y (0|1|2|3|4|5)
1975  *
1976  * @cfg {String} display (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
1977  * @cfg {String} display_xs (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
1978  * @cfg {String} display_sm (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
1979  * @cfg {String} display_lg (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
1980  * @cfg {String} display_xl (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
1981  
1982  * @config {Boolean} dragable  if this card can be dragged.
1983  * @config {String} drag_group  group for drag
1984  * @config {Boolean} dropable  if this card can recieve other cards being dropped onto it..
1985  * @config {String} drop_group  group for drag
1986  * 
1987  * @config {Boolean} collapsable can the body be collapsed.
1988  * @config {Boolean} collapsed is the body collapsed when rendered...
1989  * @config {Boolean} rotateable can the body be rotated by clicking on it..
1990  * @config {Boolean} rotated is the body rotated when rendered...
1991  * 
1992  * @constructor
1993  * Create a new Container
1994  * @param {Object} config The config object
1995  */
1996
1997 Roo.bootstrap.Card = function(config){
1998     Roo.bootstrap.Card.superclass.constructor.call(this, config);
1999     
2000     this.addEvents({
2001          // raw events
2002         /**
2003          * @event drop
2004          * When a element a card is dropped
2005          * @param {Roo.bootstrap.Card} this
2006          *
2007          * 
2008          * @param {Roo.bootstrap.Card} move_card the card being dropped?
2009          * @param {String} position 'above' or 'below'
2010          * @param {Roo.bootstrap.Card} next_to_card What card position is relative to of 'false' for empty list.
2011         
2012          */
2013         'drop' : true,
2014          /**
2015          * @event rotate
2016          * When a element a card is rotate
2017          * @param {Roo.bootstrap.Element} this
2018          * @param {Roo.Element} n the node being dropped?
2019          * @param {Boolean} rotate status
2020          */
2021         'rotate' : true
2022         
2023     });
2024 };
2025
2026
2027 Roo.extend(Roo.bootstrap.Card, Roo.bootstrap.Component,  {
2028     
2029     
2030     weight : '',
2031     
2032     margin: '', /// may be better in component?
2033     margin_top: '', 
2034     margin_bottom: '', 
2035     margin_left: '',
2036     margin_right: '',
2037     margin_x: '',
2038     margin_y: '',
2039     
2040     padding : '',
2041     padding_top: '', 
2042     padding_bottom: '', 
2043     padding_left: '',
2044     padding_right: '',
2045     padding_x: '',
2046     padding_y: '',
2047     
2048     display: '', 
2049     display_xs: '', 
2050     display_sm: '', 
2051     display_lg: '',
2052     display_xl: '',
2053  
2054     header_image  : '',
2055     header : '',
2056     header_size : 0,
2057     title : '',
2058     subtitle : '',
2059     html : '',
2060     footer: '',
2061
2062     collapsable : false,
2063     collapsed : false,
2064     rotateable : false,
2065     rotated : false,
2066     
2067     dragable : false,
2068     drag_group : false,
2069     dropable : false,
2070     drop_group : false,
2071     childContainer : false,
2072     dropEl : false, /// the dom placeholde element that indicates drop location.
2073     containerEl: false, // body container
2074     bodyEl: false, // card-body
2075     headerContainerEl : false, //
2076     headerEl : false,
2077     
2078     layoutCls : function()
2079     {
2080         var cls = '';
2081         var t = this;
2082         Roo.log(this.margin_bottom.length);
2083         ['', 'top', 'bottom', 'left', 'right', 'x', 'y' ].forEach(function(v) {
2084             // in theory these can do margin_top : ml-xs-3 ??? but we don't support that yet
2085             
2086             if (('' + t['margin' + (v.length ? '_' : '') + v]).length) {
2087                 cls += ' m' +  (v.length ? v[0]  : '') + '-' +  t['margin' + (v.length ? '_' : '') + v];
2088             }
2089             if (('' + t['padding' + (v.length ? '_' : '') + v]).length) {
2090                 cls += ' p' +  (v.length ? v[0]  : '') + '-' +  t['padding' + (v.length ? '_' : '') + v];
2091             }
2092         });
2093         
2094         ['', 'xs', 'sm', 'lg', 'xl'].forEach(function(v) {
2095             if (('' + t['display' + (v.length ? '_' : '') + v]).length) {
2096                 cls += ' d' +  (v.length ? '-' : '') + v + '-' + t['margin' + (v.length ? '_' : '') + v]
2097             }
2098         });
2099         
2100         // more generic support?
2101         if (this.hidden) {
2102             cls += ' d-none';
2103         }
2104         
2105         return cls;
2106     },
2107  
2108        // Roo.log("Call onRender: " + this.xtype);
2109         /*  We are looking at something like this.
2110 <div class="card">
2111     <img src="..." class="card-img-top" alt="...">
2112     <div class="card-body">
2113         <h5 class="card-title">Card title</h5>
2114          <h6 class="card-subtitle mb-2 text-muted">Card subtitle</h6>
2115
2116         >> this bit is really the body...
2117         <div> << we will ad dthis in hopefully it will not break shit.
2118         
2119         ** card text does not actually have any styling...
2120         
2121             <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>
2122         
2123         </div> <<
2124           <a href="#" class="card-link">Card link</a>
2125           
2126     </div>
2127     <div class="card-footer">
2128         <small class="text-muted">Last updated 3 mins ago</small>
2129     </div>
2130 </div>
2131          */
2132     getAutoCreate : function(){
2133         
2134         var cfg = {
2135             tag : 'div',
2136             cls : 'card',
2137             cn : [ ]
2138         };
2139         
2140         if (this.weight.length && this.weight != 'light') {
2141             cfg.cls += ' text-white';
2142         } else {
2143             cfg.cls += ' text-dark'; // need as it's nested..
2144         }
2145         if (this.weight.length) {
2146             cfg.cls += ' bg-' + this.weight;
2147         }
2148         
2149         cfg.cls += this.layoutCls(); 
2150         
2151         var hdr = false;
2152         var hdr_ctr = false;
2153         if (this.header.length) {
2154             hdr = {
2155                 tag : this.header_size > 0 ? 'h' + this.header_size : 'div',
2156                 cls : 'card-header',
2157                 cn : []
2158             };
2159             cfg.cn.push(hdr);
2160             hdr_ctr = hdr;
2161         } else {
2162             hdr = {
2163                 tag : 'div',
2164                 cls : 'card-header d-none',
2165                 cn : []
2166             };
2167             cfg.cn.push(hdr);
2168             hdr_ctr = hdr;
2169         }
2170         if (this.collapsable) {
2171             hdr_ctr = {
2172             tag : 'a',
2173             cls : 'd-block user-select-none',
2174             cn: [
2175                     {
2176                         tag: 'i',
2177                         cls : 'roo-collapse-toggle fa fa-chevron-down float-right ' + (this.collapsed ? 'collapsed' : '')
2178                     }
2179                    
2180                 ]
2181             };
2182             hdr.cn.push(hdr_ctr);
2183         }
2184         
2185         hdr_ctr.cn.push(        {
2186             tag: 'span',
2187             cls: 'roo-card-header-ctr' + ( this.header.length ? '' : ' d-none'),
2188             html : this.header
2189         });
2190         
2191         
2192         if (this.header_image.length) {
2193             cfg.cn.push({
2194                 tag : 'img',
2195                 cls : 'card-img-top',
2196                 src: this.header_image // escape?
2197             });
2198         } else {
2199             cfg.cn.push({
2200                     tag : 'div',
2201                     cls : 'card-img-top d-none' 
2202                 });
2203         }
2204             
2205         var body = {
2206             tag : 'div',
2207             cls : 'card-body' + (this.html === false  ? ' d-none' : ''),
2208             cn : []
2209         };
2210         var obody = body;
2211         if (this.collapsable || this.rotateable) {
2212             obody = {
2213                 tag: 'div',
2214                 cls : 'roo-collapsable collapse ' + (this.collapsed || this.rotated ? '' : 'show'),
2215                 cn : [  body ]
2216             };
2217         }
2218         
2219         cfg.cn.push(obody);
2220         
2221         if (this.title.length) {
2222             body.cn.push({
2223                 tag : 'div',
2224                 cls : 'card-title',
2225                 src: this.title // escape?
2226             });
2227         }  
2228         
2229         if (this.subtitle.length) {
2230             body.cn.push({
2231                 tag : 'div',
2232                 cls : 'card-title',
2233                 src: this.subtitle // escape?
2234             });
2235         }
2236         
2237         body.cn.push({
2238             tag : 'div',
2239             cls : 'roo-card-body-ctr'
2240         });
2241         
2242         if (this.html.length) {
2243             body.cn.push({
2244                 tag: 'div',
2245                 html : this.html
2246             });
2247         }
2248         // fixme ? handle objects?
2249         
2250         if (this.footer.length) {
2251            
2252             cfg.cn.push({
2253                 cls : 'card-footer ' + (this.rotated ? 'd-none' : ''),
2254                 html : this.footer
2255             });
2256             
2257         } else {
2258             cfg.cn.push({cls : 'card-footer d-none'});
2259         }
2260         
2261         // footer...
2262         
2263         return cfg;
2264     },
2265     
2266     
2267     getCardHeader : function()
2268     {
2269         var  ret = this.el.select('.card-header',true).first();
2270         if (ret.hasClass('d-none')) {
2271             ret.removeClass('d-none');
2272         }
2273         
2274         return ret;
2275     },
2276     getCardFooter : function()
2277     {
2278         var  ret = this.el.select('.card-footer',true).first();
2279         if (ret.hasClass('d-none')) {
2280             ret.removeClass('d-none');
2281         }
2282         
2283         return ret;
2284     },
2285     getCardImageTop : function()
2286     {
2287         var  ret = this.el.select('.card-img-top',true).first();
2288         if (ret.hasClass('d-none')) {
2289             ret.removeClass('d-none');
2290         }
2291             
2292         return ret;
2293     },
2294     
2295     getChildContainer : function()
2296     {
2297         
2298         if(!this.el){
2299             return false;
2300         }
2301         return this.el.select('.roo-card-body-ctr',true).first();    
2302     },
2303     
2304     initEvents: function() 
2305     {
2306         this.bodyEl = this.el.select('.card-body',true).first(); 
2307         this.containerEl = this.getChildContainer();
2308         if(this.dragable){
2309             this.dragZone = new Roo.dd.DragZone(this.getEl(), {
2310                     containerScroll: true,
2311                     ddGroup: this.drag_group || 'default_card_drag_group'
2312             });
2313             this.dragZone.getDragData = this.getDragData.createDelegate(this);
2314         }
2315         if (this.dropable) {
2316             this.dropZone = new Roo.dd.DropZone(this.el.select('.card-body',true).first() , {
2317                 containerScroll: true,
2318                 ddGroup: this.drop_group || 'default_card_drag_group'
2319             });
2320             this.dropZone.getTargetFromEvent = this.getTargetFromEvent.createDelegate(this);
2321             this.dropZone.onNodeEnter = this.onNodeEnter.createDelegate(this);
2322             this.dropZone.onNodeOver = this.onNodeOver.createDelegate(this);
2323             this.dropZone.onNodeOut = this.onNodeOut.createDelegate(this);
2324             this.dropZone.onNodeDrop = this.onNodeDrop.createDelegate(this);
2325         }
2326         
2327         if (this.collapsable) {
2328             this.el.select('.card-header',true).on('click', this.onToggleCollapse, this);
2329         }
2330         if (this.rotateable) {
2331             this.el.select('.card-header',true).on('click', this.onToggleRotate, this);
2332         }
2333         this.collapsableEl = this.el.select('.roo-collapsable').first();
2334          
2335         this.footerEl = this.el.select('.card-footer').first();
2336         this.collapsableToggleEl = this.el.select('.roo-collapse-toggle');
2337         this.headerContainerEl = this.el.select('.roo-card-header-ctr').first();
2338         this.headerEl = this.el.select('.card-header',true).first();
2339         
2340         if (this.rotated) {
2341             this.el.addClass('roo-card-rotated');
2342             this.fireEvent('rotate', this, true);
2343         }
2344         
2345     },
2346     getDragData : function(e)
2347     {
2348         var target = this.getEl();
2349         if (target) {
2350             //this.handleSelection(e);
2351             
2352             var dragData = {
2353                 source: this,
2354                 copy: false,
2355                 nodes: this.getEl(),
2356                 records: []
2357             };
2358             
2359             
2360             dragData.ddel = target.dom ;    // the div element
2361             Roo.log(target.getWidth( ));
2362             dragData.ddel.style.width = target.getWidth() + 'px';
2363             
2364             return dragData;
2365         }
2366         return false;
2367     },
2368     /**
2369     *    Part of the Roo.dd.DropZone interface. If no target node is found, the
2370     *    whole Element becomes the target, and this causes the drop gesture to append.
2371     */
2372     getTargetFromEvent : function(e, dragged_card_el)
2373     {
2374         var target = e.getTarget();
2375         while ((target !== null) && (target.parentNode != this.containerEl.dom)) {
2376             target = target.parentNode;
2377         }
2378         
2379         var ret = {
2380             position: '',
2381             cards : [],
2382             card_n : -1,
2383             items_n : -1,
2384             card : false 
2385         };
2386         
2387         //Roo.log([ 'target' , target ? target.id : '--nothing--']);
2388         // see if target is one of the 'cards'...
2389         
2390         
2391         //Roo.log(this.items.length);
2392         var pos = false;
2393         
2394         var last_card_n = 0;
2395         var cards_len  = 0;
2396         for (var i = 0;i< this.items.length;i++) {
2397             
2398             if (!this.items[i].el.hasClass('card')) {
2399                  continue;
2400             }
2401             pos = this.getDropPoint(e, this.items[i].el.dom);
2402             
2403             cards_len = ret.cards.length;
2404             //Roo.log(this.items[i].el.dom.id);
2405             ret.cards.push(this.items[i]);
2406             last_card_n  = i;
2407             if (ret.card_n < 0 && pos == 'above') {
2408                 ret.position = cards_len > 0 ? 'below' : pos;
2409                 ret.items_n = i > 0 ? i - 1 : 0;
2410                 ret.card_n  = cards_len  > 0 ? cards_len - 1 : 0;
2411                 ret.card = ret.cards[ret.card_n];
2412             }
2413         }
2414         if (!ret.cards.length) {
2415             ret.card = true;
2416             ret.position = 'below';
2417             ret.items_n;
2418             return ret;
2419         }
2420         // could not find a card.. stick it at the end..
2421         if (ret.card_n < 0) {
2422             ret.card_n = last_card_n;
2423             ret.card = ret.cards[last_card_n];
2424             ret.items_n = this.items.indexOf(ret.cards[last_card_n]);
2425             ret.position = 'below';
2426         }
2427         
2428         if (this.items[ret.items_n].el == dragged_card_el) {
2429             return false;
2430         }
2431         
2432         if (ret.position == 'below') {
2433             var card_after = ret.card_n+1 == ret.cards.length ? false : ret.cards[ret.card_n+1];
2434             
2435             if (card_after  && card_after.el == dragged_card_el) {
2436                 return false;
2437             }
2438             return ret;
2439         }
2440         
2441         // its's after ..
2442         var card_before = ret.card_n > 0 ? ret.cards[ret.card_n-1] : false;
2443         
2444         if (card_before  && card_before.el == dragged_card_el) {
2445             return false;
2446         }
2447         
2448         return ret;
2449     },
2450     
2451     onNodeEnter : function(n, dd, e, data){
2452         return false;
2453     },
2454     onNodeOver : function(n, dd, e, data)
2455     {
2456        
2457         var target_info = this.getTargetFromEvent(e,data.source.el);
2458         if (target_info === false) {
2459             this.dropPlaceHolder('hide');
2460             return false;
2461         }
2462         Roo.log(['getTargetFromEvent', target_info ]);
2463         
2464          
2465         this.dropPlaceHolder('show', target_info,data);
2466         
2467         return false; 
2468     },
2469     onNodeOut : function(n, dd, e, data){
2470         this.dropPlaceHolder('hide');
2471      
2472     },
2473     onNodeDrop : function(n, dd, e, data)
2474     {
2475         
2476         // call drop - return false if
2477         
2478         // this could actually fail - if the Network drops..
2479         // we will ignore this at present..- client should probably reload
2480         // the whole set of cards if stuff like that fails.
2481         
2482         
2483         var info = this.getTargetFromEvent(e,data.source.el);
2484         if (info === false) {
2485             return false;
2486         }
2487         this.dropPlaceHolder('hide');
2488   
2489          
2490     
2491     
2492     
2493         this.acceptCard(data.source, info.position, info.card, info.items_n);
2494         return true;
2495          
2496     },
2497     firstChildCard : function()
2498     {
2499         for (var i = 0;i< this.items.length;i++) {
2500             
2501             if (!this.items[i].el.hasClass('card')) {
2502                  continue;
2503             }
2504             return this.items[i];
2505         }
2506         return this.items.length ? this.items[this.items.length-1] : false; // don't try and put stuff after the cards...
2507     },
2508     /**
2509      * accept card
2510      *
2511      * -        card.acceptCard(move_card, info.position, info.card, info.items_n);
2512      */
2513     acceptCard : function(move_card,  position, next_to_card )
2514     {
2515         if (this.fireEvent("drop", this, move_card, position, next_to_card) === false) {
2516             return false;
2517         }
2518         
2519         var to_items_n = next_to_card ? this.items.indexOf(next_to_card) : 0;
2520         
2521         
2522         var dom = move_card.el.dom;
2523         dom.parentNode.removeChild(dom);
2524         dom.style.width = ''; // clear with - which is set by drag.
2525         
2526         if (next_to_card !== false && next_to_card !== true && next_to_card.el.dom.parentNode) {
2527             var cardel = next_to_card.el.dom;
2528             
2529             if (position == 'above' ) {
2530                 cardel.parentNode.insertBefore(dom, cardel);
2531             } else if (cardel.nextSibling) {
2532                 cardel.parentNode.insertBefore(dom,cardel.nextSibling);
2533             } else {
2534                 cardel.parentNode.append(dom);
2535             }
2536         } else {
2537             // card container???
2538             this.containerEl.dom.append(dom);
2539         }
2540         
2541         //FIXME HANDLE card = true 
2542         
2543         // add this to the correct place in items.
2544         
2545         
2546         
2547         // remove Card from items.
2548         
2549         var old_parent = move_card.parent();
2550         
2551         old_parent.items = old_parent.items.filter(function(e) { return e != move_card });
2552         
2553         if (this.items.length) {
2554             var nitems = [];
2555             //Roo.log([info.items_n, info.position, this.items.length]);
2556             for (var i =0; i < this.items.length; i++) {
2557                 if (i == to_items_n && position == 'above') {
2558                     nitems.push(move_card);
2559                 }
2560                 nitems.push(this.items[i]);
2561                 if (i == to_items_n && position == 'below') {
2562                     nitems.push(move_card);
2563                 }
2564             }
2565             this.items = nitems;
2566             Roo.log(this.items);
2567         } else {
2568             this.items.push(move_card);
2569         }
2570         
2571         move_card.parentId = this.id;
2572         
2573         return true;
2574         
2575         
2576     },
2577     
2578     
2579     /**    Decide whether to drop above or below a View node. */
2580     getDropPoint : function(e, n, dd)
2581     {
2582         if (dd) {
2583              return false;
2584         }
2585         if (n == this.containerEl.dom) {
2586             return "above";
2587         }
2588         var t = Roo.lib.Dom.getY(n), b = t + n.offsetHeight;
2589         var c = t + (b - t) / 2;
2590         var y = Roo.lib.Event.getPageY(e);
2591         if(y <= c) {
2592             return "above";
2593         }else{
2594             return "below";
2595         }
2596     },
2597     onToggleCollapse : function(e)
2598         {
2599         if (this.collapsed) {
2600             this.el.select('.roo-collapse-toggle').removeClass('collapsed');
2601             this.collapsableEl.addClass('show');
2602             this.collapsed = false;
2603             return;
2604         }
2605         this.el.select('.roo-collapse-toggle').addClass('collapsed');
2606         this.collapsableEl.removeClass('show');
2607         this.collapsed = true;
2608         
2609     
2610     },
2611     
2612     onToggleRotate : function(e)
2613     {
2614         this.collapsableEl.removeClass('show');
2615         this.footerEl.removeClass('d-none');
2616         this.el.removeClass('roo-card-rotated');
2617         this.el.removeClass('d-none');
2618         if (this.rotated) {
2619             
2620             this.collapsableEl.addClass('show');
2621             this.rotated = false;
2622             this.fireEvent('rotate', this, this.rotated);
2623             return;
2624         }
2625         this.el.addClass('roo-card-rotated');
2626         this.footerEl.addClass('d-none');
2627         this.el.select('.roo-collapsable').removeClass('show');
2628         
2629         this.rotated = true;
2630         this.fireEvent('rotate', this, this.rotated);
2631     
2632     },
2633     
2634     dropPlaceHolder: function (action, info, data)
2635     {
2636         if (this.dropEl === false) {
2637             this.dropEl = Roo.DomHelper.append(this.containerEl, {
2638             cls : 'd-none'
2639             },true);
2640         }
2641         this.dropEl.removeClass(['d-none', 'd-block']);        
2642         if (action == 'hide') {
2643             
2644             this.dropEl.addClass('d-none');
2645             return;
2646         }
2647         // FIXME - info.card == true!!!
2648         this.dropEl.dom.parentNode.removeChild(this.dropEl.dom);
2649         
2650         if (info.card !== true) {
2651             var cardel = info.card.el.dom;
2652             
2653             if (info.position == 'above') {
2654                 cardel.parentNode.insertBefore(this.dropEl.dom, cardel);
2655             } else if (cardel.nextSibling) {
2656                 cardel.parentNode.insertBefore(this.dropEl.dom,cardel.nextSibling);
2657             } else {
2658                 cardel.parentNode.append(this.dropEl.dom);
2659             }
2660         } else {
2661             // card container???
2662             this.containerEl.dom.append(this.dropEl.dom);
2663         }
2664         
2665         this.dropEl.addClass('d-block roo-card-dropzone');
2666         
2667         this.dropEl.setHeight( Roo.get(data.ddel).getHeight() );
2668         
2669         
2670     
2671     
2672     
2673     },
2674     setHeaderText: function(html)
2675     {
2676         this.headerContainerEl.dom.innerHTML = html;
2677     }
2678
2679     
2680 });
2681
2682 /*
2683  * - LGPL
2684  *
2685  * Card header - holder for the card header elements.
2686  * 
2687  */
2688
2689 /**
2690  * @class Roo.bootstrap.CardHeader
2691  * @extends Roo.bootstrap.Element
2692  * Bootstrap CardHeader class
2693  * @constructor
2694  * Create a new Card Header - that you can embed children into
2695  * @param {Object} config The config object
2696  */
2697
2698 Roo.bootstrap.CardHeader = function(config){
2699     Roo.bootstrap.CardHeader.superclass.constructor.call(this, config);
2700 };
2701
2702 Roo.extend(Roo.bootstrap.CardHeader, Roo.bootstrap.Element,  {
2703     
2704     
2705     container_method : 'getCardHeader' 
2706     
2707      
2708     
2709     
2710    
2711 });
2712
2713  
2714
2715  /*
2716  * - LGPL
2717  *
2718  * Card footer - holder for the card footer elements.
2719  * 
2720  */
2721
2722 /**
2723  * @class Roo.bootstrap.CardFooter
2724  * @extends Roo.bootstrap.Element
2725  * Bootstrap CardFooter class
2726  * @constructor
2727  * Create a new Card Footer - that you can embed children into
2728  * @param {Object} config The config object
2729  */
2730
2731 Roo.bootstrap.CardFooter = function(config){
2732     Roo.bootstrap.CardFooter.superclass.constructor.call(this, config);
2733 };
2734
2735 Roo.extend(Roo.bootstrap.CardFooter, Roo.bootstrap.Element,  {
2736     
2737     
2738     container_method : 'getCardFooter' 
2739     
2740      
2741     
2742     
2743    
2744 });
2745
2746  
2747
2748  /*
2749  * - LGPL
2750  *
2751  * Card header - holder for the card header elements.
2752  * 
2753  */
2754
2755 /**
2756  * @class Roo.bootstrap.CardImageTop
2757  * @extends Roo.bootstrap.Element
2758  * Bootstrap CardImageTop class
2759  * @constructor
2760  * Create a new Card Image Top container
2761  * @param {Object} config The config object
2762  */
2763
2764 Roo.bootstrap.CardImageTop = function(config){
2765     Roo.bootstrap.CardImageTop.superclass.constructor.call(this, config);
2766 };
2767
2768 Roo.extend(Roo.bootstrap.CardImageTop, Roo.bootstrap.Element,  {
2769     
2770    
2771     container_method : 'getCardImageTop' 
2772     
2773      
2774     
2775    
2776 });
2777
2778  
2779
2780  /*
2781  * - LGPL
2782  *
2783  * image
2784  * 
2785  */
2786
2787
2788 /**
2789  * @class Roo.bootstrap.Img
2790  * @extends Roo.bootstrap.Component
2791  * Bootstrap Img class
2792  * @cfg {Boolean} imgResponsive false | true
2793  * @cfg {String} border rounded | circle | thumbnail
2794  * @cfg {String} src image source
2795  * @cfg {String} alt image alternative text
2796  * @cfg {String} href a tag href
2797  * @cfg {String} target (_self|_blank|_parent|_top)target for a href.
2798  * @cfg {String} xsUrl xs image source
2799  * @cfg {String} smUrl sm image source
2800  * @cfg {String} mdUrl md image source
2801  * @cfg {String} lgUrl lg image source
2802  * 
2803  * @constructor
2804  * Create a new Input
2805  * @param {Object} config The config object
2806  */
2807
2808 Roo.bootstrap.Img = function(config){
2809     Roo.bootstrap.Img.superclass.constructor.call(this, config);
2810     
2811     this.addEvents({
2812         // img events
2813         /**
2814          * @event click
2815          * The img click event for the img.
2816          * @param {Roo.EventObject} e
2817          */
2818         "click" : true
2819     });
2820 };
2821
2822 Roo.extend(Roo.bootstrap.Img, Roo.bootstrap.Component,  {
2823     
2824     imgResponsive: true,
2825     border: '',
2826     src: 'about:blank',
2827     href: false,
2828     target: false,
2829     xsUrl: '',
2830     smUrl: '',
2831     mdUrl: '',
2832     lgUrl: '',
2833
2834     getAutoCreate : function()
2835     {   
2836         if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
2837             return this.createSingleImg();
2838         }
2839         
2840         var cfg = {
2841             tag: 'div',
2842             cls: 'roo-image-responsive-group',
2843             cn: []
2844         };
2845         var _this = this;
2846         
2847         Roo.each(['xs', 'sm', 'md', 'lg'], function(size){
2848             
2849             if(!_this[size + 'Url']){
2850                 return;
2851             }
2852             
2853             var img = {
2854                 tag: 'img',
2855                 cls: (_this.imgResponsive) ? 'img-responsive' : '',
2856                 html: _this.html || cfg.html,
2857                 src: _this[size + 'Url']
2858             };
2859             
2860             img.cls += ' roo-image-responsive-' + size;
2861             
2862             var s = ['xs', 'sm', 'md', 'lg'];
2863             
2864             s.splice(s.indexOf(size), 1);
2865             
2866             Roo.each(s, function(ss){
2867                 img.cls += ' hidden-' + ss;
2868             });
2869             
2870             if (['rounded','circle','thumbnail'].indexOf(_this.border)>-1) {
2871                 cfg.cls += ' img-' + _this.border;
2872             }
2873             
2874             if(_this.alt){
2875                 cfg.alt = _this.alt;
2876             }
2877             
2878             if(_this.href){
2879                 var a = {
2880                     tag: 'a',
2881                     href: _this.href,
2882                     cn: [
2883                         img
2884                     ]
2885                 };
2886
2887                 if(this.target){
2888                     a.target = _this.target;
2889                 }
2890             }
2891             
2892             cfg.cn.push((_this.href) ? a : img);
2893             
2894         });
2895         
2896         return cfg;
2897     },
2898     
2899     createSingleImg : function()
2900     {
2901         var cfg = {
2902             tag: 'img',
2903             cls: (this.imgResponsive) ? 'img-responsive' : '',
2904             html : null,
2905             src : 'about:blank'  // just incase src get's set to undefined?!?
2906         };
2907         
2908         cfg.html = this.html || cfg.html;
2909         
2910         cfg.src = this.src || cfg.src;
2911         
2912         if (['rounded','circle','thumbnail'].indexOf(this.border)>-1) {
2913             cfg.cls += ' img-' + this.border;
2914         }
2915         
2916         if(this.alt){
2917             cfg.alt = this.alt;
2918         }
2919         
2920         if(this.href){
2921             var a = {
2922                 tag: 'a',
2923                 href: this.href,
2924                 cn: [
2925                     cfg
2926                 ]
2927             };
2928             
2929             if(this.target){
2930                 a.target = this.target;
2931             }
2932             
2933         }
2934         
2935         return (this.href) ? a : cfg;
2936     },
2937     
2938     initEvents: function() 
2939     {
2940         if(!this.href){
2941             this.el.on('click', this.onClick, this);
2942         }
2943         
2944     },
2945     
2946     onClick : function(e)
2947     {
2948         Roo.log('img onclick');
2949         this.fireEvent('click', this, e);
2950     },
2951     /**
2952      * Sets the url of the image - used to update it
2953      * @param {String} url the url of the image
2954      */
2955     
2956     setSrc : function(url)
2957     {
2958         this.src =  url;
2959         
2960         if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
2961             this.el.dom.src =  url;
2962             return;
2963         }
2964         
2965         this.el.select('img', true).first().dom.src =  url;
2966     }
2967     
2968     
2969    
2970 });
2971
2972  /*
2973  * - LGPL
2974  *
2975  * image
2976  * 
2977  */
2978
2979
2980 /**
2981  * @class Roo.bootstrap.Link
2982  * @extends Roo.bootstrap.Component
2983  * Bootstrap Link Class
2984  * @cfg {String} alt image alternative text
2985  * @cfg {String} href a tag href
2986  * @cfg {String} target (_self|_blank|_parent|_top) target for a href.
2987  * @cfg {String} html the content of the link.
2988  * @cfg {String} anchor name for the anchor link
2989  * @cfg {String} fa - favicon
2990
2991  * @cfg {Boolean} preventDefault (true | false) default false
2992
2993  * 
2994  * @constructor
2995  * Create a new Input
2996  * @param {Object} config The config object
2997  */
2998
2999 Roo.bootstrap.Link = function(config){
3000     Roo.bootstrap.Link.superclass.constructor.call(this, config);
3001     
3002     this.addEvents({
3003         // img events
3004         /**
3005          * @event click
3006          * The img click event for the img.
3007          * @param {Roo.EventObject} e
3008          */
3009         "click" : true
3010     });
3011 };
3012
3013 Roo.extend(Roo.bootstrap.Link, Roo.bootstrap.Component,  {
3014     
3015     href: false,
3016     target: false,
3017     preventDefault: false,
3018     anchor : false,
3019     alt : false,
3020     fa: false,
3021
3022
3023     getAutoCreate : function()
3024     {
3025         var html = this.html || '';
3026         
3027         if (this.fa !== false) {
3028             html = '<i class="fa fa-' + this.fa + '"></i>';
3029         }
3030         var cfg = {
3031             tag: 'a'
3032         };
3033         // anchor's do not require html/href...
3034         if (this.anchor === false) {
3035             cfg.html = html;
3036             cfg.href = this.href || '#';
3037         } else {
3038             cfg.name = this.anchor;
3039             if (this.html !== false || this.fa !== false) {
3040                 cfg.html = html;
3041             }
3042             if (this.href !== false) {
3043                 cfg.href = this.href;
3044             }
3045         }
3046         
3047         if(this.alt !== false){
3048             cfg.alt = this.alt;
3049         }
3050         
3051         
3052         if(this.target !== false) {
3053             cfg.target = this.target;
3054         }
3055         
3056         return cfg;
3057     },
3058     
3059     initEvents: function() {
3060         
3061         if(!this.href || this.preventDefault){
3062             this.el.on('click', this.onClick, this);
3063         }
3064     },
3065     
3066     onClick : function(e)
3067     {
3068         if(this.preventDefault){
3069             e.preventDefault();
3070         }
3071         //Roo.log('img onclick');
3072         this.fireEvent('click', this, e);
3073     }
3074    
3075 });
3076
3077  /*
3078  * - LGPL
3079  *
3080  * header
3081  * 
3082  */
3083
3084 /**
3085  * @class Roo.bootstrap.Header
3086  * @extends Roo.bootstrap.Component
3087  * Bootstrap Header class
3088  * @cfg {String} html content of header
3089  * @cfg {Number} level (1|2|3|4|5|6) default 1
3090  * 
3091  * @constructor
3092  * Create a new Header
3093  * @param {Object} config The config object
3094  */
3095
3096
3097 Roo.bootstrap.Header  = function(config){
3098     Roo.bootstrap.Header.superclass.constructor.call(this, config);
3099 };
3100
3101 Roo.extend(Roo.bootstrap.Header, Roo.bootstrap.Component,  {
3102     
3103     //href : false,
3104     html : false,
3105     level : 1,
3106     
3107     
3108     
3109     getAutoCreate : function(){
3110         
3111         
3112         
3113         var cfg = {
3114             tag: 'h' + (1 *this.level),
3115             html: this.html || ''
3116         } ;
3117         
3118         return cfg;
3119     }
3120    
3121 });
3122
3123  
3124
3125  /*
3126  * Based on:
3127  * Ext JS Library 1.1.1
3128  * Copyright(c) 2006-2007, Ext JS, LLC.
3129  *
3130  * Originally Released Under LGPL - original licence link has changed is not relivant.
3131  *
3132  * Fork - LGPL
3133  * <script type="text/javascript">
3134  */
3135  
3136 /**
3137  * @class Roo.bootstrap.MenuMgr
3138  * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
3139  * @singleton
3140  */
3141 Roo.bootstrap.MenuMgr = function(){
3142    var menus, active, groups = {}, attached = false, lastShow = new Date();
3143
3144    // private - called when first menu is created
3145    function init(){
3146        menus = {};
3147        active = new Roo.util.MixedCollection();
3148        Roo.get(document).addKeyListener(27, function(){
3149            if(active.length > 0){
3150                hideAll();
3151            }
3152        });
3153    }
3154
3155    // private
3156    function hideAll(){
3157        if(active && active.length > 0){
3158            var c = active.clone();
3159            c.each(function(m){
3160                m.hide();
3161            });
3162        }
3163    }
3164
3165    // private
3166    function onHide(m){
3167        active.remove(m);
3168        if(active.length < 1){
3169            Roo.get(document).un("mouseup", onMouseDown);
3170             
3171            attached = false;
3172        }
3173    }
3174
3175    // private
3176    function onShow(m){
3177        var last = active.last();
3178        lastShow = new Date();
3179        active.add(m);
3180        if(!attached){
3181           Roo.get(document).on("mouseup", onMouseDown);
3182            
3183            attached = true;
3184        }
3185        if(m.parentMenu){
3186           //m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
3187           m.parentMenu.activeChild = m;
3188        }else if(last && last.isVisible()){
3189           //m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
3190        }
3191    }
3192
3193    // private
3194    function onBeforeHide(m){
3195        if(m.activeChild){
3196            m.activeChild.hide();
3197        }
3198        if(m.autoHideTimer){
3199            clearTimeout(m.autoHideTimer);
3200            delete m.autoHideTimer;
3201        }
3202    }
3203
3204    // private
3205    function onBeforeShow(m){
3206        var pm = m.parentMenu;
3207        if(!pm && !m.allowOtherMenus){
3208            hideAll();
3209        }else if(pm && pm.activeChild && active != m){
3210            pm.activeChild.hide();
3211        }
3212    }
3213
3214    // private this should really trigger on mouseup..
3215    function onMouseDown(e){
3216         Roo.log("on Mouse Up");
3217         
3218         if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".dropdown-menu") && !e.getTarget('.user-menu')){
3219             Roo.log("MenuManager hideAll");
3220             hideAll();
3221             e.stopEvent();
3222         }
3223         
3224         
3225    }
3226
3227    // private
3228    function onBeforeCheck(mi, state){
3229        if(state){
3230            var g = groups[mi.group];
3231            for(var i = 0, l = g.length; i < l; i++){
3232                if(g[i] != mi){
3233                    g[i].setChecked(false);
3234                }
3235            }
3236        }
3237    }
3238
3239    return {
3240
3241        /**
3242         * Hides all menus that are currently visible
3243         */
3244        hideAll : function(){
3245             hideAll();  
3246        },
3247
3248        // private
3249        register : function(menu){
3250            if(!menus){
3251                init();
3252            }
3253            menus[menu.id] = menu;
3254            menu.on("beforehide", onBeforeHide);
3255            menu.on("hide", onHide);
3256            menu.on("beforeshow", onBeforeShow);
3257            menu.on("show", onShow);
3258            var g = menu.group;
3259            if(g && menu.events["checkchange"]){
3260                if(!groups[g]){
3261                    groups[g] = [];
3262                }
3263                groups[g].push(menu);
3264                menu.on("checkchange", onCheck);
3265            }
3266        },
3267
3268         /**
3269          * Returns a {@link Roo.menu.Menu} object
3270          * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
3271          * be used to generate and return a new Menu instance.
3272          */
3273        get : function(menu){
3274            if(typeof menu == "string"){ // menu id
3275                return menus[menu];
3276            }else if(menu.events){  // menu instance
3277                return menu;
3278            }
3279            /*else if(typeof menu.length == 'number'){ // array of menu items?
3280                return new Roo.bootstrap.Menu({items:menu});
3281            }else{ // otherwise, must be a config
3282                return new Roo.bootstrap.Menu(menu);
3283            }
3284            */
3285            return false;
3286        },
3287
3288        // private
3289        unregister : function(menu){
3290            delete menus[menu.id];
3291            menu.un("beforehide", onBeforeHide);
3292            menu.un("hide", onHide);
3293            menu.un("beforeshow", onBeforeShow);
3294            menu.un("show", onShow);
3295            var g = menu.group;
3296            if(g && menu.events["checkchange"]){
3297                groups[g].remove(menu);
3298                menu.un("checkchange", onCheck);
3299            }
3300        },
3301
3302        // private
3303        registerCheckable : function(menuItem){
3304            var g = menuItem.group;
3305            if(g){
3306                if(!groups[g]){
3307                    groups[g] = [];
3308                }
3309                groups[g].push(menuItem);
3310                menuItem.on("beforecheckchange", onBeforeCheck);
3311            }
3312        },
3313
3314        // private
3315        unregisterCheckable : function(menuItem){
3316            var g = menuItem.group;
3317            if(g){
3318                groups[g].remove(menuItem);
3319                menuItem.un("beforecheckchange", onBeforeCheck);
3320            }
3321        }
3322    };
3323 }();/*
3324  * - LGPL
3325  *
3326  * menu
3327  * 
3328  */
3329
3330 /**
3331  * @class Roo.bootstrap.Menu
3332  * @extends Roo.bootstrap.Component
3333  * Bootstrap Menu class - container for MenuItems
3334  * @cfg {String} type (dropdown|treeview|submenu) type of menu
3335  * @cfg {bool} hidden  if the menu should be hidden when rendered.
3336  * @cfg {bool} stopEvent (true|false)  Stop event after trigger press (default true)
3337  * @cfg {bool} isLink (true|false)  the menu has link disable auto expand and collaspe (default false)
3338  * 
3339  * @constructor
3340  * Create a new Menu
3341  * @param {Object} config The config object
3342  */
3343
3344
3345 Roo.bootstrap.Menu = function(config){
3346     Roo.bootstrap.Menu.superclass.constructor.call(this, config);
3347     if (this.registerMenu && this.type != 'treeview')  {
3348         Roo.bootstrap.MenuMgr.register(this);
3349     }
3350     
3351     
3352     this.addEvents({
3353         /**
3354          * @event beforeshow
3355          * Fires before this menu is displayed (return false to block)
3356          * @param {Roo.menu.Menu} this
3357          */
3358         beforeshow : true,
3359         /**
3360          * @event beforehide
3361          * Fires before this menu is hidden (return false to block)
3362          * @param {Roo.menu.Menu} this
3363          */
3364         beforehide : true,
3365         /**
3366          * @event show
3367          * Fires after this menu is displayed
3368          * @param {Roo.menu.Menu} this
3369          */
3370         show : true,
3371         /**
3372          * @event hide
3373          * Fires after this menu is hidden
3374          * @param {Roo.menu.Menu} this
3375          */
3376         hide : true,
3377         /**
3378          * @event click
3379          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
3380          * @param {Roo.menu.Menu} this
3381          * @param {Roo.menu.Item} menuItem The menu item that was clicked
3382          * @param {Roo.EventObject} e
3383          */
3384         click : true,
3385         /**
3386          * @event mouseover
3387          * Fires when the mouse is hovering over this menu
3388          * @param {Roo.menu.Menu} this
3389          * @param {Roo.EventObject} e
3390          * @param {Roo.menu.Item} menuItem The menu item that was clicked
3391          */
3392         mouseover : true,
3393         /**
3394          * @event mouseout
3395          * Fires when the mouse exits this menu
3396          * @param {Roo.menu.Menu} this
3397          * @param {Roo.EventObject} e
3398          * @param {Roo.menu.Item} menuItem The menu item that was clicked
3399          */
3400         mouseout : true,
3401         /**
3402          * @event itemclick
3403          * Fires when a menu item contained in this menu is clicked
3404          * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
3405          * @param {Roo.EventObject} e
3406          */
3407         itemclick: true
3408     });
3409     this.menuitems = new Roo.util.MixedCollection(false, function(o) { return o.el.id; });
3410 };
3411
3412 Roo.extend(Roo.bootstrap.Menu, Roo.bootstrap.Component,  {
3413     
3414    /// html : false,
3415     //align : '',
3416     triggerEl : false,  // is this set by component builder? -- it should really be fetched from parent()???
3417     type: false,
3418     /**
3419      * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
3420      */
3421     registerMenu : true,
3422     
3423     menuItems :false, // stores the menu items..
3424     
3425     hidden:true,
3426         
3427     parentMenu : false,
3428     
3429     stopEvent : true,
3430     
3431     isLink : false,
3432     
3433     getChildContainer : function() {
3434         return this.el;  
3435     },
3436     
3437     getAutoCreate : function(){
3438          
3439         //if (['right'].indexOf(this.align)!==-1) {
3440         //    cfg.cn[1].cls += ' pull-right'
3441         //}
3442         
3443         
3444         var cfg = {
3445             tag : 'ul',
3446             cls : 'dropdown-menu' ,
3447             style : 'z-index:1000'
3448             
3449         };
3450         
3451         if (this.type === 'submenu') {
3452             cfg.cls = 'submenu active';
3453         }
3454         if (this.type === 'treeview') {
3455             cfg.cls = 'treeview-menu';
3456         }
3457         
3458         return cfg;
3459     },
3460     initEvents : function() {
3461         
3462        // Roo.log("ADD event");
3463        // Roo.log(this.triggerEl.dom);
3464         
3465         this.triggerEl.on('click', this.onTriggerClick, this);
3466         
3467         this.triggerEl.on(Roo.isTouch ? 'touchstart' : 'mouseup', this.onTriggerPress, this);
3468         
3469         
3470         if (this.triggerEl.hasClass('nav-item')) {
3471             // dropdown toggle on the 'a' in BS4?
3472             this.triggerEl.select('.nav-link',true).first().addClass('dropdown-toggle');
3473         } else {
3474             this.triggerEl.addClass('dropdown-toggle');
3475         }
3476         if (Roo.isTouch) {
3477             this.el.on('touchstart'  , this.onTouch, this);
3478         }
3479         this.el.on('click' , this.onClick, this);
3480
3481         this.el.on("mouseover", this.onMouseOver, this);
3482         this.el.on("mouseout", this.onMouseOut, this);
3483         
3484     },
3485     
3486     findTargetItem : function(e)
3487     {
3488         var t = e.getTarget(".dropdown-menu-item", this.el,  true);
3489         if(!t){
3490             return false;
3491         }
3492         //Roo.log(t);         Roo.log(t.id);
3493         if(t && t.id){
3494             //Roo.log(this.menuitems);
3495             return this.menuitems.get(t.id);
3496             
3497             //return this.items.get(t.menuItemId);
3498         }
3499         
3500         return false;
3501     },
3502     
3503     onTouch : function(e) 
3504     {
3505         Roo.log("menu.onTouch");
3506         //e.stopEvent(); this make the user popdown broken
3507         this.onClick(e);
3508     },
3509     
3510     onClick : function(e)
3511     {
3512         Roo.log("menu.onClick");
3513         
3514         var t = this.findTargetItem(e);
3515         if(!t || t.isContainer){
3516             return;
3517         }
3518         Roo.log(e);
3519         /*
3520         if (Roo.isTouch && e.type == 'touchstart' && t.menu  && !t.disabled) {
3521             if(t == this.activeItem && t.shouldDeactivate(e)){
3522                 this.activeItem.deactivate();
3523                 delete this.activeItem;
3524                 return;
3525             }
3526             if(t.canActivate){
3527                 this.setActiveItem(t, true);
3528             }
3529             return;
3530             
3531             
3532         }
3533         */
3534        
3535         Roo.log('pass click event');
3536         
3537         t.onClick(e);
3538         
3539         this.fireEvent("click", this, t, e);
3540         
3541         var _this = this;
3542         
3543         if(!t.href.length || t.href == '#'){
3544             (function() { _this.hide(); }).defer(100);
3545         }
3546         
3547     },
3548     
3549     onMouseOver : function(e){
3550         var t  = this.findTargetItem(e);
3551         //Roo.log(t);
3552         //if(t){
3553         //    if(t.canActivate && !t.disabled){
3554         //        this.setActiveItem(t, true);
3555         //    }
3556         //}
3557         
3558         this.fireEvent("mouseover", this, e, t);
3559     },
3560     isVisible : function(){
3561         return !this.hidden;
3562     },
3563     onMouseOut : function(e){
3564         var t  = this.findTargetItem(e);
3565         
3566         //if(t ){
3567         //    if(t == this.activeItem && t.shouldDeactivate(e)){
3568         //        this.activeItem.deactivate();
3569         //        delete this.activeItem;
3570         //    }
3571         //}
3572         this.fireEvent("mouseout", this, e, t);
3573     },
3574     
3575     
3576     /**
3577      * Displays this menu relative to another element
3578      * @param {String/HTMLElement/Roo.Element} element The element to align to
3579      * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
3580      * the element (defaults to this.defaultAlign)
3581      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
3582      */
3583     show : function(el, pos, parentMenu)
3584     {
3585         if (false === this.fireEvent("beforeshow", this)) {
3586             Roo.log("show canceled");
3587             return;
3588         }
3589         this.parentMenu = parentMenu;
3590         if(!this.el){
3591             this.render();
3592         }
3593         
3594         this.showAt(this.el.getAlignToXY(el, pos || this.defaultAlign), parentMenu, false);
3595     },
3596      /**
3597      * Displays this menu at a specific xy position
3598      * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
3599      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
3600      */
3601     showAt : function(xy, parentMenu, /* private: */_e){
3602         this.parentMenu = parentMenu;
3603         if(!this.el){
3604             this.render();
3605         }
3606         if(_e !== false){
3607             this.fireEvent("beforeshow", this);
3608             //xy = this.el.adjustForConstraints(xy);
3609         }
3610         
3611         //this.el.show();
3612         this.hideMenuItems();
3613         this.hidden = false;
3614         this.triggerEl.addClass('open');
3615         this.el.addClass('show');
3616         
3617         // reassign x when hitting right
3618         if(this.el.getWidth() + xy[0] >= Roo.lib.Dom.getViewWidth()){
3619             xy[0] = xy[0] - this.el.getWidth() + this.triggerEl.getWidth();
3620         }
3621         
3622         // reassign y when hitting bottom
3623         if(this.el.getHeight() + xy[1] >= Roo.lib.Dom.getViewHeight()){
3624             xy[1] = xy[1] - this.el.getHeight() - this.triggerEl.getHeight();
3625         }
3626         
3627         // but the list may align on trigger left or trigger top... should it be a properity?
3628         
3629         if(this.el.getStyle('top') != 'auto' && this.el.getStyle('top').slice(-1) != "%"){
3630             this.el.setXY(xy);
3631         }
3632         
3633         this.focus();
3634         this.fireEvent("show", this);
3635     },
3636     
3637     focus : function(){
3638         return;
3639         if(!this.hidden){
3640             this.doFocus.defer(50, this);
3641         }
3642     },
3643
3644     doFocus : function(){
3645         if(!this.hidden){
3646             this.focusEl.focus();
3647         }
3648     },
3649
3650     /**
3651      * Hides this menu and optionally all parent menus
3652      * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
3653      */
3654     hide : function(deep)
3655     {
3656         if (false === this.fireEvent("beforehide", this)) {
3657             Roo.log("hide canceled");
3658             return;
3659         }
3660         this.hideMenuItems();
3661         if(this.el && this.isVisible()){
3662            
3663             if(this.activeItem){
3664                 this.activeItem.deactivate();
3665                 this.activeItem = null;
3666             }
3667             this.triggerEl.removeClass('open');;
3668             this.el.removeClass('show');
3669             this.hidden = true;
3670             this.fireEvent("hide", this);
3671         }
3672         if(deep === true && this.parentMenu){
3673             this.parentMenu.hide(true);
3674         }
3675     },
3676     
3677     onTriggerClick : function(e)
3678     {
3679         Roo.log('trigger click');
3680         
3681         var target = e.getTarget();
3682         
3683         Roo.log(target.nodeName.toLowerCase());
3684         
3685         if(target.nodeName.toLowerCase() === 'i'){
3686             e.preventDefault();
3687         }
3688         
3689     },
3690     
3691     onTriggerPress  : function(e)
3692     {
3693         Roo.log('trigger press');
3694         //Roo.log(e.getTarget());
3695        // Roo.log(this.triggerEl.dom);
3696        
3697         // trigger only occurs on normal menu's -- if it's a treeview or dropdown... do not hide/show..
3698         var pel = Roo.get(e.getTarget());
3699         if (pel.findParent('.dropdown-menu') || pel.findParent('.treeview-menu') ) {
3700             Roo.log('is treeview or dropdown?');
3701             return;
3702         }
3703         
3704         if(e.getTarget().nodeName.toLowerCase() !== 'i' && this.isLink){
3705             return;
3706         }
3707         
3708         if (this.isVisible()) {
3709             Roo.log('hide');
3710             this.hide();
3711         } else {
3712             Roo.log('show');
3713             this.show(this.triggerEl, '?', false);
3714         }
3715         
3716         if(this.stopEvent || e.getTarget().nodeName.toLowerCase() === 'i'){
3717             e.stopEvent();
3718         }
3719         
3720     },
3721        
3722     
3723     hideMenuItems : function()
3724     {
3725         Roo.log("hide Menu Items");
3726         if (!this.el) { 
3727             return;
3728         }
3729         
3730         this.el.select('.open',true).each(function(aa) {
3731             
3732             aa.removeClass('open');
3733          
3734         });
3735     },
3736     addxtypeChild : function (tree, cntr) {
3737         var comp= Roo.bootstrap.Menu.superclass.addxtypeChild.call(this, tree, cntr);
3738           
3739         this.menuitems.add(comp);
3740         return comp;
3741
3742     },
3743     getEl : function()
3744     {
3745         Roo.log(this.el);
3746         return this.el;
3747     },
3748     
3749     clear : function()
3750     {
3751         this.getEl().dom.innerHTML = '';
3752         this.menuitems.clear();
3753     }
3754 });
3755
3756  
3757  /*
3758  * - LGPL
3759  *
3760  * menu item
3761  * 
3762  */
3763
3764
3765 /**
3766  * @class Roo.bootstrap.MenuItem
3767  * @extends Roo.bootstrap.Component
3768  * Bootstrap MenuItem class
3769  * @cfg {String} html the menu label
3770  * @cfg {String} href the link
3771  * @cfg {Boolean} preventDefault do not trigger A href on clicks (default false).
3772  * @cfg {Boolean} isContainer is it a container - just returns a drop down item..
3773  * @cfg {Boolean} active  used on sidebars to highlight active itesm
3774  * @cfg {String} fa favicon to show on left of menu item.
3775  * @cfg {Roo.bootsrap.Menu} menu the child menu.
3776  * 
3777  * 
3778  * @constructor
3779  * Create a new MenuItem
3780  * @param {Object} config The config object
3781  */
3782
3783
3784 Roo.bootstrap.MenuItem = function(config){
3785     Roo.bootstrap.MenuItem.superclass.constructor.call(this, config);
3786     this.addEvents({
3787         // raw events
3788         /**
3789          * @event click
3790          * The raw click event for the entire grid.
3791          * @param {Roo.bootstrap.MenuItem} this
3792          * @param {Roo.EventObject} e
3793          */
3794         "click" : true
3795     });
3796 };
3797
3798 Roo.extend(Roo.bootstrap.MenuItem, Roo.bootstrap.Component,  {
3799     
3800     href : false,
3801     html : false,
3802     preventDefault: false,
3803     isContainer : false,
3804     active : false,
3805     fa: false,
3806     
3807     getAutoCreate : function(){
3808         
3809         if(this.isContainer){
3810             return {
3811                 tag: 'li',
3812                 cls: 'dropdown-menu-item '
3813             };
3814         }
3815         var ctag = {
3816             tag: 'span',
3817             html: 'Link'
3818         };
3819         
3820         var anc = {
3821             tag : 'a',
3822             cls : 'dropdown-item',
3823             href : '#',
3824             cn : [  ]
3825         };
3826         
3827         if (this.fa !== false) {
3828             anc.cn.push({
3829                 tag : 'i',
3830                 cls : 'fa fa-' + this.fa
3831             });
3832         }
3833         
3834         anc.cn.push(ctag);
3835         
3836         
3837         var cfg= {
3838             tag: 'li',
3839             cls: 'dropdown-menu-item',
3840             cn: [ anc ]
3841         };
3842         if (this.parent().type == 'treeview') {
3843             cfg.cls = 'treeview-menu';
3844         }
3845         if (this.active) {
3846             cfg.cls += ' active';
3847         }
3848         
3849         
3850         
3851         anc.href = this.href || cfg.cn[0].href ;
3852         ctag.html = this.html || cfg.cn[0].html ;
3853         return cfg;
3854     },
3855     
3856     initEvents: function()
3857     {
3858         if (this.parent().type == 'treeview') {
3859             this.el.select('a').on('click', this.onClick, this);
3860         }
3861         
3862         if (this.menu) {
3863             this.menu.parentType = this.xtype;
3864             this.menu.triggerEl = this.el;
3865             this.menu = this.addxtype(Roo.apply({}, this.menu));
3866         }
3867         
3868     },
3869     onClick : function(e)
3870     {
3871         Roo.log('item on click ');
3872         
3873         if(this.preventDefault){
3874             e.preventDefault();
3875         }
3876         //this.parent().hideMenuItems();
3877         
3878         this.fireEvent('click', this, e);
3879     },
3880     getEl : function()
3881     {
3882         return this.el;
3883     } 
3884 });
3885
3886  
3887
3888  /*
3889  * - LGPL
3890  *
3891  * menu separator
3892  * 
3893  */
3894
3895
3896 /**
3897  * @class Roo.bootstrap.MenuSeparator
3898  * @extends Roo.bootstrap.Component
3899  * Bootstrap MenuSeparator class
3900  * 
3901  * @constructor
3902  * Create a new MenuItem
3903  * @param {Object} config The config object
3904  */
3905
3906
3907 Roo.bootstrap.MenuSeparator = function(config){
3908     Roo.bootstrap.MenuSeparator.superclass.constructor.call(this, config);
3909 };
3910
3911 Roo.extend(Roo.bootstrap.MenuSeparator, Roo.bootstrap.Component,  {
3912     
3913     getAutoCreate : function(){
3914         var cfg = {
3915             cls: 'divider',
3916             tag : 'li'
3917         };
3918         
3919         return cfg;
3920     }
3921    
3922 });
3923
3924  
3925
3926  
3927 /*
3928 * Licence: LGPL
3929 */
3930
3931 /**
3932  * @class Roo.bootstrap.Modal
3933  * @extends Roo.bootstrap.Component
3934  * Bootstrap Modal class
3935  * @cfg {String} title Title of dialog
3936  * @cfg {String} html - the body of the dialog (for simple ones) - you can also use template..
3937  * @cfg {Roo.Template} tmpl - a template with variables. to use it, add a handler in show:method  adn
3938  * @cfg {Boolean} specificTitle default false
3939  * @cfg {Array} buttons Array of buttons or standard button set..
3940  * @cfg {String} buttonPosition (left|right|center) default right (DEPRICATED) - use mr-auto on buttons to put them on the left
3941  * @cfg {Boolean} animate default true
3942  * @cfg {Boolean} allow_close default true
3943  * @cfg {Boolean} fitwindow default false
3944  * @cfg {Number} width fixed width - usefull for chrome extension only really.
3945  * @cfg {Number} height fixed height - usefull for chrome extension only really.
3946  * @cfg {String} size (sm|lg|xl) default empty
3947  * @cfg {Number} max_width set the max width of modal
3948  * @cfg {Boolean} editableTitle can the title be edited
3949
3950  *
3951  *
3952  * @constructor
3953  * Create a new Modal Dialog
3954  * @param {Object} config The config object
3955  */
3956
3957 Roo.bootstrap.Modal = function(config){
3958     Roo.bootstrap.Modal.superclass.constructor.call(this, config);
3959     this.addEvents({
3960         // raw events
3961         /**
3962          * @event btnclick
3963          * The raw btnclick event for the button
3964          * @param {Roo.EventObject} e
3965          */
3966         "btnclick" : true,
3967         /**
3968          * @event resize
3969          * Fire when dialog resize
3970          * @param {Roo.bootstrap.Modal} this
3971          * @param {Roo.EventObject} e
3972          */
3973         "resize" : true,
3974         /**
3975          * @event titlechanged
3976          * Fire when the editable title has been changed
3977          * @param {Roo.bootstrap.Modal} this
3978          * @param {Roo.EventObject} value
3979          */
3980         "titlechanged" : true 
3981         
3982     });
3983     this.buttons = this.buttons || [];
3984
3985     if (this.tmpl) {
3986         this.tmpl = Roo.factory(this.tmpl);
3987     }
3988
3989 };
3990
3991 Roo.extend(Roo.bootstrap.Modal, Roo.bootstrap.Component,  {
3992
3993     title : 'test dialog',
3994
3995     buttons : false,
3996
3997     // set on load...
3998
3999     html: false,
4000
4001     tmp: false,
4002
4003     specificTitle: false,
4004
4005     buttonPosition: 'right',
4006
4007     allow_close : true,
4008
4009     animate : true,
4010
4011     fitwindow: false,
4012     
4013      // private
4014     dialogEl: false,
4015     bodyEl:  false,
4016     footerEl:  false,
4017     titleEl:  false,
4018     closeEl:  false,
4019
4020     size: '',
4021     
4022     max_width: 0,
4023     
4024     max_height: 0,
4025     
4026     fit_content: false,
4027     editableTitle  : false,
4028
4029     onRender : function(ct, position)
4030     {
4031         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
4032
4033         if(!this.el){
4034             var cfg = Roo.apply({},  this.getAutoCreate());
4035             cfg.id = Roo.id();
4036             //if(!cfg.name){
4037             //    cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
4038             //}
4039             //if (!cfg.name.length) {
4040             //    delete cfg.name;
4041            // }
4042             if (this.cls) {
4043                 cfg.cls += ' ' + this.cls;
4044             }
4045             if (this.style) {
4046                 cfg.style = this.style;
4047             }
4048             this.el = Roo.get(document.body).createChild(cfg, position);
4049         }
4050         //var type = this.el.dom.type;
4051
4052
4053         if(this.tabIndex !== undefined){
4054             this.el.dom.setAttribute('tabIndex', this.tabIndex);
4055         }
4056
4057         this.dialogEl = this.el.select('.modal-dialog',true).first();
4058         this.bodyEl = this.el.select('.modal-body',true).first();
4059         this.closeEl = this.el.select('.modal-header .close', true).first();
4060         this.headerEl = this.el.select('.modal-header',true).first();
4061         this.titleEl = this.el.select('.modal-title',true).first();
4062         this.footerEl = this.el.select('.modal-footer',true).first();
4063
4064         this.maskEl = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
4065         
4066         //this.el.addClass("x-dlg-modal");
4067
4068         if (this.buttons.length) {
4069             Roo.each(this.buttons, function(bb) {
4070                 var b = Roo.apply({}, bb);
4071                 b.xns = b.xns || Roo.bootstrap;
4072                 b.xtype = b.xtype || 'Button';
4073                 if (typeof(b.listeners) == 'undefined') {
4074                     b.listeners = { click : this.onButtonClick.createDelegate(this)  };
4075                 }
4076
4077                 var btn = Roo.factory(b);
4078
4079                 btn.render(this.getButtonContainer());
4080
4081             },this);
4082         }
4083         // render the children.
4084         var nitems = [];
4085
4086         if(typeof(this.items) != 'undefined'){
4087             var items = this.items;
4088             delete this.items;
4089
4090             for(var i =0;i < items.length;i++) {
4091                 nitems.push(this.addxtype(Roo.apply({}, items[i])));
4092             }
4093         }
4094
4095         this.items = nitems;
4096
4097         // where are these used - they used to be body/close/footer
4098
4099
4100         this.initEvents();
4101         //this.el.addClass([this.fieldClass, this.cls]);
4102
4103     },
4104
4105     getAutoCreate : function()
4106     {
4107         // we will default to modal-body-overflow - might need to remove or make optional later.
4108         var bdy = {
4109                 cls : 'modal-body enable-modal-body-overflow ', 
4110                 html : this.html || ''
4111         };
4112
4113         var title = {
4114             tag: 'h4',
4115             cls : 'modal-title',
4116             html : this.title
4117         };
4118
4119         if(this.specificTitle){ // WTF is this?
4120             title = this.title;
4121         }
4122
4123         var header = [];
4124         if (this.allow_close && Roo.bootstrap.version == 3) {
4125             header.push({
4126                 tag: 'button',
4127                 cls : 'close',
4128                 html : '&times'
4129             });
4130         }
4131
4132         header.push(title);
4133
4134         if (this.editableTitle) {
4135             header.push({
4136                 cls: 'form-control roo-editable-title d-none',
4137                 tag: 'input',
4138                 type: 'text'
4139             });
4140         }
4141         
4142         if (this.allow_close && Roo.bootstrap.version == 4) {
4143             header.push({
4144                 tag: 'button',
4145                 cls : 'close',
4146                 html : '&times'
4147             });
4148         }
4149         
4150         var size = '';
4151
4152         if(this.size.length){
4153             size = 'modal-' + this.size;
4154         }
4155         
4156         var footer = Roo.bootstrap.version == 3 ?
4157             {
4158                 cls : 'modal-footer',
4159                 cn : [
4160                     {
4161                         tag: 'div',
4162                         cls: 'btn-' + this.buttonPosition
4163                     }
4164                 ]
4165
4166             } :
4167             {  // BS4 uses mr-auto on left buttons....
4168                 cls : 'modal-footer'
4169             };
4170
4171             
4172
4173         
4174         
4175         var modal = {
4176             cls: "modal",
4177              cn : [
4178                 {
4179                     cls: "modal-dialog " + size,
4180                     cn : [
4181                         {
4182                             cls : "modal-content",
4183                             cn : [
4184                                 {
4185                                     cls : 'modal-header',
4186                                     cn : header
4187                                 },
4188                                 bdy,
4189                                 footer
4190                             ]
4191
4192                         }
4193                     ]
4194
4195                 }
4196             ]
4197         };
4198
4199         if(this.animate){
4200             modal.cls += ' fade';
4201         }
4202
4203         return modal;
4204
4205     },
4206     getChildContainer : function() {
4207
4208          return this.bodyEl;
4209
4210     },
4211     getButtonContainer : function() {
4212         
4213          return Roo.bootstrap.version == 4 ?
4214             this.el.select('.modal-footer',true).first()
4215             : this.el.select('.modal-footer div',true).first();
4216
4217     },
4218     initEvents : function()
4219     {
4220         if (this.allow_close) {
4221             this.closeEl.on('click', this.hide, this);
4222         }
4223         Roo.EventManager.onWindowResize(this.resize, this, true);
4224         if (this.editableTitle) {
4225             this.headerEditEl =  this.headerEl.select('.form-control',true).first();
4226             this.headerEl.on('click', function() { this.toggleHeaderInput(true) } , this);
4227             this.headerEditEl.on('keyup', function(e) {
4228                     if([  e.RETURN , e.TAB , e.ESC ].indexOf(e.keyCode) > -1) {
4229                         this.toggleHeaderInput(false)
4230                     }
4231                 }, this);
4232             this.headerEditEl.on('blur', function(e) {
4233                 this.toggleHeaderInput(false)
4234             },this);
4235         }
4236
4237     },
4238   
4239
4240     resize : function()
4241     {
4242         this.maskEl.setSize(
4243             Roo.lib.Dom.getViewWidth(true),
4244             Roo.lib.Dom.getViewHeight(true)
4245         );
4246         
4247         if (this.fitwindow) {
4248             
4249            
4250             this.setSize(
4251                 this.width || Roo.lib.Dom.getViewportWidth(true) - 30,
4252                 this.height || Roo.lib.Dom.getViewportHeight(true) // catering margin-top 30 margin-bottom 30
4253             );
4254             return;
4255         }
4256         
4257         if(this.max_width !== 0) {
4258             
4259             var w = Math.min(this.max_width, Roo.lib.Dom.getViewportWidth(true) - 30);
4260             
4261             if(this.height) {
4262                 this.setSize(w, this.height);
4263                 return;
4264             }
4265             
4266             if(this.max_height) {
4267                 this.setSize(w,Math.min(
4268                     this.max_height,
4269                     Roo.lib.Dom.getViewportHeight(true) - 60
4270                 ));
4271                 
4272                 return;
4273             }
4274             
4275             if(!this.fit_content) {
4276                 this.setSize(w, Roo.lib.Dom.getViewportHeight(true) - 60);
4277                 return;
4278             }
4279             
4280             this.setSize(w, Math.min(
4281                 60 +
4282                 this.headerEl.getHeight() + 
4283                 this.footerEl.getHeight() + 
4284                 this.getChildHeight(this.bodyEl.dom.childNodes),
4285                 Roo.lib.Dom.getViewportHeight(true) - 60)
4286             );
4287         }
4288         
4289     },
4290
4291     setSize : function(w,h)
4292     {
4293         if (!w && !h) {
4294             return;
4295         }
4296         
4297         this.resizeTo(w,h);
4298     },
4299
4300     show : function() {
4301
4302         if (!this.rendered) {
4303             this.render();
4304         }
4305
4306         //this.el.setStyle('display', 'block');
4307         this.el.removeClass('hideing');
4308         this.el.dom.style.display='block';
4309         
4310         Roo.get(document.body).addClass('modal-open');
4311  
4312         if(this.animate){  // element has 'fade'  - so stuff happens after .3s ?- not sure why the delay?
4313             
4314             (function(){
4315                 this.el.addClass('show');
4316                 this.el.addClass('in');
4317             }).defer(50, this);
4318         }else{
4319             this.el.addClass('show');
4320             this.el.addClass('in');
4321         }
4322
4323         // not sure how we can show data in here..
4324         //if (this.tmpl) {
4325         //    this.getChildContainer().dom.innerHTML = this.tmpl.applyTemplate(this);
4326         //}
4327
4328         Roo.get(document.body).addClass("x-body-masked");
4329         
4330         this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true),   Roo.lib.Dom.getViewHeight(true));
4331         this.maskEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
4332         this.maskEl.dom.style.display = 'block';
4333         this.maskEl.addClass('show');
4334         
4335         
4336         this.resize();
4337         
4338         this.fireEvent('show', this);
4339
4340         // set zindex here - otherwise it appears to be ignored...
4341         this.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
4342
4343         (function () {
4344             this.items.forEach( function(e) {
4345                 e.layout ? e.layout() : false;
4346
4347             });
4348         }).defer(100,this);
4349
4350     },
4351     hide : function()
4352     {
4353         if(this.fireEvent("beforehide", this) !== false){
4354             
4355             this.maskEl.removeClass('show');
4356             
4357             this.maskEl.dom.style.display = '';
4358             Roo.get(document.body).removeClass("x-body-masked");
4359             this.el.removeClass('in');
4360             this.el.select('.modal-dialog', true).first().setStyle('transform','');
4361
4362             if(this.animate){ // why
4363                 this.el.addClass('hideing');
4364                 this.el.removeClass('show');
4365                 (function(){
4366                     if (!this.el.hasClass('hideing')) {
4367                         return; // it's been shown again...
4368                     }
4369                     
4370                     this.el.dom.style.display='';
4371
4372                     Roo.get(document.body).removeClass('modal-open');
4373                     this.el.removeClass('hideing');
4374                 }).defer(150,this);
4375                 
4376             }else{
4377                 this.el.removeClass('show');
4378                 this.el.dom.style.display='';
4379                 Roo.get(document.body).removeClass('modal-open');
4380
4381             }
4382             this.fireEvent('hide', this);
4383         }
4384     },
4385     isVisible : function()
4386     {
4387         
4388         return this.el.hasClass('show') && !this.el.hasClass('hideing');
4389         
4390     },
4391
4392     addButton : function(str, cb)
4393     {
4394
4395
4396         var b = Roo.apply({}, { html : str } );
4397         b.xns = b.xns || Roo.bootstrap;
4398         b.xtype = b.xtype || 'Button';
4399         if (typeof(b.listeners) == 'undefined') {
4400             b.listeners = { click : cb.createDelegate(this)  };
4401         }
4402
4403         var btn = Roo.factory(b);
4404
4405         btn.render(this.getButtonContainer());
4406
4407         return btn;
4408
4409     },
4410
4411     setDefaultButton : function(btn)
4412     {
4413         //this.el.select('.modal-footer').()
4414     },
4415
4416     resizeTo: function(w,h)
4417     {
4418         this.dialogEl.setWidth(w);
4419         
4420         var diff = this.headerEl.getHeight() + this.footerEl.getHeight() + 60; // dialog margin-bottom: 30  
4421
4422         this.bodyEl.setHeight(h - diff);
4423         
4424         this.fireEvent('resize', this);
4425     },
4426     
4427     setContentSize  : function(w, h)
4428     {
4429
4430     },
4431     onButtonClick: function(btn,e)
4432     {
4433         //Roo.log([a,b,c]);
4434         this.fireEvent('btnclick', btn.name, e);
4435     },
4436      /**
4437      * Set the title of the Dialog
4438      * @param {String} str new Title
4439      */
4440     setTitle: function(str) {
4441         this.titleEl.dom.innerHTML = str;
4442         this.title = str;
4443     },
4444     /**
4445      * Set the body of the Dialog
4446      * @param {String} str new Title
4447      */
4448     setBody: function(str) {
4449         this.bodyEl.dom.innerHTML = str;
4450     },
4451     /**
4452      * Set the body of the Dialog using the template
4453      * @param {Obj} data - apply this data to the template and replace the body contents.
4454      */
4455     applyBody: function(obj)
4456     {
4457         if (!this.tmpl) {
4458             Roo.log("Error - using apply Body without a template");
4459             //code
4460         }
4461         this.tmpl.overwrite(this.bodyEl, obj);
4462     },
4463     
4464     getChildHeight : function(child_nodes)
4465     {
4466         if(
4467             !child_nodes ||
4468             child_nodes.length == 0
4469         ) {
4470             return 0;
4471         }
4472         
4473         var child_height = 0;
4474         
4475         for(var i = 0; i < child_nodes.length; i++) {
4476             
4477             /*
4478             * for modal with tabs...
4479             if(child_nodes[i].classList.contains('roo-layout-panel')) {
4480                 
4481                 var layout_childs = child_nodes[i].childNodes;
4482                 
4483                 for(var j = 0; j < layout_childs.length; j++) {
4484                     
4485                     if(layout_childs[j].classList.contains('roo-layout-panel-body')) {
4486                         
4487                         var layout_body_childs = layout_childs[j].childNodes;
4488                         
4489                         for(var k = 0; k < layout_body_childs.length; k++) {
4490                             
4491                             if(layout_body_childs[k].classList.contains('navbar')) {
4492                                 child_height += layout_body_childs[k].offsetHeight;
4493                                 continue;
4494                             }
4495                             
4496                             if(layout_body_childs[k].classList.contains('roo-layout-tabs-body')) {
4497                                 
4498                                 var layout_body_tab_childs = layout_body_childs[k].childNodes;
4499                                 
4500                                 for(var m = 0; m < layout_body_tab_childs.length; m++) {
4501                                     
4502                                     if(layout_body_tab_childs[m].classList.contains('roo-layout-active-content')) {
4503                                         child_height += this.getChildHeight(layout_body_tab_childs[m].childNodes);
4504                                         continue;
4505                                     }
4506                                     
4507                                 }
4508                                 
4509                             }
4510                             
4511                         }
4512                     }
4513                 }
4514                 continue;
4515             }
4516             */
4517             
4518             child_height += child_nodes[i].offsetHeight;
4519             // Roo.log(child_nodes[i].offsetHeight);
4520         }
4521         
4522         return child_height;
4523     },
4524     toggleHeaderInput : function(is_edit)
4525     {
4526         
4527         if (is_edit && this.is_header_editing) {
4528             return; // already editing..
4529         }
4530         if (is_edit) {
4531     
4532             this.headerEditEl.dom.value = this.title;
4533             this.headerEditEl.removeClass('d-none');
4534             this.headerEditEl.dom.focus();
4535             this.titleEl.addClass('d-none');
4536             
4537             this.is_header_editing = true;
4538             return
4539         }
4540         // flip back to not editing.
4541         this.title = this.headerEditEl.dom.value;
4542         this.headerEditEl.addClass('d-none');
4543         this.titleEl.removeClass('d-none');
4544         this.titleEl.dom.innerHTML = String.format('{0}', this.title);
4545         this.is_header_editing = false;
4546         this.fireEvent('titlechanged', this, this.title);
4547     
4548             
4549         
4550     }
4551
4552 });
4553
4554
4555 Roo.apply(Roo.bootstrap.Modal,  {
4556     /**
4557          * Button config that displays a single OK button
4558          * @type Object
4559          */
4560         OK :  [{
4561             name : 'ok',
4562             weight : 'primary',
4563             html : 'OK'
4564         }],
4565         /**
4566          * Button config that displays Yes and No buttons
4567          * @type Object
4568          */
4569         YESNO : [
4570             {
4571                 name  : 'no',
4572                 html : 'No'
4573             },
4574             {
4575                 name  :'yes',
4576                 weight : 'primary',
4577                 html : 'Yes'
4578             }
4579         ],
4580
4581         /**
4582          * Button config that displays OK and Cancel buttons
4583          * @type Object
4584          */
4585         OKCANCEL : [
4586             {
4587                name : 'cancel',
4588                 html : 'Cancel'
4589             },
4590             {
4591                 name : 'ok',
4592                 weight : 'primary',
4593                 html : 'OK'
4594             }
4595         ],
4596         /**
4597          * Button config that displays Yes, No and Cancel buttons
4598          * @type Object
4599          */
4600         YESNOCANCEL : [
4601             {
4602                 name : 'yes',
4603                 weight : 'primary',
4604                 html : 'Yes'
4605             },
4606             {
4607                 name : 'no',
4608                 html : 'No'
4609             },
4610             {
4611                 name : 'cancel',
4612                 html : 'Cancel'
4613             }
4614         ],
4615         
4616         zIndex : 10001
4617 });
4618
4619 /*
4620  * - LGPL
4621  *
4622  * messagebox - can be used as a replace
4623  * 
4624  */
4625 /**
4626  * @class Roo.MessageBox
4627  * Utility class for generating different styles of message boxes.  The alias Roo.Msg can also be used.
4628  * Example usage:
4629  *<pre><code>
4630 // Basic alert:
4631 Roo.Msg.alert('Status', 'Changes saved successfully.');
4632
4633 // Prompt for user data:
4634 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
4635     if (btn == 'ok'){
4636         // process text value...
4637     }
4638 });
4639
4640 // Show a dialog using config options:
4641 Roo.Msg.show({
4642    title:'Save Changes?',
4643    msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
4644    buttons: Roo.Msg.YESNOCANCEL,
4645    fn: processResult,
4646    animEl: 'elId'
4647 });
4648 </code></pre>
4649  * @singleton
4650  */
4651 Roo.bootstrap.MessageBox = function(){
4652     var dlg, opt, mask, waitTimer;
4653     var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
4654     var buttons, activeTextEl, bwidth;
4655
4656     
4657     // private
4658     var handleButton = function(button){
4659         dlg.hide();
4660         Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
4661     };
4662
4663     // private
4664     var handleHide = function(){
4665         if(opt && opt.cls){
4666             dlg.el.removeClass(opt.cls);
4667         }
4668         //if(waitTimer){
4669         //    Roo.TaskMgr.stop(waitTimer);
4670         //    waitTimer = null;
4671         //}
4672     };
4673
4674     // private
4675     var updateButtons = function(b){
4676         var width = 0;
4677         if(!b){
4678             buttons["ok"].hide();
4679             buttons["cancel"].hide();
4680             buttons["yes"].hide();
4681             buttons["no"].hide();
4682             dlg.footerEl.hide();
4683             
4684             return width;
4685         }
4686         dlg.footerEl.show();
4687         for(var k in buttons){
4688             if(typeof buttons[k] != "function"){
4689                 if(b[k]){
4690                     buttons[k].show();
4691                     buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.bootstrap.MessageBox.buttonText[k]);
4692                     width += buttons[k].el.getWidth()+15;
4693                 }else{
4694                     buttons[k].hide();
4695                 }
4696             }
4697         }
4698         return width;
4699     };
4700
4701     // private
4702     var handleEsc = function(d, k, e){
4703         if(opt && opt.closable !== false){
4704             dlg.hide();
4705         }
4706         if(e){
4707             e.stopEvent();
4708         }
4709     };
4710
4711     return {
4712         /**
4713          * Returns a reference to the underlying {@link Roo.BasicDialog} element
4714          * @return {Roo.BasicDialog} The BasicDialog element
4715          */
4716         getDialog : function(){
4717            if(!dlg){
4718                 dlg = new Roo.bootstrap.Modal( {
4719                     //draggable: true,
4720                     //resizable:false,
4721                     //constraintoviewport:false,
4722                     //fixedcenter:true,
4723                     //collapsible : false,
4724                     //shim:true,
4725                     //modal: true,
4726                 //    width: 'auto',
4727                   //  height:100,
4728                     //buttonAlign:"center",
4729                     closeClick : function(){
4730                         if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
4731                             handleButton("no");
4732                         }else{
4733                             handleButton("cancel");
4734                         }
4735                     }
4736                 });
4737                 dlg.render();
4738                 dlg.on("hide", handleHide);
4739                 mask = dlg.mask;
4740                 //dlg.addKeyListener(27, handleEsc);
4741                 buttons = {};
4742                 this.buttons = buttons;
4743                 var bt = this.buttonText;
4744                 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
4745                 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
4746                 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
4747                 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
4748                 //Roo.log(buttons);
4749                 bodyEl = dlg.bodyEl.createChild({
4750
4751                     html:'<span class="roo-mb-text"></span><br /><input type="text" class="roo-mb-input" />' +
4752                         '<textarea class="roo-mb-textarea"></textarea>' +
4753                         '<div class="roo-mb-progress-wrap"><div class="roo-mb-progress"><div class="roo-mb-progress-bar">&#160;</div></div></div>'
4754                 });
4755                 msgEl = bodyEl.dom.firstChild;
4756                 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
4757                 textboxEl.enableDisplayMode();
4758                 textboxEl.addKeyListener([10,13], function(){
4759                     if(dlg.isVisible() && opt && opt.buttons){
4760                         if(opt.buttons.ok){
4761                             handleButton("ok");
4762                         }else if(opt.buttons.yes){
4763                             handleButton("yes");
4764                         }
4765                     }
4766                 });
4767                 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
4768                 textareaEl.enableDisplayMode();
4769                 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
4770                 progressEl.enableDisplayMode();
4771                 
4772                 // This is supposed to be the progessElement.. but I think it's controlling the height of everything..
4773                 var pf = progressEl.dom.firstChild;
4774                 if (pf) {
4775                     pp = Roo.get(pf.firstChild);
4776                     pp.setHeight(pf.offsetHeight);
4777                 }
4778                 
4779             }
4780             return dlg;
4781         },
4782
4783         /**
4784          * Updates the message box body text
4785          * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
4786          * the XHTML-compliant non-breaking space character '&amp;#160;')
4787          * @return {Roo.MessageBox} This message box
4788          */
4789         updateText : function(text)
4790         {
4791             if(!dlg.isVisible() && !opt.width){
4792                 dlg.dialogEl.setStyle({ 'max-width' : this.maxWidth});
4793                 // dlg.resizeTo(this.maxWidth, 100); // forcing the height breaks long alerts()
4794             }
4795             msgEl.innerHTML = text || '&#160;';
4796       
4797             var cw =  Math.max(msgEl.offsetWidth, msgEl.parentNode.scrollWidth);
4798             //Roo.log("guesed size: " + JSON.stringify([cw,msgEl.offsetWidth, msgEl.parentNode.scrollWidth]));
4799             var w = Math.max(
4800                     Math.min(opt.width || cw , this.maxWidth), 
4801                     Math.max(opt.minWidth || this.minWidth, bwidth)
4802             );
4803             if(opt.prompt){
4804                 activeTextEl.setWidth(w);
4805             }
4806             if(dlg.isVisible()){
4807                 dlg.fixedcenter = false;
4808             }
4809             // to big, make it scroll. = But as usual stupid IE does not support
4810             // !important..
4811             
4812             if ( bodyEl.getHeight() > (Roo.lib.Dom.getViewHeight() - 100)) {
4813                 bodyEl.setHeight ( Roo.lib.Dom.getViewHeight() - 100 );
4814                 bodyEl.dom.style.overflowY = 'auto' + ( Roo.isIE ? '' : ' !important');
4815             } else {
4816                 bodyEl.dom.style.height = '';
4817                 bodyEl.dom.style.overflowY = '';
4818             }
4819             if (cw > w) {
4820                 bodyEl.dom.style.get = 'auto' + ( Roo.isIE ? '' : ' !important');
4821             } else {
4822                 bodyEl.dom.style.overflowX = '';
4823             }
4824             
4825             dlg.setContentSize(w, bodyEl.getHeight());
4826             if(dlg.isVisible()){
4827                 dlg.fixedcenter = true;
4828             }
4829             return this;
4830         },
4831
4832         /**
4833          * Updates a progress-style message box's text and progress bar.  Only relevant on message boxes
4834          * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
4835          * @param {Number} value Any number between 0 and 1 (e.g., .5)
4836          * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
4837          * @return {Roo.MessageBox} This message box
4838          */
4839         updateProgress : function(value, text){
4840             if(text){
4841                 this.updateText(text);
4842             }
4843             
4844             if (pp) { // weird bug on my firefox - for some reason this is not defined
4845                 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
4846                 pp.setHeight(Math.floor(progressEl.dom.firstChild.offsetHeight));
4847             }
4848             return this;
4849         },        
4850
4851         /**
4852          * Returns true if the message box is currently displayed
4853          * @return {Boolean} True if the message box is visible, else false
4854          */
4855         isVisible : function(){
4856             return dlg && dlg.isVisible();  
4857         },
4858
4859         /**
4860          * Hides the message box if it is displayed
4861          */
4862         hide : function(){
4863             if(this.isVisible()){
4864                 dlg.hide();
4865             }  
4866         },
4867
4868         /**
4869          * Displays a new message box, or reinitializes an existing message box, based on the config options
4870          * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
4871          * The following config object properties are supported:
4872          * <pre>
4873 Property    Type             Description
4874 ----------  ---------------  ------------------------------------------------------------------------------------
4875 animEl            String/Element   An id or Element from which the message box should animate as it opens and
4876                                    closes (defaults to undefined)
4877 buttons           Object/Boolean   A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
4878                                    cancel:'Bar'}), or false to not show any buttons (defaults to false)
4879 closable          Boolean          False to hide the top-right close button (defaults to true).  Note that
4880                                    progress and wait dialogs will ignore this property and always hide the
4881                                    close button as they can only be closed programmatically.
4882 cls               String           A custom CSS class to apply to the message box element
4883 defaultTextHeight Number           The default height in pixels of the message box's multiline textarea if
4884                                    displayed (defaults to 75)
4885 fn                Function         A callback function to execute after closing the dialog.  The arguments to the
4886                                    function will be btn (the name of the button that was clicked, if applicable,
4887                                    e.g. "ok"), and text (the value of the active text field, if applicable).
4888                                    Progress and wait dialogs will ignore this option since they do not respond to
4889                                    user actions and can only be closed programmatically, so any required function
4890                                    should be called by the same code after it closes the dialog.
4891 icon              String           A CSS class that provides a background image to be used as an icon for
4892                                    the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
4893 maxWidth          Number           The maximum width in pixels of the message box (defaults to 600)
4894 minWidth          Number           The minimum width in pixels of the message box (defaults to 100)
4895 modal             Boolean          False to allow user interaction with the page while the message box is
4896                                    displayed (defaults to true)
4897 msg               String           A string that will replace the existing message box body text (defaults
4898                                    to the XHTML-compliant non-breaking space character '&#160;')
4899 multiline         Boolean          True to prompt the user to enter multi-line text (defaults to false)
4900 progress          Boolean          True to display a progress bar (defaults to false)
4901 progressText      String           The text to display inside the progress bar if progress = true (defaults to '')
4902 prompt            Boolean          True to prompt the user to enter single-line text (defaults to false)
4903 proxyDrag         Boolean          True to display a lightweight proxy while dragging (defaults to false)
4904 title             String           The title text
4905 value             String           The string value to set into the active textbox element if displayed
4906 wait              Boolean          True to display a progress bar (defaults to false)
4907 width             Number           The width of the dialog in pixels
4908 </pre>
4909          *
4910          * Example usage:
4911          * <pre><code>
4912 Roo.Msg.show({
4913    title: 'Address',
4914    msg: 'Please enter your address:',
4915    width: 300,
4916    buttons: Roo.MessageBox.OKCANCEL,
4917    multiline: true,
4918    fn: saveAddress,
4919    animEl: 'addAddressBtn'
4920 });
4921 </code></pre>
4922          * @param {Object} config Configuration options
4923          * @return {Roo.MessageBox} This message box
4924          */
4925         show : function(options)
4926         {
4927             
4928             // this causes nightmares if you show one dialog after another
4929             // especially on callbacks..
4930              
4931             if(this.isVisible()){
4932                 
4933                 this.hide();
4934                 Roo.log("[Roo.Messagebox] Show called while message displayed:" );
4935                 Roo.log("Old Dialog Message:" +  msgEl.innerHTML );
4936                 Roo.log("New Dialog Message:" +  options.msg )
4937                 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
4938                 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
4939                 
4940             }
4941             var d = this.getDialog();
4942             opt = options;
4943             d.setTitle(opt.title || "&#160;");
4944             d.closeEl.setDisplayed(opt.closable !== false);
4945             activeTextEl = textboxEl;
4946             opt.prompt = opt.prompt || (opt.multiline ? true : false);
4947             if(opt.prompt){
4948                 if(opt.multiline){
4949                     textboxEl.hide();
4950                     textareaEl.show();
4951                     textareaEl.setHeight(typeof opt.multiline == "number" ?
4952                         opt.multiline : this.defaultTextHeight);
4953                     activeTextEl = textareaEl;
4954                 }else{
4955                     textboxEl.show();
4956                     textareaEl.hide();
4957                 }
4958             }else{
4959                 textboxEl.hide();
4960                 textareaEl.hide();
4961             }
4962             progressEl.setDisplayed(opt.progress === true);
4963             if (opt.progress) {
4964                 d.animate = false; // do not animate progress, as it may not have finished animating before we close it..
4965             }
4966             this.updateProgress(0);
4967             activeTextEl.dom.value = opt.value || "";
4968             if(opt.prompt){
4969                 dlg.setDefaultButton(activeTextEl);
4970             }else{
4971                 var bs = opt.buttons;
4972                 var db = null;
4973                 if(bs && bs.ok){
4974                     db = buttons["ok"];
4975                 }else if(bs && bs.yes){
4976                     db = buttons["yes"];
4977                 }
4978                 dlg.setDefaultButton(db);
4979             }
4980             bwidth = updateButtons(opt.buttons);
4981             this.updateText(opt.msg);
4982             if(opt.cls){
4983                 d.el.addClass(opt.cls);
4984             }
4985             d.proxyDrag = opt.proxyDrag === true;
4986             d.modal = opt.modal !== false;
4987             d.mask = opt.modal !== false ? mask : false;
4988             if(!d.isVisible()){
4989                 // force it to the end of the z-index stack so it gets a cursor in FF
4990                 document.body.appendChild(dlg.el.dom);
4991                 d.animateTarget = null;
4992                 d.show(options.animEl);
4993             }
4994             return this;
4995         },
4996
4997         /**
4998          * Displays a message box with a progress bar.  This message box has no buttons and is not closeable by
4999          * the user.  You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
5000          * and closing the message box when the process is complete.
5001          * @param {String} title The title bar text
5002          * @param {String} msg The message box body text
5003          * @return {Roo.MessageBox} This message box
5004          */
5005         progress : function(title, msg){
5006             this.show({
5007                 title : title,
5008                 msg : msg,
5009                 buttons: false,
5010                 progress:true,
5011                 closable:false,
5012                 minWidth: this.minProgressWidth,
5013                 modal : true
5014             });
5015             return this;
5016         },
5017
5018         /**
5019          * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
5020          * If a callback function is passed it will be called after the user clicks the button, and the
5021          * id of the button that was clicked will be passed as the only parameter to the callback
5022          * (could also be the top-right close button).
5023          * @param {String} title The title bar text
5024          * @param {String} msg The message box body text
5025          * @param {Function} fn (optional) The callback function invoked after the message box is closed
5026          * @param {Object} scope (optional) The scope of the callback function
5027          * @return {Roo.MessageBox} This message box
5028          */
5029         alert : function(title, msg, fn, scope)
5030         {
5031             this.show({
5032                 title : title,
5033                 msg : msg,
5034                 buttons: this.OK,
5035                 fn: fn,
5036                 closable : false,
5037                 scope : scope,
5038                 modal : true
5039             });
5040             return this;
5041         },
5042
5043         /**
5044          * Displays a message box with an infinitely auto-updating progress bar.  This can be used to block user
5045          * interaction while waiting for a long-running process to complete that does not have defined intervals.
5046          * You are responsible for closing the message box when the process is complete.
5047          * @param {String} msg The message box body text
5048          * @param {String} title (optional) The title bar text
5049          * @return {Roo.MessageBox} This message box
5050          */
5051         wait : function(msg, title){
5052             this.show({
5053                 title : title,
5054                 msg : msg,
5055                 buttons: false,
5056                 closable:false,
5057                 progress:true,
5058                 modal:true,
5059                 width:300,
5060                 wait:true
5061             });
5062             waitTimer = Roo.TaskMgr.start({
5063                 run: function(i){
5064                     Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
5065                 },
5066                 interval: 1000
5067             });
5068             return this;
5069         },
5070
5071         /**
5072          * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
5073          * If a callback function is passed it will be called after the user clicks either button, and the id of the
5074          * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
5075          * @param {String} title The title bar text
5076          * @param {String} msg The message box body text
5077          * @param {Function} fn (optional) The callback function invoked after the message box is closed
5078          * @param {Object} scope (optional) The scope of the callback function
5079          * @return {Roo.MessageBox} This message box
5080          */
5081         confirm : function(title, msg, fn, scope){
5082             this.show({
5083                 title : title,
5084                 msg : msg,
5085                 buttons: this.YESNO,
5086                 fn: fn,
5087                 scope : scope,
5088                 modal : true
5089             });
5090             return this;
5091         },
5092
5093         /**
5094          * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
5095          * JavaScript's Window.prompt).  The prompt can be a single-line or multi-line textbox.  If a callback function
5096          * is passed it will be called after the user clicks either button, and the id of the button that was clicked
5097          * (could also be the top-right close button) and the text that was entered will be passed as the two
5098          * parameters to the callback.
5099          * @param {String} title The title bar text
5100          * @param {String} msg The message box body text
5101          * @param {Function} fn (optional) The callback function invoked after the message box is closed
5102          * @param {Object} scope (optional) The scope of the callback function
5103          * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
5104          * property, or the height in pixels to create the textbox (defaults to false / single-line)
5105          * @return {Roo.MessageBox} This message box
5106          */
5107         prompt : function(title, msg, fn, scope, multiline){
5108             this.show({
5109                 title : title,
5110                 msg : msg,
5111                 buttons: this.OKCANCEL,
5112                 fn: fn,
5113                 minWidth:250,
5114                 scope : scope,
5115                 prompt:true,
5116                 multiline: multiline,
5117                 modal : true
5118             });
5119             return this;
5120         },
5121
5122         /**
5123          * Button config that displays a single OK button
5124          * @type Object
5125          */
5126         OK : {ok:true},
5127         /**
5128          * Button config that displays Yes and No buttons
5129          * @type Object
5130          */
5131         YESNO : {yes:true, no:true},
5132         /**
5133          * Button config that displays OK and Cancel buttons
5134          * @type Object
5135          */
5136         OKCANCEL : {ok:true, cancel:true},
5137         /**
5138          * Button config that displays Yes, No and Cancel buttons
5139          * @type Object
5140          */
5141         YESNOCANCEL : {yes:true, no:true, cancel:true},
5142
5143         /**
5144          * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
5145          * @type Number
5146          */
5147         defaultTextHeight : 75,
5148         /**
5149          * The maximum width in pixels of the message box (defaults to 600)
5150          * @type Number
5151          */
5152         maxWidth : 600,
5153         /**
5154          * The minimum width in pixels of the message box (defaults to 100)
5155          * @type Number
5156          */
5157         minWidth : 100,
5158         /**
5159          * The minimum width in pixels of the message box if it is a progress-style dialog.  This is useful
5160          * for setting a different minimum width than text-only dialogs may need (defaults to 250)
5161          * @type Number
5162          */
5163         minProgressWidth : 250,
5164         /**
5165          * An object containing the default button text strings that can be overriden for localized language support.
5166          * Supported properties are: ok, cancel, yes and no.
5167          * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
5168          * @type Object
5169          */
5170         buttonText : {
5171             ok : "OK",
5172             cancel : "Cancel",
5173             yes : "Yes",
5174             no : "No"
5175         }
5176     };
5177 }();
5178
5179 /**
5180  * Shorthand for {@link Roo.MessageBox}
5181  */
5182 Roo.MessageBox = Roo.MessageBox || Roo.bootstrap.MessageBox;
5183 Roo.Msg = Roo.Msg || Roo.MessageBox;
5184 /*
5185  * - LGPL
5186  *
5187  * navbar
5188  * 
5189  */
5190
5191 /**
5192  * @class Roo.bootstrap.Navbar
5193  * @extends Roo.bootstrap.Component
5194  * Bootstrap Navbar class
5195
5196  * @constructor
5197  * Create a new Navbar
5198  * @param {Object} config The config object
5199  */
5200
5201
5202 Roo.bootstrap.Navbar = function(config){
5203     Roo.bootstrap.Navbar.superclass.constructor.call(this, config);
5204     this.addEvents({
5205         // raw events
5206         /**
5207          * @event beforetoggle
5208          * Fire before toggle the menu
5209          * @param {Roo.EventObject} e
5210          */
5211         "beforetoggle" : true
5212     });
5213 };
5214
5215 Roo.extend(Roo.bootstrap.Navbar, Roo.bootstrap.Component,  {
5216     
5217     
5218    
5219     // private
5220     navItems : false,
5221     loadMask : false,
5222     
5223     
5224     getAutoCreate : function(){
5225         
5226         
5227         throw { message : "nav bar is now a abstract base class - use NavSimplebar / NavHeaderbar / NavSidebar etc..."};
5228         
5229     },
5230     
5231     initEvents :function ()
5232     {
5233         //Roo.log(this.el.select('.navbar-toggle',true));
5234         this.el.select('.navbar-toggle',true).on('click', this.onToggle , this);
5235         
5236         var mark = {
5237             tag: "div",
5238             cls:"x-dlg-mask"
5239         };
5240         
5241         this.maskEl = Roo.DomHelper.append(this.el, mark, true);
5242         
5243         var size = this.el.getSize();
5244         this.maskEl.setSize(size.width, size.height);
5245         this.maskEl.enableDisplayMode("block");
5246         this.maskEl.hide();
5247         
5248         if(this.loadMask){
5249             this.maskEl.show();
5250         }
5251     },
5252     
5253     
5254     getChildContainer : function()
5255     {
5256         if (this.el && this.el.select('.collapse').getCount()) {
5257             return this.el.select('.collapse',true).first();
5258         }
5259         
5260         return this.el;
5261     },
5262     
5263     mask : function()
5264     {
5265         this.maskEl.show();
5266     },
5267     
5268     unmask : function()
5269     {
5270         this.maskEl.hide();
5271     },
5272     onToggle : function()
5273     {
5274         
5275         if(this.fireEvent('beforetoggle', this) === false){
5276             return;
5277         }
5278         var ce = this.el.select('.navbar-collapse',true).first();
5279       
5280         if (!ce.hasClass('show')) {
5281            this.expand();
5282         } else {
5283             this.collapse();
5284         }
5285         
5286         
5287     
5288     },
5289     /**
5290      * Expand the navbar pulldown 
5291      */
5292     expand : function ()
5293     {
5294        
5295         var ce = this.el.select('.navbar-collapse',true).first();
5296         if (ce.hasClass('collapsing')) {
5297             return;
5298         }
5299         ce.dom.style.height = '';
5300                // show it...
5301         ce.addClass('in'); // old...
5302         ce.removeClass('collapse');
5303         ce.addClass('show');
5304         var h = ce.getHeight();
5305         Roo.log(h);
5306         ce.removeClass('show');
5307         // at this point we should be able to see it..
5308         ce.addClass('collapsing');
5309         
5310         ce.setHeight(0); // resize it ...
5311         ce.on('transitionend', function() {
5312             //Roo.log('done transition');
5313             ce.removeClass('collapsing');
5314             ce.addClass('show');
5315             ce.removeClass('collapse');
5316
5317             ce.dom.style.height = '';
5318         }, this, { single: true} );
5319         ce.setHeight(h);
5320         ce.dom.scrollTop = 0;
5321     },
5322     /**
5323      * Collapse the navbar pulldown 
5324      */
5325     collapse : function()
5326     {
5327          var ce = this.el.select('.navbar-collapse',true).first();
5328        
5329         if (ce.hasClass('collapsing') || ce.hasClass('collapse') ) {
5330             // it's collapsed or collapsing..
5331             return;
5332         }
5333         ce.removeClass('in'); // old...
5334         ce.setHeight(ce.getHeight());
5335         ce.removeClass('show');
5336         ce.addClass('collapsing');
5337         
5338         ce.on('transitionend', function() {
5339             ce.dom.style.height = '';
5340             ce.removeClass('collapsing');
5341             ce.addClass('collapse');
5342         }, this, { single: true} );
5343         ce.setHeight(0);
5344     }
5345     
5346     
5347     
5348 });
5349
5350
5351
5352  
5353
5354  /*
5355  * - LGPL
5356  *
5357  * navbar
5358  * 
5359  */
5360
5361 /**
5362  * @class Roo.bootstrap.NavSimplebar
5363  * @extends Roo.bootstrap.Navbar
5364  * Bootstrap Sidebar class
5365  *
5366  * @cfg {Boolean} inverse is inverted color
5367  * 
5368  * @cfg {String} type (nav | pills | tabs)
5369  * @cfg {Boolean} arrangement stacked | justified
5370  * @cfg {String} align (left | right) alignment
5371  * 
5372  * @cfg {Boolean} main (true|false) main nav bar? default false
5373  * @cfg {Boolean} loadMask (true|false) loadMask on the bar
5374  * 
5375  * @cfg {String} tag (header|footer|nav|div) default is nav 
5376
5377  * @cfg {String} weight (light|primary|secondary|success|danger|warning|info|dark|white) default is light.
5378  * 
5379  * 
5380  * @constructor
5381  * Create a new Sidebar
5382  * @param {Object} config The config object
5383  */
5384
5385
5386 Roo.bootstrap.NavSimplebar = function(config){
5387     Roo.bootstrap.NavSimplebar.superclass.constructor.call(this, config);
5388 };
5389
5390 Roo.extend(Roo.bootstrap.NavSimplebar, Roo.bootstrap.Navbar,  {
5391     
5392     inverse: false,
5393     
5394     type: false,
5395     arrangement: '',
5396     align : false,
5397     
5398     weight : 'light',
5399     
5400     main : false,
5401     
5402     
5403     tag : false,
5404     
5405     
5406     getAutoCreate : function(){
5407         
5408         
5409         var cfg = {
5410             tag : this.tag || 'div',
5411             cls : 'navbar roo-navbar-simple' //navbar-expand-lg ??
5412         };
5413         if (['light','white'].indexOf(this.weight) > -1) {
5414             cfg.cls += ['light','white'].indexOf(this.weight) > -1 ? ' navbar-light' : ' navbar-dark';
5415         }
5416         cfg.cls += ' bg-' + this.weight;
5417         
5418         if (this.inverse) {
5419             cfg.cls += ' navbar-inverse';
5420             
5421         }
5422         
5423         // i'm not actually sure these are really used - normally we add a navGroup to a navbar
5424         
5425         if (Roo.bootstrap.version == 4 && this.xtype == 'NavSimplebar') {
5426             return cfg;
5427         }
5428         
5429         
5430     
5431         
5432         cfg.cn = [
5433             {
5434                 cls: 'nav nav-' + this.xtype,
5435                 tag : 'ul'
5436             }
5437         ];
5438         
5439          
5440         this.type = this.type || 'nav';
5441         if (['tabs','pills'].indexOf(this.type) != -1) {
5442             cfg.cn[0].cls += ' nav-' + this.type
5443         
5444         
5445         } else {
5446             if (this.type!=='nav') {
5447                 Roo.log('nav type must be nav/tabs/pills')
5448             }
5449             cfg.cn[0].cls += ' navbar-nav'
5450         }
5451         
5452         
5453         
5454         
5455         if (['stacked','justified'].indexOf(this.arrangement) != -1) {
5456             cfg.cn[0].cls += ' nav-' + this.arrangement;
5457         }
5458         
5459         
5460         if (this.align === 'right') {
5461             cfg.cn[0].cls += ' navbar-right';
5462         }
5463         
5464         
5465         
5466         
5467         return cfg;
5468     
5469         
5470     }
5471     
5472     
5473     
5474 });
5475
5476
5477
5478  
5479
5480  
5481        /*
5482  * - LGPL
5483  *
5484  * navbar
5485  * navbar-fixed-top
5486  * navbar-expand-md  fixed-top 
5487  */
5488
5489 /**
5490  * @class Roo.bootstrap.NavHeaderbar
5491  * @extends Roo.bootstrap.NavSimplebar
5492  * Bootstrap Sidebar class
5493  *
5494  * @cfg {String} brand what is brand
5495  * @cfg {String} position (fixed-top|fixed-bottom|static-top) position
5496  * @cfg {String} brand_href href of the brand
5497  * @cfg {Boolean} srButton generate the (screen reader / mobile) sr-only button   default true
5498  * @cfg {Boolean} autohide a top nav bar header that hides on scroll.
5499  * @cfg {Boolean} desktopCenter should the header be centered on desktop using a container class
5500  * @cfg {Roo.bootstrap.Row} mobilerow - a row to display on mobile only..
5501  * 
5502  * @constructor
5503  * Create a new Sidebar
5504  * @param {Object} config The config object
5505  */
5506
5507
5508 Roo.bootstrap.NavHeaderbar = function(config){
5509     Roo.bootstrap.NavHeaderbar.superclass.constructor.call(this, config);
5510       
5511 };
5512
5513 Roo.extend(Roo.bootstrap.NavHeaderbar, Roo.bootstrap.NavSimplebar,  {
5514     
5515     position: '',
5516     brand: '',
5517     brand_href: false,
5518     srButton : true,
5519     autohide : false,
5520     desktopCenter : false,
5521    
5522     
5523     getAutoCreate : function(){
5524         
5525         var   cfg = {
5526             tag: this.nav || 'nav',
5527             cls: 'navbar navbar-expand-md',
5528             role: 'navigation',
5529             cn: []
5530         };
5531         
5532         var cn = cfg.cn;
5533         if (this.desktopCenter) {
5534             cn.push({cls : 'container', cn : []});
5535             cn = cn[0].cn;
5536         }
5537         
5538         if(this.srButton){
5539             var btn = {
5540                 tag: 'button',
5541                 type: 'button',
5542                 cls: 'navbar-toggle navbar-toggler',
5543                 'data-toggle': 'collapse',
5544                 cn: [
5545                     {
5546                         tag: 'span',
5547                         cls: 'sr-only',
5548                         html: 'Toggle navigation'
5549                     },
5550                     {
5551                         tag: 'span',
5552                         cls: 'icon-bar navbar-toggler-icon'
5553                     },
5554                     {
5555                         tag: 'span',
5556                         cls: 'icon-bar'
5557                     },
5558                     {
5559                         tag: 'span',
5560                         cls: 'icon-bar'
5561                     }
5562                 ]
5563             };
5564             
5565             cn.push( Roo.bootstrap.version == 4 ? btn : {
5566                 tag: 'div',
5567                 cls: 'navbar-header',
5568                 cn: [
5569                     btn
5570                 ]
5571             });
5572         }
5573         
5574         cn.push({
5575             tag: 'div',
5576             cls: Roo.bootstrap.version == 4  ? 'nav flex-row roo-navbar-collapse collapse navbar-collapse' : 'collapse navbar-collapse roo-navbar-collapse',
5577             cn : []
5578         });
5579         
5580         cfg.cls += this.inverse ? ' navbar-inverse navbar-dark bg-dark' : ' navbar-default';
5581         
5582         if (['light','white'].indexOf(this.weight) > -1) {
5583             cfg.cls += ['light','white'].indexOf(this.weight) > -1 ? ' navbar-light' : ' navbar-dark';
5584         }
5585         cfg.cls += ' bg-' + this.weight;
5586         
5587         
5588         if (['fixed-top','fixed-bottom','static-top'].indexOf(this.position)>-1) {
5589             cfg.cls += ' navbar-' + this.position + ' ' + this.position ;
5590             
5591             // tag can override this..
5592             
5593             cfg.tag = this.tag || (this.position  == 'fixed-bottom' ? 'footer' : 'header');
5594         }
5595         
5596         if (this.brand !== '') {
5597             var cp =  Roo.bootstrap.version == 4 ? cn : cn[0].cn;
5598             cp.unshift({ // changed from push ?? BS4 needs it at the start? - does this break or exsiting?
5599                 tag: 'a',
5600                 href: this.brand_href ? this.brand_href : '#',
5601                 cls: 'navbar-brand',
5602                 cn: [
5603                 this.brand
5604                 ]
5605             });
5606         }
5607         
5608         if(this.main){
5609             cfg.cls += ' main-nav';
5610         }
5611         
5612         
5613         return cfg;
5614
5615         
5616     },
5617     getHeaderChildContainer : function()
5618     {
5619         if (this.srButton && this.el.select('.navbar-header').getCount()) {
5620             return this.el.select('.navbar-header',true).first();
5621         }
5622         
5623         return this.getChildContainer();
5624     },
5625     
5626     getChildContainer : function()
5627     {
5628          
5629         return this.el.select('.roo-navbar-collapse',true).first();
5630          
5631         
5632     },
5633     
5634     initEvents : function()
5635     {
5636         Roo.bootstrap.NavHeaderbar.superclass.initEvents.call(this);
5637         
5638         if (this.autohide) {
5639             
5640             var prevScroll = 0;
5641             var ft = this.el;
5642             
5643             Roo.get(document).on('scroll',function(e) {
5644                 var ns = Roo.get(document).getScroll().top;
5645                 var os = prevScroll;
5646                 prevScroll = ns;
5647                 
5648                 if(ns > os){
5649                     ft.removeClass('slideDown');
5650                     ft.addClass('slideUp');
5651                     return;
5652                 }
5653                 ft.removeClass('slideUp');
5654                 ft.addClass('slideDown');
5655                  
5656               
5657           },this);
5658         }
5659     }    
5660     
5661 });
5662
5663
5664
5665  
5666
5667  /*
5668  * - LGPL
5669  *
5670  * navbar
5671  * 
5672  */
5673
5674 /**
5675  * @class Roo.bootstrap.NavSidebar
5676  * @extends Roo.bootstrap.Navbar
5677  * Bootstrap Sidebar class
5678  * 
5679  * @constructor
5680  * Create a new Sidebar
5681  * @param {Object} config The config object
5682  */
5683
5684
5685 Roo.bootstrap.NavSidebar = function(config){
5686     Roo.bootstrap.NavSidebar.superclass.constructor.call(this, config);
5687 };
5688
5689 Roo.extend(Roo.bootstrap.NavSidebar, Roo.bootstrap.Navbar,  {
5690     
5691     sidebar : true, // used by Navbar Item and NavbarGroup at present...
5692     
5693     getAutoCreate : function(){
5694         
5695         
5696         return  {
5697             tag: 'div',
5698             cls: 'sidebar sidebar-nav'
5699         };
5700     
5701         
5702     }
5703     
5704     
5705     
5706 });
5707
5708
5709
5710  
5711
5712  /*
5713  * - LGPL
5714  *
5715  * nav group
5716  * 
5717  */
5718
5719 /**
5720  * @class Roo.bootstrap.NavGroup
5721  * @extends Roo.bootstrap.Component
5722  * Bootstrap NavGroup class
5723  * @cfg {String} align (left|right)
5724  * @cfg {Boolean} inverse
5725  * @cfg {String} type (nav|pills|tab) default nav
5726  * @cfg {String} navId - reference Id for navbar.
5727
5728  * 
5729  * @constructor
5730  * Create a new nav group
5731  * @param {Object} config The config object
5732  */
5733
5734 Roo.bootstrap.NavGroup = function(config){
5735     Roo.bootstrap.NavGroup.superclass.constructor.call(this, config);
5736     this.navItems = [];
5737    
5738     Roo.bootstrap.NavGroup.register(this);
5739      this.addEvents({
5740         /**
5741              * @event changed
5742              * Fires when the active item changes
5743              * @param {Roo.bootstrap.NavGroup} this
5744              * @param {Roo.bootstrap.Navbar.Item} selected The item selected
5745              * @param {Roo.bootstrap.Navbar.Item} prev The previously selected item 
5746          */
5747         'changed': true
5748      });
5749     
5750 };
5751
5752 Roo.extend(Roo.bootstrap.NavGroup, Roo.bootstrap.Component,  {
5753     
5754     align: '',
5755     inverse: false,
5756     form: false,
5757     type: 'nav',
5758     navId : '',
5759     // private
5760     
5761     navItems : false, 
5762     
5763     getAutoCreate : function()
5764     {
5765         var cfg = Roo.apply({}, Roo.bootstrap.NavGroup.superclass.getAutoCreate.call(this));
5766         
5767         cfg = {
5768             tag : 'ul',
5769             cls: 'nav' 
5770         };
5771         if (Roo.bootstrap.version == 4) {
5772             if (['tabs','pills'].indexOf(this.type) != -1) {
5773                 cfg.cls += ' nav-' + this.type; 
5774             } else {
5775                 // trying to remove so header bar can right align top?
5776                 if (this.parent() && this.parent().xtype != 'NavHeaderbar') {
5777                     // do not use on header bar... 
5778                     cfg.cls += ' navbar-nav';
5779                 }
5780             }
5781             
5782         } else {
5783             if (['tabs','pills'].indexOf(this.type) != -1) {
5784                 cfg.cls += ' nav-' + this.type
5785             } else {
5786                 if (this.type !== 'nav') {
5787                     Roo.log('nav type must be nav/tabs/pills')
5788                 }
5789                 cfg.cls += ' navbar-nav'
5790             }
5791         }
5792         
5793         if (this.parent() && this.parent().sidebar) {
5794             cfg = {
5795                 tag: 'ul',
5796                 cls: 'dashboard-menu sidebar-menu'
5797             };
5798             
5799             return cfg;
5800         }
5801         
5802         if (this.form === true) {
5803             cfg = {
5804                 tag: 'form',
5805                 cls: 'navbar-form form-inline'
5806             };
5807             //nav navbar-right ml-md-auto
5808             if (this.align === 'right') {
5809                 cfg.cls += ' navbar-right ml-md-auto';
5810             } else {
5811                 cfg.cls += ' navbar-left';
5812             }
5813         }
5814         
5815         if (this.align === 'right') {
5816             cfg.cls += ' navbar-right ml-md-auto';
5817         } else {
5818             cfg.cls += ' mr-auto';
5819         }
5820         
5821         if (this.inverse) {
5822             cfg.cls += ' navbar-inverse';
5823             
5824         }
5825         
5826         
5827         return cfg;
5828     },
5829     /**
5830     * sets the active Navigation item
5831     * @param {Roo.bootstrap.NavItem} the new current navitem
5832     */
5833     setActiveItem : function(item)
5834     {
5835         var prev = false;
5836         Roo.each(this.navItems, function(v){
5837             if (v == item) {
5838                 return ;
5839             }
5840             if (v.isActive()) {
5841                 v.setActive(false, true);
5842                 prev = v;
5843                 
5844             }
5845             
5846         });
5847
5848         item.setActive(true, true);
5849         this.fireEvent('changed', this, item, prev);
5850         
5851         
5852     },
5853     /**
5854     * gets the active Navigation item
5855     * @return {Roo.bootstrap.NavItem} the current navitem
5856     */
5857     getActive : function()
5858     {
5859         
5860         var prev = false;
5861         Roo.each(this.navItems, function(v){
5862             
5863             if (v.isActive()) {
5864                 prev = v;
5865                 
5866             }
5867             
5868         });
5869         return prev;
5870     },
5871     
5872     indexOfNav : function()
5873     {
5874         
5875         var prev = false;
5876         Roo.each(this.navItems, function(v,i){
5877             
5878             if (v.isActive()) {
5879                 prev = i;
5880                 
5881             }
5882             
5883         });
5884         return prev;
5885     },
5886     /**
5887     * adds a Navigation item
5888     * @param {Roo.bootstrap.NavItem} the navitem to add
5889     */
5890     addItem : function(cfg)
5891     {
5892         if (this.form && Roo.bootstrap.version == 4) {
5893             cfg.tag = 'div';
5894         }
5895         var cn = new Roo.bootstrap.NavItem(cfg);
5896         this.register(cn);
5897         cn.parentId = this.id;
5898         cn.onRender(this.el, null);
5899         return cn;
5900     },
5901     /**
5902     * register a Navigation item
5903     * @param {Roo.bootstrap.NavItem} the navitem to add
5904     */
5905     register : function(item)
5906     {
5907         this.navItems.push( item);
5908         item.navId = this.navId;
5909     
5910     },
5911     
5912     /**
5913     * clear all the Navigation item
5914     */
5915    
5916     clearAll : function()
5917     {
5918         this.navItems = [];
5919         this.el.dom.innerHTML = '';
5920     },
5921     
5922     getNavItem: function(tabId)
5923     {
5924         var ret = false;
5925         Roo.each(this.navItems, function(e) {
5926             if (e.tabId == tabId) {
5927                ret =  e;
5928                return false;
5929             }
5930             return true;
5931             
5932         });
5933         return ret;
5934     },
5935     
5936     setActiveNext : function()
5937     {
5938         var i = this.indexOfNav(this.getActive());
5939         if (i > this.navItems.length) {
5940             return;
5941         }
5942         this.setActiveItem(this.navItems[i+1]);
5943     },
5944     setActivePrev : function()
5945     {
5946         var i = this.indexOfNav(this.getActive());
5947         if (i  < 1) {
5948             return;
5949         }
5950         this.setActiveItem(this.navItems[i-1]);
5951     },
5952     clearWasActive : function(except) {
5953         Roo.each(this.navItems, function(e) {
5954             if (e.tabId != except.tabId && e.was_active) {
5955                e.was_active = false;
5956                return false;
5957             }
5958             return true;
5959             
5960         });
5961     },
5962     getWasActive : function ()
5963     {
5964         var r = false;
5965         Roo.each(this.navItems, function(e) {
5966             if (e.was_active) {
5967                r = e;
5968                return false;
5969             }
5970             return true;
5971             
5972         });
5973         return r;
5974     }
5975     
5976     
5977 });
5978
5979  
5980 Roo.apply(Roo.bootstrap.NavGroup, {
5981     
5982     groups: {},
5983      /**
5984     * register a Navigation Group
5985     * @param {Roo.bootstrap.NavGroup} the navgroup to add
5986     */
5987     register : function(navgrp)
5988     {
5989         this.groups[navgrp.navId] = navgrp;
5990         
5991     },
5992     /**
5993     * fetch a Navigation Group based on the navigation ID
5994     * @param {string} the navgroup to add
5995     * @returns {Roo.bootstrap.NavGroup} the navgroup 
5996     */
5997     get: function(navId) {
5998         if (typeof(this.groups[navId]) == 'undefined') {
5999             return false;
6000             //this.register(new Roo.bootstrap.NavGroup({ navId : navId }));
6001         }
6002         return this.groups[navId] ;
6003     }
6004     
6005     
6006     
6007 });
6008
6009  /*
6010  * - LGPL
6011  *
6012  * row
6013  * 
6014  */
6015
6016 /**
6017  * @class Roo.bootstrap.NavItem
6018  * @extends Roo.bootstrap.Component
6019  * Bootstrap Navbar.NavItem class
6020  * @cfg {String} href  link to
6021  * @cfg {String} button_weight (default | primary | secondary | success | info | warning | danger | link ) default none
6022
6023  * @cfg {String} html content of button
6024  * @cfg {String} badge text inside badge
6025  * @cfg {String} badgecls (bg-green|bg-red|bg-yellow)the extra classes for the badge
6026  * @cfg {String} glyphicon DEPRICATED - use fa
6027  * @cfg {String} icon DEPRICATED - use fa
6028  * @cfg {String} fa - Fontawsome icon name (can add stuff to it like fa-2x)
6029  * @cfg {Boolean} active Is item active
6030  * @cfg {Boolean} disabled Is item disabled
6031  
6032  * @cfg {Boolean} preventDefault (true | false) default false
6033  * @cfg {String} tabId the tab that this item activates.
6034  * @cfg {String} tagtype (a|span) render as a href or span?
6035  * @cfg {Boolean} animateRef (true|false) link to element default false  
6036   
6037  * @constructor
6038  * Create a new Navbar Item
6039  * @param {Object} config The config object
6040  */
6041 Roo.bootstrap.NavItem = function(config){
6042     Roo.bootstrap.NavItem.superclass.constructor.call(this, config);
6043     this.addEvents({
6044         // raw events
6045         /**
6046          * @event click
6047          * The raw click event for the entire grid.
6048          * @param {Roo.EventObject} e
6049          */
6050         "click" : true,
6051          /**
6052             * @event changed
6053             * Fires when the active item active state changes
6054             * @param {Roo.bootstrap.NavItem} this
6055             * @param {boolean} state the new state
6056              
6057          */
6058         'changed': true,
6059         /**
6060             * @event scrollto
6061             * Fires when scroll to element
6062             * @param {Roo.bootstrap.NavItem} this
6063             * @param {Object} options
6064             * @param {Roo.EventObject} e
6065              
6066          */
6067         'scrollto': true
6068     });
6069    
6070 };
6071
6072 Roo.extend(Roo.bootstrap.NavItem, Roo.bootstrap.Component,  {
6073     
6074     href: false,
6075     html: '',
6076     badge: '',
6077     icon: false,
6078     fa : false,
6079     glyphicon: false,
6080     active: false,
6081     preventDefault : false,
6082     tabId : false,
6083     tagtype : 'a',
6084     tag: 'li',
6085     disabled : false,
6086     animateRef : false,
6087     was_active : false,
6088     button_weight : '',
6089     button_outline : false,
6090     
6091     navLink: false,
6092     
6093     getAutoCreate : function(){
6094          
6095         var cfg = {
6096             tag: this.tag,
6097             cls: 'nav-item'
6098         };
6099         
6100         if (this.active) {
6101             cfg.cls = typeof(cfg.cls) == 'undefined' ? 'active' : cfg.cls + ' active';
6102         }
6103         if (this.disabled) {
6104             cfg.cls += ' disabled';
6105         }
6106         
6107         // BS4 only?
6108         if (this.button_weight.length) {
6109             cfg.tag = this.href ? 'a' : 'button';
6110             cfg.html = this.html || '';
6111             cfg.cls += ' btn btn' + (this.button_outline ? '-outline' : '') + '-' + this.button_weight;
6112             if (this.href) {
6113                 cfg.href = this.href;
6114             }
6115             if (this.fa) {
6116                 cfg.html = '<i class="fa fas fa-'+this.fa+'"></i> <span>' + this.html + '</span>';
6117             }
6118             
6119             // menu .. should add dropdown-menu class - so no need for carat..
6120             
6121             if (this.badge !== '') {
6122                  
6123                 cfg.html += ' <span class="badge badge-secondary">' + this.badge + '</span>';
6124             }
6125             return cfg;
6126         }
6127         
6128         if (this.href || this.html || this.glyphicon || this.icon || this.fa) {
6129             cfg.cn = [
6130                 {
6131                     tag: this.tagtype,
6132                     href : this.href || "#",
6133                     html: this.html || ''
6134                 }
6135             ];
6136             if (this.tagtype == 'a') {
6137                 cfg.cn[0].cls = 'nav-link';
6138             }
6139             if (this.icon) {
6140                 cfg.cn[0].html = '<i class="'+this.icon+'"></i> <span>' + cfg.cn[0].html + '</span>';
6141             }
6142             if (this.fa) {
6143                 cfg.cn[0].html = '<i class="fa fas fa-'+this.fa+'"></i> <span>' + cfg.cn[0].html + '</span>';
6144             }
6145             if(this.glyphicon) {
6146                 cfg.cn[0].html = '<span class="glyphicon glyphicon-' + this.glyphicon + '"></span> '  + cfg.cn[0].html;
6147             }
6148             
6149             if (this.menu) {
6150                 
6151                 cfg.cn[0].html += " <span class='caret'></span>";
6152              
6153             }
6154             
6155             if (this.badge !== '') {
6156                  
6157                 cfg.cn[0].html += ' <span class="badge badge-secondary">' + this.badge + '</span>';
6158             }
6159         }
6160         
6161         
6162         
6163         return cfg;
6164     },
6165     onRender : function(ct, position)
6166     {
6167        // Roo.log("Call onRender: " + this.xtype);
6168         if (Roo.bootstrap.version == 4 && ct.dom.type != 'ul') {
6169             this.tag = 'div';
6170         }
6171         
6172         var ret = Roo.bootstrap.NavItem.superclass.onRender.call(this, ct, position);
6173         this.navLink = this.el.select('.nav-link',true).first();
6174         return ret;
6175     },
6176       
6177     
6178     initEvents: function() 
6179     {
6180         if (typeof (this.menu) != 'undefined') {
6181             this.menu.parentType = this.xtype;
6182             this.menu.triggerEl = this.el;
6183             this.menu = this.addxtype(Roo.apply({}, this.menu));
6184         }
6185         
6186         this.el.select('a',true).on('click', this.onClick, this);
6187         
6188         if(this.tagtype == 'span'){
6189             this.el.select('span',true).on('click', this.onClick, this);
6190         }
6191        
6192         // at this point parent should be available..
6193         this.parent().register(this);
6194     },
6195     
6196     onClick : function(e)
6197     {
6198         if (e.getTarget('.dropdown-menu-item')) {
6199             // did you click on a menu itemm.... - then don't trigger onclick..
6200             return;
6201         }
6202         
6203         if(
6204                 this.preventDefault || 
6205                 this.href == '#' 
6206         ){
6207             Roo.log("NavItem - prevent Default?");
6208             e.preventDefault();
6209         }
6210         
6211         if (this.disabled) {
6212             return;
6213         }
6214         
6215         var tg = Roo.bootstrap.TabGroup.get(this.navId);
6216         if (tg && tg.transition) {
6217             Roo.log("waiting for the transitionend");
6218             return;
6219         }
6220         
6221         
6222         
6223         //Roo.log("fire event clicked");
6224         if(this.fireEvent('click', this, e) === false){
6225             return;
6226         };
6227         
6228         if(this.tagtype == 'span'){
6229             return;
6230         }
6231         
6232         //Roo.log(this.href);
6233         var ael = this.el.select('a',true).first();
6234         //Roo.log(ael);
6235         
6236         if(ael && this.animateRef && this.href.indexOf('#') > -1){
6237             //Roo.log(["test:",ael.dom.href.split("#")[0], document.location.toString().split("#")[0]]);
6238             if (ael.dom.href.split("#")[0] != document.location.toString().split("#")[0]) {
6239                 return; // ignore... - it's a 'hash' to another page.
6240             }
6241             Roo.log("NavItem - prevent Default?");
6242             e.preventDefault();
6243             this.scrollToElement(e);
6244         }
6245         
6246         
6247         var p =  this.parent();
6248    
6249         if (['tabs','pills'].indexOf(p.type)!==-1) {
6250             if (typeof(p.setActiveItem) !== 'undefined') {
6251                 p.setActiveItem(this);
6252             }
6253         }
6254         
6255         // if parent is a navbarheader....- and link is probably a '#' page ref.. then remove the expanded menu.
6256         if (p.parentType == 'NavHeaderbar' && !this.menu) {
6257             // remove the collapsed menu expand...
6258             p.parent().el.select('.roo-navbar-collapse',true).removeClass('in');  
6259         }
6260     },
6261     
6262     isActive: function () {
6263         return this.active
6264     },
6265     setActive : function(state, fire, is_was_active)
6266     {
6267         if (this.active && !state && this.navId) {
6268             this.was_active = true;
6269             var nv = Roo.bootstrap.NavGroup.get(this.navId);
6270             if (nv) {
6271                 nv.clearWasActive(this);
6272             }
6273             
6274         }
6275         this.active = state;
6276         
6277         if (!state ) {
6278             this.el.removeClass('active');
6279             this.navLink ? this.navLink.removeClass('active') : false;
6280         } else if (!this.el.hasClass('active')) {
6281             
6282             this.el.addClass('active');
6283             if (Roo.bootstrap.version == 4 && this.navLink ) {
6284                 this.navLink.addClass('active');
6285             }
6286             
6287         }
6288         if (fire) {
6289             this.fireEvent('changed', this, state);
6290         }
6291         
6292         // show a panel if it's registered and related..
6293         
6294         if (!this.navId || !this.tabId || !state || is_was_active) {
6295             return;
6296         }
6297         
6298         var tg = Roo.bootstrap.TabGroup.get(this.navId);
6299         if (!tg) {
6300             return;
6301         }
6302         var pan = tg.getPanelByName(this.tabId);
6303         if (!pan) {
6304             return;
6305         }
6306         // if we can not flip to new panel - go back to old nav highlight..
6307         if (false == tg.showPanel(pan)) {
6308             var nv = Roo.bootstrap.NavGroup.get(this.navId);
6309             if (nv) {
6310                 var onav = nv.getWasActive();
6311                 if (onav) {
6312                     onav.setActive(true, false, true);
6313                 }
6314             }
6315             
6316         }
6317         
6318         
6319         
6320     },
6321      // this should not be here...
6322     setDisabled : function(state)
6323     {
6324         this.disabled = state;
6325         if (!state ) {
6326             this.el.removeClass('disabled');
6327         } else if (!this.el.hasClass('disabled')) {
6328             this.el.addClass('disabled');
6329         }
6330         
6331     },
6332     
6333     /**
6334      * Fetch the element to display the tooltip on.
6335      * @return {Roo.Element} defaults to this.el
6336      */
6337     tooltipEl : function()
6338     {
6339         return this.el.select('' + this.tagtype + '', true).first();
6340     },
6341     
6342     scrollToElement : function(e)
6343     {
6344         var c = document.body;
6345         
6346         /*
6347          * Firefox / IE places the overflow at the html level, unless specifically styled to behave differently.
6348          */
6349         if(Roo.isFirefox || Roo.isIE || Roo.isIE11){
6350             c = document.documentElement;
6351         }
6352         
6353         var target = Roo.get(c).select('a[name=' + this.href.split('#')[1] +']', true).first();
6354         
6355         if(!target){
6356             return;
6357         }
6358
6359         var o = target.calcOffsetsTo(c);
6360         
6361         var options = {
6362             target : target,
6363             value : o[1]
6364         };
6365         
6366         this.fireEvent('scrollto', this, options, e);
6367         
6368         Roo.get(c).scrollTo('top', options.value, true);
6369         
6370         return;
6371     }
6372 });
6373  
6374
6375  /*
6376  * - LGPL
6377  *
6378  * sidebar item
6379  *
6380  *  li
6381  *    <span> icon </span>
6382  *    <span> text </span>
6383  *    <span>badge </span>
6384  */
6385
6386 /**
6387  * @class Roo.bootstrap.NavSidebarItem
6388  * @extends Roo.bootstrap.NavItem
6389  * Bootstrap Navbar.NavSidebarItem class
6390  * {String} badgeWeight (default|primary|success|info|warning|danger)the extra classes for the badge
6391  * {Boolean} open is the menu open
6392  * {Boolean} buttonView use button as the tigger el rather that a (default false)
6393  * {String} buttonWeight (default|primary|success|info|warning|danger)the extra classes for the button
6394  * {String} buttonSize (sm|md|lg)the extra classes for the button
6395  * {Boolean} showArrow show arrow next to the text (default true)
6396  * @constructor
6397  * Create a new Navbar Button
6398  * @param {Object} config The config object
6399  */
6400 Roo.bootstrap.NavSidebarItem = function(config){
6401     Roo.bootstrap.NavSidebarItem.superclass.constructor.call(this, config);
6402     this.addEvents({
6403         // raw events
6404         /**
6405          * @event click
6406          * The raw click event for the entire grid.
6407          * @param {Roo.EventObject} e
6408          */
6409         "click" : true,
6410          /**
6411             * @event changed
6412             * Fires when the active item active state changes
6413             * @param {Roo.bootstrap.NavSidebarItem} this
6414             * @param {boolean} state the new state
6415              
6416          */
6417         'changed': true
6418     });
6419    
6420 };
6421
6422 Roo.extend(Roo.bootstrap.NavSidebarItem, Roo.bootstrap.NavItem,  {
6423     
6424     badgeWeight : 'default',
6425     
6426     open: false,
6427     
6428     buttonView : false,
6429     
6430     buttonWeight : 'default',
6431     
6432     buttonSize : 'md',
6433     
6434     showArrow : true,
6435     
6436     getAutoCreate : function(){
6437         
6438         
6439         var a = {
6440                 tag: 'a',
6441                 href : this.href || '#',
6442                 cls: '',
6443                 html : '',
6444                 cn : []
6445         };
6446         
6447         if(this.buttonView){
6448             a = {
6449                 tag: 'button',
6450                 href : this.href || '#',
6451                 cls: 'btn btn-' + this.buttonWeight + ' btn-' + this.buttonSize + 'roo-button-dropdown-toggle',
6452                 html : this.html,
6453                 cn : []
6454             };
6455         }
6456         
6457         var cfg = {
6458             tag: 'li',
6459             cls: '',
6460             cn: [ a ]
6461         };
6462         
6463         if (this.active) {
6464             cfg.cls += ' active';
6465         }
6466         
6467         if (this.disabled) {
6468             cfg.cls += ' disabled';
6469         }
6470         if (this.open) {
6471             cfg.cls += ' open x-open';
6472         }
6473         // left icon..
6474         if (this.glyphicon || this.icon) {
6475             var c = this.glyphicon  ? ('glyphicon glyphicon-'+this.glyphicon)  : this.icon;
6476             a.cn.push({ tag : 'i', cls : c }) ;
6477         }
6478         
6479         if(!this.buttonView){
6480             var span = {
6481                 tag: 'span',
6482                 html : this.html || ''
6483             };
6484
6485             a.cn.push(span);
6486             
6487         }
6488         
6489         if (this.badge !== '') {
6490             a.cn.push({ tag: 'span',  cls : 'badge pull-right badge-' + this.badgeWeight, html: this.badge }); 
6491         }
6492         
6493         if (this.menu) {
6494             
6495             if(this.showArrow){
6496                 a.cn.push({ tag : 'i', cls : 'glyphicon glyphicon-chevron-down pull-right'});
6497             }
6498             
6499             a.cls += ' dropdown-toggle treeview' ;
6500         }
6501         
6502         return cfg;
6503     },
6504     
6505     initEvents : function()
6506     { 
6507         if (typeof (this.menu) != 'undefined') {
6508             this.menu.parentType = this.xtype;
6509             this.menu.triggerEl = this.el;
6510             this.menu = this.addxtype(Roo.apply({}, this.menu));
6511         }
6512         
6513         this.el.on('click', this.onClick, this);
6514         
6515         if(this.badge !== ''){
6516             this.badgeEl = this.el.select('.badge', true).first().setVisibilityMode(Roo.Element.DISPLAY);
6517         }
6518         
6519     },
6520     
6521     onClick : function(e)
6522     {
6523         if(this.disabled){
6524             e.preventDefault();
6525             return;
6526         }
6527         
6528         if(this.preventDefault){
6529             e.preventDefault();
6530         }
6531         
6532         this.fireEvent('click', this, e);
6533     },
6534     
6535     disable : function()
6536     {
6537         this.setDisabled(true);
6538     },
6539     
6540     enable : function()
6541     {
6542         this.setDisabled(false);
6543     },
6544     
6545     setDisabled : function(state)
6546     {
6547         if(this.disabled == state){
6548             return;
6549         }
6550         
6551         this.disabled = state;
6552         
6553         if (state) {
6554             this.el.addClass('disabled');
6555             return;
6556         }
6557         
6558         this.el.removeClass('disabled');
6559         
6560         return;
6561     },
6562     
6563     setActive : function(state)
6564     {
6565         if(this.active == state){
6566             return;
6567         }
6568         
6569         this.active = state;
6570         
6571         if (state) {
6572             this.el.addClass('active');
6573             return;
6574         }
6575         
6576         this.el.removeClass('active');
6577         
6578         return;
6579     },
6580     
6581     isActive: function () 
6582     {
6583         return this.active;
6584     },
6585     
6586     setBadge : function(str)
6587     {
6588         if(!this.badgeEl){
6589             return;
6590         }
6591         
6592         this.badgeEl.dom.innerHTML = str;
6593     }
6594     
6595    
6596      
6597  
6598 });
6599  
6600
6601  /*
6602  * - LGPL
6603  *
6604  * row
6605  * 
6606  */
6607
6608 /**
6609  * @class Roo.bootstrap.Row
6610  * @extends Roo.bootstrap.Component
6611  * Bootstrap Row class (contains columns...)
6612  * 
6613  * @constructor
6614  * Create a new Row
6615  * @param {Object} config The config object
6616  */
6617
6618 Roo.bootstrap.Row = function(config){
6619     Roo.bootstrap.Row.superclass.constructor.call(this, config);
6620 };
6621
6622 Roo.extend(Roo.bootstrap.Row, Roo.bootstrap.Component,  {
6623     
6624     getAutoCreate : function(){
6625        return {
6626             cls: 'row clearfix'
6627        };
6628     }
6629     
6630     
6631 });
6632
6633  
6634
6635  /*
6636  * - LGPL
6637  *
6638  * pagination
6639  * 
6640  */
6641
6642 /**
6643  * @class Roo.bootstrap.Pagination
6644  * @extends Roo.bootstrap.Component
6645  * Bootstrap Pagination class
6646  * @cfg {String} size xs | sm | md | lg
6647  * @cfg {Boolean} inverse false | true
6648  * 
6649  * @constructor
6650  * Create a new Pagination
6651  * @param {Object} config The config object
6652  */
6653
6654 Roo.bootstrap.Pagination = function(config){
6655     Roo.bootstrap.Pagination.superclass.constructor.call(this, config);
6656 };
6657
6658 Roo.extend(Roo.bootstrap.Pagination, Roo.bootstrap.Component,  {
6659     
6660     cls: false,
6661     size: false,
6662     inverse: false,
6663     
6664     getAutoCreate : function(){
6665         var cfg = {
6666             tag: 'ul',
6667                 cls: 'pagination'
6668         };
6669         if (this.inverse) {
6670             cfg.cls += ' inverse';
6671         }
6672         if (this.html) {
6673             cfg.html=this.html;
6674         }
6675         if (this.cls) {
6676             cfg.cls += " " + this.cls;
6677         }
6678         return cfg;
6679     }
6680    
6681 });
6682
6683  
6684
6685  /*
6686  * - LGPL
6687  *
6688  * Pagination item
6689  * 
6690  */
6691
6692
6693 /**
6694  * @class Roo.bootstrap.PaginationItem
6695  * @extends Roo.bootstrap.Component
6696  * Bootstrap PaginationItem class
6697  * @cfg {String} html text
6698  * @cfg {String} href the link
6699  * @cfg {Boolean} preventDefault (true | false) default true
6700  * @cfg {Boolean} active (true | false) default false
6701  * @cfg {Boolean} disabled default false
6702  * 
6703  * 
6704  * @constructor
6705  * Create a new PaginationItem
6706  * @param {Object} config The config object
6707  */
6708
6709
6710 Roo.bootstrap.PaginationItem = function(config){
6711     Roo.bootstrap.PaginationItem.superclass.constructor.call(this, config);
6712     this.addEvents({
6713         // raw events
6714         /**
6715          * @event click
6716          * The raw click event for the entire grid.
6717          * @param {Roo.EventObject} e
6718          */
6719         "click" : true
6720     });
6721 };
6722
6723 Roo.extend(Roo.bootstrap.PaginationItem, Roo.bootstrap.Component,  {
6724     
6725     href : false,
6726     html : false,
6727     preventDefault: true,
6728     active : false,
6729     cls : false,
6730     disabled: false,
6731     
6732     getAutoCreate : function(){
6733         var cfg= {
6734             tag: 'li',
6735             cn: [
6736                 {
6737                     tag : 'a',
6738                     href : this.href ? this.href : '#',
6739                     html : this.html ? this.html : ''
6740                 }
6741             ]
6742         };
6743         
6744         if(this.cls){
6745             cfg.cls = this.cls;
6746         }
6747         
6748         if(this.disabled){
6749             cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' disabled' : 'disabled';
6750         }
6751         
6752         if(this.active){
6753             cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' active' : 'active';
6754         }
6755         
6756         return cfg;
6757     },
6758     
6759     initEvents: function() {
6760         
6761         this.el.on('click', this.onClick, this);
6762         
6763     },
6764     onClick : function(e)
6765     {
6766         Roo.log('PaginationItem on click ');
6767         if(this.preventDefault){
6768             e.preventDefault();
6769         }
6770         
6771         if(this.disabled){
6772             return;
6773         }
6774         
6775         this.fireEvent('click', this, e);
6776     }
6777    
6778 });
6779
6780  
6781
6782  /*
6783  * - LGPL
6784  *
6785  * slider
6786  * 
6787  */
6788
6789
6790 /**
6791  * @class Roo.bootstrap.Slider
6792  * @extends Roo.bootstrap.Component
6793  * Bootstrap Slider class
6794  *    
6795  * @constructor
6796  * Create a new Slider
6797  * @param {Object} config The config object
6798  */
6799
6800 Roo.bootstrap.Slider = function(config){
6801     Roo.bootstrap.Slider.superclass.constructor.call(this, config);
6802 };
6803
6804 Roo.extend(Roo.bootstrap.Slider, Roo.bootstrap.Component,  {
6805     
6806     getAutoCreate : function(){
6807         
6808         var cfg = {
6809             tag: 'div',
6810             cls: 'slider slider-sample1 vertical-handler ui-slider ui-slider-horizontal ui-widget ui-widget-content ui-corner-all',
6811             cn: [
6812                 {
6813                     tag: 'a',
6814                     cls: 'ui-slider-handle ui-state-default ui-corner-all'
6815                 }
6816             ]
6817         };
6818         
6819         return cfg;
6820     }
6821    
6822 });
6823
6824  /*
6825  * Based on:
6826  * Ext JS Library 1.1.1
6827  * Copyright(c) 2006-2007, Ext JS, LLC.
6828  *
6829  * Originally Released Under LGPL - original licence link has changed is not relivant.
6830  *
6831  * Fork - LGPL
6832  * <script type="text/javascript">
6833  */
6834  
6835
6836 /**
6837  * @class Roo.grid.ColumnModel
6838  * @extends Roo.util.Observable
6839  * This is the default implementation of a ColumnModel used by the Grid. It defines
6840  * the columns in the grid.
6841  * <br>Usage:<br>
6842  <pre><code>
6843  var colModel = new Roo.grid.ColumnModel([
6844         {header: "Ticker", width: 60, sortable: true, locked: true},
6845         {header: "Company Name", width: 150, sortable: true},
6846         {header: "Market Cap.", width: 100, sortable: true},
6847         {header: "$ Sales", width: 100, sortable: true, renderer: money},
6848         {header: "Employees", width: 100, sortable: true, resizable: false}
6849  ]);
6850  </code></pre>
6851  * <p>
6852  
6853  * The config options listed for this class are options which may appear in each
6854  * individual column definition.
6855  * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
6856  * @constructor
6857  * @param {Object} config An Array of column config objects. See this class's
6858  * config objects for details.
6859 */
6860 Roo.grid.ColumnModel = function(config){
6861         /**
6862      * The config passed into the constructor
6863      */
6864     this.config = config;
6865     this.lookup = {};
6866
6867     // if no id, create one
6868     // if the column does not have a dataIndex mapping,
6869     // map it to the order it is in the config
6870     for(var i = 0, len = config.length; i < len; i++){
6871         var c = config[i];
6872         if(typeof c.dataIndex == "undefined"){
6873             c.dataIndex = i;
6874         }
6875         if(typeof c.renderer == "string"){
6876             c.renderer = Roo.util.Format[c.renderer];
6877         }
6878         if(typeof c.id == "undefined"){
6879             c.id = Roo.id();
6880         }
6881         if(c.editor && c.editor.xtype){
6882             c.editor  = Roo.factory(c.editor, Roo.grid);
6883         }
6884         if(c.editor && c.editor.isFormField){
6885             c.editor = new Roo.grid.GridEditor(c.editor);
6886         }
6887         this.lookup[c.id] = c;
6888     }
6889
6890     /**
6891      * The width of columns which have no width specified (defaults to 100)
6892      * @type Number
6893      */
6894     this.defaultWidth = 100;
6895
6896     /**
6897      * Default sortable of columns which have no sortable specified (defaults to false)
6898      * @type Boolean
6899      */
6900     this.defaultSortable = false;
6901
6902     this.addEvents({
6903         /**
6904              * @event widthchange
6905              * Fires when the width of a column changes.
6906              * @param {ColumnModel} this
6907              * @param {Number} columnIndex The column index
6908              * @param {Number} newWidth The new width
6909              */
6910             "widthchange": true,
6911         /**
6912              * @event headerchange
6913              * Fires when the text of a header changes.
6914              * @param {ColumnModel} this
6915              * @param {Number} columnIndex The column index
6916              * @param {Number} newText The new header text
6917              */
6918             "headerchange": true,
6919         /**
6920              * @event hiddenchange
6921              * Fires when a column is hidden or "unhidden".
6922              * @param {ColumnModel} this
6923              * @param {Number} columnIndex The column index
6924              * @param {Boolean} hidden true if hidden, false otherwise
6925              */
6926             "hiddenchange": true,
6927             /**
6928          * @event columnmoved
6929          * Fires when a column is moved.
6930          * @param {ColumnModel} this
6931          * @param {Number} oldIndex
6932          * @param {Number} newIndex
6933          */
6934         "columnmoved" : true,
6935         /**
6936          * @event columlockchange
6937          * Fires when a column's locked state is changed
6938          * @param {ColumnModel} this
6939          * @param {Number} colIndex
6940          * @param {Boolean} locked true if locked
6941          */
6942         "columnlockchange" : true
6943     });
6944     Roo.grid.ColumnModel.superclass.constructor.call(this);
6945 };
6946 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
6947     /**
6948      * @cfg {String} header The header text to display in the Grid view.
6949      */
6950     /**
6951      * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
6952      * {@link Roo.data.Record} definition from which to draw the column's value. If not
6953      * specified, the column's index is used as an index into the Record's data Array.
6954      */
6955     /**
6956      * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
6957      * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
6958      */
6959     /**
6960      * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
6961      * Defaults to the value of the {@link #defaultSortable} property.
6962      * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
6963      */
6964     /**
6965      * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid.  Defaults to false.
6966      */
6967     /**
6968      * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed.  Defaults to false.
6969      */
6970     /**
6971      * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
6972      */
6973     /**
6974      * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
6975      */
6976     /**
6977      * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
6978      * given the cell's data value. See {@link #setRenderer}. If not specified, the
6979      * default renderer returns the escaped data value. If an object is returned (bootstrap only)
6980      * then it is treated as a Roo Component object instance, and it is rendered after the initial row is rendered
6981      */
6982        /**
6983      * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor 
6984      */
6985     /**
6986      * @cfg {String} align (Optional) Set the CSS text-align property of the column.  Defaults to undefined.
6987      */
6988     /**
6989      * @cfg {String} valign (Optional) Set the CSS vertical-align property of the column (eg. middle, top, bottom etc).  Defaults to undefined.
6990      */
6991     /**
6992      * @cfg {String} cursor (Optional)
6993      */
6994     /**
6995      * @cfg {String} tooltip (Optional)
6996      */
6997     /**
6998      * @cfg {Number} xs (Optional)
6999      */
7000     /**
7001      * @cfg {Number} sm (Optional)
7002      */
7003     /**
7004      * @cfg {Number} md (Optional)
7005      */
7006     /**
7007      * @cfg {Number} lg (Optional)
7008      */
7009     /**
7010      * Returns the id of the column at the specified index.
7011      * @param {Number} index The column index
7012      * @return {String} the id
7013      */
7014     getColumnId : function(index){
7015         return this.config[index].id;
7016     },
7017
7018     /**
7019      * Returns the column for a specified id.
7020      * @param {String} id The column id
7021      * @return {Object} the column
7022      */
7023     getColumnById : function(id){
7024         return this.lookup[id];
7025     },
7026
7027     
7028     /**
7029      * Returns the column for a specified dataIndex.
7030      * @param {String} dataIndex The column dataIndex
7031      * @return {Object|Boolean} the column or false if not found
7032      */
7033     getColumnByDataIndex: function(dataIndex){
7034         var index = this.findColumnIndex(dataIndex);
7035         return index > -1 ? this.config[index] : false;
7036     },
7037     
7038     /**
7039      * Returns the index for a specified column id.
7040      * @param {String} id The column id
7041      * @return {Number} the index, or -1 if not found
7042      */
7043     getIndexById : function(id){
7044         for(var i = 0, len = this.config.length; i < len; i++){
7045             if(this.config[i].id == id){
7046                 return i;
7047             }
7048         }
7049         return -1;
7050     },
7051     
7052     /**
7053      * Returns the index for a specified column dataIndex.
7054      * @param {String} dataIndex The column dataIndex
7055      * @return {Number} the index, or -1 if not found
7056      */
7057     
7058     findColumnIndex : function(dataIndex){
7059         for(var i = 0, len = this.config.length; i < len; i++){
7060             if(this.config[i].dataIndex == dataIndex){
7061                 return i;
7062             }
7063         }
7064         return -1;
7065     },
7066     
7067     
7068     moveColumn : function(oldIndex, newIndex){
7069         var c = this.config[oldIndex];
7070         this.config.splice(oldIndex, 1);
7071         this.config.splice(newIndex, 0, c);
7072         this.dataMap = null;
7073         this.fireEvent("columnmoved", this, oldIndex, newIndex);
7074     },
7075
7076     isLocked : function(colIndex){
7077         return this.config[colIndex].locked === true;
7078     },
7079
7080     setLocked : function(colIndex, value, suppressEvent){
7081         if(this.isLocked(colIndex) == value){
7082             return;
7083         }
7084         this.config[colIndex].locked = value;
7085         if(!suppressEvent){
7086             this.fireEvent("columnlockchange", this, colIndex, value);
7087         }
7088     },
7089
7090     getTotalLockedWidth : function(){
7091         var totalWidth = 0;
7092         for(var i = 0; i < this.config.length; i++){
7093             if(this.isLocked(i) && !this.isHidden(i)){
7094                 this.totalWidth += this.getColumnWidth(i);
7095             }
7096         }
7097         return totalWidth;
7098     },
7099
7100     getLockedCount : function(){
7101         for(var i = 0, len = this.config.length; i < len; i++){
7102             if(!this.isLocked(i)){
7103                 return i;
7104             }
7105         }
7106         
7107         return this.config.length;
7108     },
7109
7110     /**
7111      * Returns the number of columns.
7112      * @return {Number}
7113      */
7114     getColumnCount : function(visibleOnly){
7115         if(visibleOnly === true){
7116             var c = 0;
7117             for(var i = 0, len = this.config.length; i < len; i++){
7118                 if(!this.isHidden(i)){
7119                     c++;
7120                 }
7121             }
7122             return c;
7123         }
7124         return this.config.length;
7125     },
7126
7127     /**
7128      * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
7129      * @param {Function} fn
7130      * @param {Object} scope (optional)
7131      * @return {Array} result
7132      */
7133     getColumnsBy : function(fn, scope){
7134         var r = [];
7135         for(var i = 0, len = this.config.length; i < len; i++){
7136             var c = this.config[i];
7137             if(fn.call(scope||this, c, i) === true){
7138                 r[r.length] = c;
7139             }
7140         }
7141         return r;
7142     },
7143
7144     /**
7145      * Returns true if the specified column is sortable.
7146      * @param {Number} col The column index
7147      * @return {Boolean}
7148      */
7149     isSortable : function(col){
7150         if(typeof this.config[col].sortable == "undefined"){
7151             return this.defaultSortable;
7152         }
7153         return this.config[col].sortable;
7154     },
7155
7156     /**
7157      * Returns the rendering (formatting) function defined for the column.
7158      * @param {Number} col The column index.
7159      * @return {Function} The function used to render the cell. See {@link #setRenderer}.
7160      */
7161     getRenderer : function(col){
7162         if(!this.config[col].renderer){
7163             return Roo.grid.ColumnModel.defaultRenderer;
7164         }
7165         return this.config[col].renderer;
7166     },
7167
7168     /**
7169      * Sets the rendering (formatting) function for a column.
7170      * @param {Number} col The column index
7171      * @param {Function} fn The function to use to process the cell's raw data
7172      * to return HTML markup for the grid view. The render function is called with
7173      * the following parameters:<ul>
7174      * <li>Data value.</li>
7175      * <li>Cell metadata. An object in which you may set the following attributes:<ul>
7176      * <li>css A CSS style string to apply to the table cell.</li>
7177      * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
7178      * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
7179      * <li>Row index</li>
7180      * <li>Column index</li>
7181      * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
7182      */
7183     setRenderer : function(col, fn){
7184         this.config[col].renderer = fn;
7185     },
7186
7187     /**
7188      * Returns the width for the specified column.
7189      * @param {Number} col The column index
7190      * @return {Number}
7191      */
7192     getColumnWidth : function(col){
7193         return this.config[col].width * 1 || this.defaultWidth;
7194     },
7195
7196     /**
7197      * Sets the width for a column.
7198      * @param {Number} col The column index
7199      * @param {Number} width The new width
7200      */
7201     setColumnWidth : function(col, width, suppressEvent){
7202         this.config[col].width = width;
7203         this.totalWidth = null;
7204         if(!suppressEvent){
7205              this.fireEvent("widthchange", this, col, width);
7206         }
7207     },
7208
7209     /**
7210      * Returns the total width of all columns.
7211      * @param {Boolean} includeHidden True to include hidden column widths
7212      * @return {Number}
7213      */
7214     getTotalWidth : function(includeHidden){
7215         if(!this.totalWidth){
7216             this.totalWidth = 0;
7217             for(var i = 0, len = this.config.length; i < len; i++){
7218                 if(includeHidden || !this.isHidden(i)){
7219                     this.totalWidth += this.getColumnWidth(i);
7220                 }
7221             }
7222         }
7223         return this.totalWidth;
7224     },
7225
7226     /**
7227      * Returns the header for the specified column.
7228      * @param {Number} col The column index
7229      * @return {String}
7230      */
7231     getColumnHeader : function(col){
7232         return this.config[col].header;
7233     },
7234
7235     /**
7236      * Sets the header for a column.
7237      * @param {Number} col The column index
7238      * @param {String} header The new header
7239      */
7240     setColumnHeader : function(col, header){
7241         this.config[col].header = header;
7242         this.fireEvent("headerchange", this, col, header);
7243     },
7244
7245     /**
7246      * Returns the tooltip for the specified column.
7247      * @param {Number} col The column index
7248      * @return {String}
7249      */
7250     getColumnTooltip : function(col){
7251             return this.config[col].tooltip;
7252     },
7253     /**
7254      * Sets the tooltip for a column.
7255      * @param {Number} col The column index
7256      * @param {String} tooltip The new tooltip
7257      */
7258     setColumnTooltip : function(col, tooltip){
7259             this.config[col].tooltip = tooltip;
7260     },
7261
7262     /**
7263      * Returns the dataIndex for the specified column.
7264      * @param {Number} col The column index
7265      * @return {Number}
7266      */
7267     getDataIndex : function(col){
7268         return this.config[col].dataIndex;
7269     },
7270
7271     /**
7272      * Sets the dataIndex for a column.
7273      * @param {Number} col The column index
7274      * @param {Number} dataIndex The new dataIndex
7275      */
7276     setDataIndex : function(col, dataIndex){
7277         this.config[col].dataIndex = dataIndex;
7278     },
7279
7280     
7281     
7282     /**
7283      * Returns true if the cell is editable.
7284      * @param {Number} colIndex The column index
7285      * @param {Number} rowIndex The row index - this is nto actually used..?
7286      * @return {Boolean}
7287      */
7288     isCellEditable : function(colIndex, rowIndex){
7289         return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
7290     },
7291
7292     /**
7293      * Returns the editor defined for the cell/column.
7294      * return false or null to disable editing.
7295      * @param {Number} colIndex The column index
7296      * @param {Number} rowIndex The row index
7297      * @return {Object}
7298      */
7299     getCellEditor : function(colIndex, rowIndex){
7300         return this.config[colIndex].editor;
7301     },
7302
7303     /**
7304      * Sets if a column is editable.
7305      * @param {Number} col The column index
7306      * @param {Boolean} editable True if the column is editable
7307      */
7308     setEditable : function(col, editable){
7309         this.config[col].editable = editable;
7310     },
7311
7312
7313     /**
7314      * Returns true if the column is hidden.
7315      * @param {Number} colIndex The column index
7316      * @return {Boolean}
7317      */
7318     isHidden : function(colIndex){
7319         return this.config[colIndex].hidden;
7320     },
7321
7322
7323     /**
7324      * Returns true if the column width cannot be changed
7325      */
7326     isFixed : function(colIndex){
7327         return this.config[colIndex].fixed;
7328     },
7329
7330     /**
7331      * Returns true if the column can be resized
7332      * @return {Boolean}
7333      */
7334     isResizable : function(colIndex){
7335         return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
7336     },
7337     /**
7338      * Sets if a column is hidden.
7339      * @param {Number} colIndex The column index
7340      * @param {Boolean} hidden True if the column is hidden
7341      */
7342     setHidden : function(colIndex, hidden){
7343         this.config[colIndex].hidden = hidden;
7344         this.totalWidth = null;
7345         this.fireEvent("hiddenchange", this, colIndex, hidden);
7346     },
7347
7348     /**
7349      * Sets the editor for a column.
7350      * @param {Number} col The column index
7351      * @param {Object} editor The editor object
7352      */
7353     setEditor : function(col, editor){
7354         this.config[col].editor = editor;
7355     }
7356 });
7357
7358 Roo.grid.ColumnModel.defaultRenderer = function(value)
7359 {
7360     if(typeof value == "object") {
7361         return value;
7362     }
7363         if(typeof value == "string" && value.length < 1){
7364             return "&#160;";
7365         }
7366     
7367         return String.format("{0}", value);
7368 };
7369
7370 // Alias for backwards compatibility
7371 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
7372 /*
7373  * Based on:
7374  * Ext JS Library 1.1.1
7375  * Copyright(c) 2006-2007, Ext JS, LLC.
7376  *
7377  * Originally Released Under LGPL - original licence link has changed is not relivant.
7378  *
7379  * Fork - LGPL
7380  * <script type="text/javascript">
7381  */
7382  
7383 /**
7384  * @class Roo.LoadMask
7385  * A simple utility class for generically masking elements while loading data.  If the element being masked has
7386  * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
7387  * process and the mask element will be cached for reuse.  For all other elements, this mask will replace the
7388  * element's UpdateManager load indicator and will be destroyed after the initial load.
7389  * @constructor
7390  * Create a new LoadMask
7391  * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
7392  * @param {Object} config The config object
7393  */
7394 Roo.LoadMask = function(el, config){
7395     this.el = Roo.get(el);
7396     Roo.apply(this, config);
7397     if(this.store){
7398         this.store.on('beforeload', this.onBeforeLoad, this);
7399         this.store.on('load', this.onLoad, this);
7400         this.store.on('loadexception', this.onLoadException, this);
7401         this.removeMask = false;
7402     }else{
7403         var um = this.el.getUpdateManager();
7404         um.showLoadIndicator = false; // disable the default indicator
7405         um.on('beforeupdate', this.onBeforeLoad, this);
7406         um.on('update', this.onLoad, this);
7407         um.on('failure', this.onLoad, this);
7408         this.removeMask = true;
7409     }
7410 };
7411
7412 Roo.LoadMask.prototype = {
7413     /**
7414      * @cfg {Boolean} removeMask
7415      * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
7416      * False to persist the mask element reference for multiple uses (e.g., for paged data widgets).  Defaults to false.
7417      */
7418     /**
7419      * @cfg {String} msg
7420      * The text to display in a centered loading message box (defaults to 'Loading...')
7421      */
7422     msg : 'Loading...',
7423     /**
7424      * @cfg {String} msgCls
7425      * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
7426      */
7427     msgCls : 'x-mask-loading',
7428
7429     /**
7430      * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
7431      * @type Boolean
7432      */
7433     disabled: false,
7434
7435     /**
7436      * Disables the mask to prevent it from being displayed
7437      */
7438     disable : function(){
7439        this.disabled = true;
7440     },
7441
7442     /**
7443      * Enables the mask so that it can be displayed
7444      */
7445     enable : function(){
7446         this.disabled = false;
7447     },
7448     
7449     onLoadException : function()
7450     {
7451         Roo.log(arguments);
7452         
7453         if (typeof(arguments[3]) != 'undefined') {
7454             Roo.MessageBox.alert("Error loading",arguments[3]);
7455         } 
7456         /*
7457         try {
7458             if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
7459                 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
7460             }   
7461         } catch(e) {
7462             
7463         }
7464         */
7465     
7466         (function() { this.el.unmask(this.removeMask); }).defer(50, this);
7467     },
7468     // private
7469     onLoad : function()
7470     {
7471         (function() { this.el.unmask(this.removeMask); }).defer(50, this);
7472     },
7473
7474     // private
7475     onBeforeLoad : function(){
7476         if(!this.disabled){
7477             (function() { this.el.mask(this.msg, this.msgCls); }).defer(50, this);
7478         }
7479     },
7480
7481     // private
7482     destroy : function(){
7483         if(this.store){
7484             this.store.un('beforeload', this.onBeforeLoad, this);
7485             this.store.un('load', this.onLoad, this);
7486             this.store.un('loadexception', this.onLoadException, this);
7487         }else{
7488             var um = this.el.getUpdateManager();
7489             um.un('beforeupdate', this.onBeforeLoad, this);
7490             um.un('update', this.onLoad, this);
7491             um.un('failure', this.onLoad, this);
7492         }
7493     }
7494 };/*
7495  * - LGPL
7496  *
7497  * table
7498  * 
7499  */
7500
7501 /**
7502  * @class Roo.bootstrap.Table
7503  * @extends Roo.bootstrap.Component
7504  * Bootstrap Table class
7505  * @cfg {String} cls table class
7506  * @cfg {String} align (left|center|right) Specifies the alignment of a table according to surrounding text
7507  * @cfg {String} bgcolor Specifies the background color for a table
7508  * @cfg {Number} border Specifies whether the table cells should have borders or not
7509  * @cfg {Number} cellpadding Specifies the space between the cell wall and the cell content
7510  * @cfg {Number} cellspacing Specifies the space between cells
7511  * @cfg {String} frame Specifies which parts of the outside borders that should be visible
7512  * @cfg {String} rules Specifies which parts of the inside borders that should be visible
7513  * @cfg {String} sortable Specifies that the table should be sortable
7514  * @cfg {String} summary Specifies a summary of the content of a table
7515  * @cfg {Number} width Specifies the width of a table
7516  * @cfg {String} layout table layout (auto | fixed | initial | inherit)
7517  * 
7518  * @cfg {boolean} striped Should the rows be alternative striped
7519  * @cfg {boolean} bordered Add borders to the table
7520  * @cfg {boolean} hover Add hover highlighting
7521  * @cfg {boolean} condensed Format condensed
7522  * @cfg {boolean} responsive Format condensed
7523  * @cfg {Boolean} loadMask (true|false) default false
7524  * @cfg {Boolean} footerShow (true|false) generate tfoot, default true
7525  * @cfg {Boolean} headerShow (true|false) generate thead, default true
7526  * @cfg {Boolean} rowSelection (true|false) default false
7527  * @cfg {Boolean} cellSelection (true|false) default false
7528  * @cfg {Boolean} scrollBody (true|false) default false - body scrolled / fixed header
7529  * @cfg {Roo.bootstrap.PagingToolbar} footer  a paging toolbar
7530  * @cfg {Boolean} lazyLoad  auto load data while scrolling to the end (default false)
7531  * @cfg {Boolean} auto_hide_footer  auto hide footer if only one page (default false)
7532  
7533  * 
7534  * @constructor
7535  * Create a new Table
7536  * @param {Object} config The config object
7537  */
7538
7539 Roo.bootstrap.Table = function(config){
7540     Roo.bootstrap.Table.superclass.constructor.call(this, config);
7541     
7542   
7543     
7544     // BC...
7545     this.rowSelection = (typeof(config.rowSelection) != 'undefined') ? config.rowSelection : this.rowSelection;
7546     this.cellSelection = (typeof(config.cellSelection) != 'undefined') ? config.cellSelection : this.cellSelection;
7547     this.headerShow = (typeof(config.thead) != 'undefined') ? config.thead : this.headerShow;
7548     this.footerShow = (typeof(config.tfoot) != 'undefined') ? config.tfoot : this.footerShow;
7549     
7550     this.sm = this.sm || {xtype: 'RowSelectionModel'};
7551     if (this.sm) {
7552         this.sm.grid = this;
7553         this.selModel = Roo.factory(this.sm, Roo.bootstrap.Table);
7554         this.sm = this.selModel;
7555         this.sm.xmodule = this.xmodule || false;
7556     }
7557     
7558     if (this.cm && typeof(this.cm.config) == 'undefined') {
7559         this.colModel = new Roo.grid.ColumnModel(this.cm);
7560         this.cm = this.colModel;
7561         this.cm.xmodule = this.xmodule || false;
7562     }
7563     if (this.store) {
7564         this.store= Roo.factory(this.store, Roo.data);
7565         this.ds = this.store;
7566         this.ds.xmodule = this.xmodule || false;
7567          
7568     }
7569     if (this.footer && this.store) {
7570         this.footer.dataSource = this.ds;
7571         this.footer = Roo.factory(this.footer);
7572     }
7573     
7574     /** @private */
7575     this.addEvents({
7576         /**
7577          * @event cellclick
7578          * Fires when a cell is clicked
7579          * @param {Roo.bootstrap.Table} this
7580          * @param {Roo.Element} el
7581          * @param {Number} rowIndex
7582          * @param {Number} columnIndex
7583          * @param {Roo.EventObject} e
7584          */
7585         "cellclick" : true,
7586         /**
7587          * @event celldblclick
7588          * Fires when a cell is double clicked
7589          * @param {Roo.bootstrap.Table} this
7590          * @param {Roo.Element} el
7591          * @param {Number} rowIndex
7592          * @param {Number} columnIndex
7593          * @param {Roo.EventObject} e
7594          */
7595         "celldblclick" : true,
7596         /**
7597          * @event rowclick
7598          * Fires when a row is clicked
7599          * @param {Roo.bootstrap.Table} this
7600          * @param {Roo.Element} el
7601          * @param {Number} rowIndex
7602          * @param {Roo.EventObject} e
7603          */
7604         "rowclick" : true,
7605         /**
7606          * @event rowdblclick
7607          * Fires when a row is double clicked
7608          * @param {Roo.bootstrap.Table} this
7609          * @param {Roo.Element} el
7610          * @param {Number} rowIndex
7611          * @param {Roo.EventObject} e
7612          */
7613         "rowdblclick" : true,
7614         /**
7615          * @event mouseover
7616          * Fires when a mouseover occur
7617          * @param {Roo.bootstrap.Table} this
7618          * @param {Roo.Element} el
7619          * @param {Number} rowIndex
7620          * @param {Number} columnIndex
7621          * @param {Roo.EventObject} e
7622          */
7623         "mouseover" : true,
7624         /**
7625          * @event mouseout
7626          * Fires when a mouseout occur
7627          * @param {Roo.bootstrap.Table} this
7628          * @param {Roo.Element} el
7629          * @param {Number} rowIndex
7630          * @param {Number} columnIndex
7631          * @param {Roo.EventObject} e
7632          */
7633         "mouseout" : true,
7634         /**
7635          * @event rowclass
7636          * Fires when a row is rendered, so you can change add a style to it.
7637          * @param {Roo.bootstrap.Table} this
7638          * @param {Object} rowcfg   contains record  rowIndex colIndex and rowClass - set rowClass to add a style.
7639          */
7640         'rowclass' : true,
7641           /**
7642          * @event rowsrendered
7643          * Fires when all the  rows have been rendered
7644          * @param {Roo.bootstrap.Table} this
7645          */
7646         'rowsrendered' : true,
7647         /**
7648          * @event contextmenu
7649          * The raw contextmenu event for the entire grid.
7650          * @param {Roo.EventObject} e
7651          */
7652         "contextmenu" : true,
7653         /**
7654          * @event rowcontextmenu
7655          * Fires when a row is right clicked
7656          * @param {Roo.bootstrap.Table} this
7657          * @param {Number} rowIndex
7658          * @param {Roo.EventObject} e
7659          */
7660         "rowcontextmenu" : true,
7661         /**
7662          * @event cellcontextmenu
7663          * Fires when a cell is right clicked
7664          * @param {Roo.bootstrap.Table} this
7665          * @param {Number} rowIndex
7666          * @param {Number} cellIndex
7667          * @param {Roo.EventObject} e
7668          */
7669          "cellcontextmenu" : true,
7670          /**
7671          * @event headercontextmenu
7672          * Fires when a header is right clicked
7673          * @param {Roo.bootstrap.Table} this
7674          * @param {Number} columnIndex
7675          * @param {Roo.EventObject} e
7676          */
7677         "headercontextmenu" : true
7678     });
7679 };
7680
7681 Roo.extend(Roo.bootstrap.Table, Roo.bootstrap.Component,  {
7682     
7683     cls: false,
7684     align: false,
7685     bgcolor: false,
7686     border: false,
7687     cellpadding: false,
7688     cellspacing: false,
7689     frame: false,
7690     rules: false,
7691     sortable: false,
7692     summary: false,
7693     width: false,
7694     striped : false,
7695     scrollBody : false,
7696     bordered: false,
7697     hover:  false,
7698     condensed : false,
7699     responsive : false,
7700     sm : false,
7701     cm : false,
7702     store : false,
7703     loadMask : false,
7704     footerShow : true,
7705     headerShow : true,
7706   
7707     rowSelection : false,
7708     cellSelection : false,
7709     layout : false,
7710     
7711     // Roo.Element - the tbody
7712     mainBody: false,
7713     // Roo.Element - thead element
7714     mainHead: false,
7715     
7716     container: false, // used by gridpanel...
7717     
7718     lazyLoad : false,
7719     
7720     CSS : Roo.util.CSS,
7721     
7722     auto_hide_footer : false,
7723     
7724     getAutoCreate : function()
7725     {
7726         var cfg = Roo.apply({}, Roo.bootstrap.Table.superclass.getAutoCreate.call(this));
7727         
7728         cfg = {
7729             tag: 'table',
7730             cls : 'table',
7731             cn : []
7732         };
7733         if (this.scrollBody) {
7734             cfg.cls += ' table-body-fixed';
7735         }    
7736         if (this.striped) {
7737             cfg.cls += ' table-striped';
7738         }
7739         
7740         if (this.hover) {
7741             cfg.cls += ' table-hover';
7742         }
7743         if (this.bordered) {
7744             cfg.cls += ' table-bordered';
7745         }
7746         if (this.condensed) {
7747             cfg.cls += ' table-condensed';
7748         }
7749         if (this.responsive) {
7750             cfg.cls += ' table-responsive';
7751         }
7752         
7753         if (this.cls) {
7754             cfg.cls+=  ' ' +this.cls;
7755         }
7756         
7757         // this lot should be simplifed...
7758         var _t = this;
7759         var cp = [
7760             'align',
7761             'bgcolor',
7762             'border',
7763             'cellpadding',
7764             'cellspacing',
7765             'frame',
7766             'rules',
7767             'sortable',
7768             'summary',
7769             'width'
7770         ].forEach(function(k) {
7771             if (_t[k]) {
7772                 cfg[k] = _t[k];
7773             }
7774         });
7775         
7776         
7777         if (this.layout) {
7778             cfg.style = (typeof(cfg.style) == 'undefined') ? ('table-layout:' + this.layout + ';') : (cfg.style + ('table-layout:' + this.layout + ';'));
7779         }
7780         
7781         if(this.store || this.cm){
7782             if(this.headerShow){
7783                 cfg.cn.push(this.renderHeader());
7784             }
7785             
7786             cfg.cn.push(this.renderBody());
7787             
7788             if(this.footerShow){
7789                 cfg.cn.push(this.renderFooter());
7790             }
7791             // where does this come from?
7792             //cfg.cls+=  ' TableGrid';
7793         }
7794         
7795         return { cn : [ cfg ] };
7796     },
7797     
7798     initEvents : function()
7799     {   
7800         if(!this.store || !this.cm){
7801             return;
7802         }
7803         if (this.selModel) {
7804             this.selModel.initEvents();
7805         }
7806         
7807         
7808         //Roo.log('initEvents with ds!!!!');
7809         
7810         this.mainBody = this.el.select('tbody', true).first();
7811         this.mainHead = this.el.select('thead', true).first();
7812         this.mainFoot = this.el.select('tfoot', true).first();
7813         
7814         
7815         
7816         var _this = this;
7817         
7818         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
7819             e.on('click', _this.sort, _this);
7820         });
7821         
7822         this.mainBody.on("click", this.onClick, this);
7823         this.mainBody.on("dblclick", this.onDblClick, this);
7824         
7825         // why is this done????? = it breaks dialogs??
7826         //this.parent().el.setStyle('position', 'relative');
7827         
7828         
7829         if (this.footer) {
7830             this.footer.parentId = this.id;
7831             this.footer.onRender(this.el.select('tfoot tr td').first(), null);
7832             
7833             if(this.lazyLoad){
7834                 this.el.select('tfoot tr td').first().addClass('hide');
7835             }
7836         } 
7837         
7838         if(this.loadMask) {
7839             this.maskEl = new Roo.LoadMask(this.el, { store : this.ds, msgCls: 'roo-el-mask-msg' });
7840         }
7841         
7842         this.store.on('load', this.onLoad, this);
7843         this.store.on('beforeload', this.onBeforeLoad, this);
7844         this.store.on('update', this.onUpdate, this);
7845         this.store.on('add', this.onAdd, this);
7846         this.store.on("clear", this.clear, this);
7847         
7848         this.el.on("contextmenu", this.onContextMenu, this);
7849         
7850         this.mainBody.on('scroll', this.onBodyScroll, this);
7851         
7852         this.cm.on("headerchange", this.onHeaderChange, this);
7853         
7854         this.cm.on("hiddenchange", this.onHiddenChange, this, arguments);
7855         
7856     },
7857     
7858     onContextMenu : function(e, t)
7859     {
7860         this.processEvent("contextmenu", e);
7861     },
7862     
7863     processEvent : function(name, e)
7864     {
7865         if (name != 'touchstart' ) {
7866             this.fireEvent(name, e);    
7867         }
7868         
7869         var t = e.getTarget();
7870         
7871         var cell = Roo.get(t);
7872         
7873         if(!cell){
7874             return;
7875         }
7876         
7877         if(cell.findParent('tfoot', false, true)){
7878             return;
7879         }
7880         
7881         if(cell.findParent('thead', false, true)){
7882             
7883             if(e.getTarget().nodeName.toLowerCase() != 'th'){
7884                 cell = Roo.get(t).findParent('th', false, true);
7885                 if (!cell) {
7886                     Roo.log("failed to find th in thead?");
7887                     Roo.log(e.getTarget());
7888                     return;
7889                 }
7890             }
7891             
7892             var cellIndex = cell.dom.cellIndex;
7893             
7894             var ename = name == 'touchstart' ? 'click' : name;
7895             this.fireEvent("header" + ename, this, cellIndex, e);
7896             
7897             return;
7898         }
7899         
7900         if(e.getTarget().nodeName.toLowerCase() != 'td'){
7901             cell = Roo.get(t).findParent('td', false, true);
7902             if (!cell) {
7903                 Roo.log("failed to find th in tbody?");
7904                 Roo.log(e.getTarget());
7905                 return;
7906             }
7907         }
7908         
7909         var row = cell.findParent('tr', false, true);
7910         var cellIndex = cell.dom.cellIndex;
7911         var rowIndex = row.dom.rowIndex - 1;
7912         
7913         if(row !== false){
7914             
7915             this.fireEvent("row" + name, this, rowIndex, e);
7916             
7917             if(cell !== false){
7918             
7919                 this.fireEvent("cell" + name, this, rowIndex, cellIndex, e);
7920             }
7921         }
7922         
7923     },
7924     
7925     onMouseover : function(e, el)
7926     {
7927         var cell = Roo.get(el);
7928         
7929         if(!cell){
7930             return;
7931         }
7932         
7933         if(e.getTarget().nodeName.toLowerCase() != 'td'){
7934             cell = cell.findParent('td', false, true);
7935         }
7936         
7937         var row = cell.findParent('tr', false, true);
7938         var cellIndex = cell.dom.cellIndex;
7939         var rowIndex = row.dom.rowIndex - 1; // start from 0
7940         
7941         this.fireEvent('mouseover', this, cell, rowIndex, cellIndex, e);
7942         
7943     },
7944     
7945     onMouseout : function(e, el)
7946     {
7947         var cell = Roo.get(el);
7948         
7949         if(!cell){
7950             return;
7951         }
7952         
7953         if(e.getTarget().nodeName.toLowerCase() != 'td'){
7954             cell = cell.findParent('td', false, true);
7955         }
7956         
7957         var row = cell.findParent('tr', false, true);
7958         var cellIndex = cell.dom.cellIndex;
7959         var rowIndex = row.dom.rowIndex - 1; // start from 0
7960         
7961         this.fireEvent('mouseout', this, cell, rowIndex, cellIndex, e);
7962         
7963     },
7964     
7965     onClick : function(e, el)
7966     {
7967         var cell = Roo.get(el);
7968         
7969         if(!cell || (!this.cellSelection && !this.rowSelection)){
7970             return;
7971         }
7972         
7973         if(e.getTarget().nodeName.toLowerCase() != 'td'){
7974             cell = cell.findParent('td', false, true);
7975         }
7976         
7977         if(!cell || typeof(cell) == 'undefined'){
7978             return;
7979         }
7980         
7981         var row = cell.findParent('tr', false, true);
7982         
7983         if(!row || typeof(row) == 'undefined'){
7984             return;
7985         }
7986         
7987         var cellIndex = cell.dom.cellIndex;
7988         var rowIndex = this.getRowIndex(row);
7989         
7990         // why??? - should these not be based on SelectionModel?
7991         if(this.cellSelection){
7992             this.fireEvent('cellclick', this, cell, rowIndex, cellIndex, e);
7993         }
7994         
7995         if(this.rowSelection){
7996             this.fireEvent('rowclick', this, row, rowIndex, e);
7997         }
7998         
7999         
8000     },
8001         
8002     onDblClick : function(e,el)
8003     {
8004         var cell = Roo.get(el);
8005         
8006         if(!cell || (!this.cellSelection && !this.rowSelection)){
8007             return;
8008         }
8009         
8010         if(e.getTarget().nodeName.toLowerCase() != 'td'){
8011             cell = cell.findParent('td', false, true);
8012         }
8013         
8014         if(!cell || typeof(cell) == 'undefined'){
8015             return;
8016         }
8017         
8018         var row = cell.findParent('tr', false, true);
8019         
8020         if(!row || typeof(row) == 'undefined'){
8021             return;
8022         }
8023         
8024         var cellIndex = cell.dom.cellIndex;
8025         var rowIndex = this.getRowIndex(row);
8026         
8027         if(this.cellSelection){
8028             this.fireEvent('celldblclick', this, cell, rowIndex, cellIndex, e);
8029         }
8030         
8031         if(this.rowSelection){
8032             this.fireEvent('rowdblclick', this, row, rowIndex, e);
8033         }
8034     },
8035     
8036     sort : function(e,el)
8037     {
8038         var col = Roo.get(el);
8039         
8040         if(!col.hasClass('sortable')){
8041             return;
8042         }
8043         
8044         var sort = col.attr('sort');
8045         var dir = 'ASC';
8046         
8047         if(col.select('i', true).first().hasClass('glyphicon-arrow-up')){
8048             dir = 'DESC';
8049         }
8050         
8051         this.store.sortInfo = {field : sort, direction : dir};
8052         
8053         if (this.footer) {
8054             Roo.log("calling footer first");
8055             this.footer.onClick('first');
8056         } else {
8057         
8058             this.store.load({ params : { start : 0 } });
8059         }
8060     },
8061     
8062     renderHeader : function()
8063     {
8064         var header = {
8065             tag: 'thead',
8066             cn : []
8067         };
8068         
8069         var cm = this.cm;
8070         this.totalWidth = 0;
8071         
8072         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
8073             
8074             var config = cm.config[i];
8075             
8076             var c = {
8077                 tag: 'th',
8078                 cls : 'x-hcol-' + i,
8079                 style : '',
8080                 html: cm.getColumnHeader(i)
8081             };
8082             
8083             var hh = '';
8084             
8085             if(typeof(config.sortable) != 'undefined' && config.sortable){
8086                 c.cls = 'sortable';
8087                 c.html = '<i class="glyphicon"></i>' + c.html;
8088             }
8089             
8090             // could use BS4 hidden-..-down 
8091             
8092             if(typeof(config.lgHeader) != 'undefined'){
8093                 hh += '<span class="hidden-xs hidden-sm hidden-md ">' + config.lgHeader + '</span>';
8094             }
8095             
8096             if(typeof(config.mdHeader) != 'undefined'){
8097                 hh += '<span class="hidden-xs hidden-sm hidden-lg">' + config.mdHeader + '</span>';
8098             }
8099             
8100             if(typeof(config.smHeader) != 'undefined'){
8101                 hh += '<span class="hidden-xs hidden-md hidden-lg">' + config.smHeader + '</span>';
8102             }
8103             
8104             if(typeof(config.xsHeader) != 'undefined'){
8105                 hh += '<span class="hidden-sm hidden-md hidden-lg">' + config.xsHeader + '</span>';
8106             }
8107             
8108             if(hh.length){
8109                 c.html = hh;
8110             }
8111             
8112             if(typeof(config.tooltip) != 'undefined'){
8113                 c.tooltip = config.tooltip;
8114             }
8115             
8116             if(typeof(config.colspan) != 'undefined'){
8117                 c.colspan = config.colspan;
8118             }
8119             
8120             if(typeof(config.hidden) != 'undefined' && config.hidden){
8121                 c.style += ' display:none;';
8122             }
8123             
8124             if(typeof(config.dataIndex) != 'undefined'){
8125                 c.sort = config.dataIndex;
8126             }
8127             
8128            
8129             
8130             if(typeof(config.align) != 'undefined' && config.align.length){
8131                 c.style += ' text-align:' + config.align + ';';
8132             }
8133             
8134             if(typeof(config.width) != 'undefined'){
8135                 c.style += ' width:' + config.width + 'px;';
8136                 this.totalWidth += config.width;
8137             } else {
8138                 this.totalWidth += 100; // assume minimum of 100 per column?
8139             }
8140             
8141             if(typeof(config.cls) != 'undefined'){
8142                 c.cls = (typeof(c.cls) == 'undefined') ? config.cls : (c.cls + ' ' + config.cls);
8143             }
8144             
8145             ['xs','sm','md','lg'].map(function(size){
8146                 
8147                 if(typeof(config[size]) == 'undefined'){
8148                     return;
8149                 }
8150                  
8151                 if (!config[size]) { // 0 = hidden
8152                     // BS 4 '0' is treated as hide that column and below.
8153                     c.cls += ' hidden-' + size + ' hidden' + size + '-down';
8154                     return;
8155                 }
8156                 
8157                 c.cls += ' col-' + size + '-' + config[size] + (
8158                     size == 'xs' ? (' col-' + config[size] ) : '' // bs4 col-{num} replaces col-xs
8159                 );
8160                 
8161                 
8162             });
8163             
8164             header.cn.push(c)
8165         }
8166         
8167         return header;
8168     },
8169     
8170     renderBody : function()
8171     {
8172         var body = {
8173             tag: 'tbody',
8174             cn : [
8175                 {
8176                     tag: 'tr',
8177                     cn : [
8178                         {
8179                             tag : 'td',
8180                             colspan :  this.cm.getColumnCount()
8181                         }
8182                     ]
8183                 }
8184             ]
8185         };
8186         
8187         return body;
8188     },
8189     
8190     renderFooter : function()
8191     {
8192         var footer = {
8193             tag: 'tfoot',
8194             cn : [
8195                 {
8196                     tag: 'tr',
8197                     cn : [
8198                         {
8199                             tag : 'td',
8200                             colspan :  this.cm.getColumnCount()
8201                         }
8202                     ]
8203                 }
8204             ]
8205         };
8206         
8207         return footer;
8208     },
8209     
8210     
8211     
8212     onLoad : function()
8213     {
8214 //        Roo.log('ds onload');
8215         this.clear();
8216         
8217         var _this = this;
8218         var cm = this.cm;
8219         var ds = this.store;
8220         
8221         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
8222             e.select('i', true).removeClass(['glyphicon-arrow-up', 'glyphicon-arrow-down']);
8223             if (_this.store.sortInfo) {
8224                     
8225                 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'ASC'){
8226                     e.select('i', true).addClass(['glyphicon-arrow-up']);
8227                 }
8228                 
8229                 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'DESC'){
8230                     e.select('i', true).addClass(['glyphicon-arrow-down']);
8231                 }
8232             }
8233         });
8234         
8235         var tbody =  this.mainBody;
8236               
8237         if(ds.getCount() > 0){
8238             ds.data.each(function(d,rowIndex){
8239                 var row =  this.renderRow(cm, ds, rowIndex);
8240                 
8241                 tbody.createChild(row);
8242                 
8243                 var _this = this;
8244                 
8245                 if(row.cellObjects.length){
8246                     Roo.each(row.cellObjects, function(r){
8247                         _this.renderCellObject(r);
8248                     })
8249                 }
8250                 
8251             }, this);
8252         }
8253         
8254         var tfoot = this.el.select('tfoot', true).first();
8255         
8256         if(this.footerShow && this.auto_hide_footer && this.mainFoot){
8257             
8258             this.mainFoot.setVisibilityMode(Roo.Element.DISPLAY).hide();
8259             
8260             var total = this.ds.getTotalCount();
8261             
8262             if(this.footer.pageSize < total){
8263                 this.mainFoot.show();
8264             }
8265         }
8266         
8267         Roo.each(this.el.select('tbody td', true).elements, function(e){
8268             e.on('mouseover', _this.onMouseover, _this);
8269         });
8270         
8271         Roo.each(this.el.select('tbody td', true).elements, function(e){
8272             e.on('mouseout', _this.onMouseout, _this);
8273         });
8274         this.fireEvent('rowsrendered', this);
8275         
8276         this.autoSize();
8277     },
8278     
8279     
8280     onUpdate : function(ds,record)
8281     {
8282         this.refreshRow(record);
8283         this.autoSize();
8284     },
8285     
8286     onRemove : function(ds, record, index, isUpdate){
8287         if(isUpdate !== true){
8288             this.fireEvent("beforerowremoved", this, index, record);
8289         }
8290         var bt = this.mainBody.dom;
8291         
8292         var rows = this.el.select('tbody > tr', true).elements;
8293         
8294         if(typeof(rows[index]) != 'undefined'){
8295             bt.removeChild(rows[index].dom);
8296         }
8297         
8298 //        if(bt.rows[index]){
8299 //            bt.removeChild(bt.rows[index]);
8300 //        }
8301         
8302         if(isUpdate !== true){
8303             //this.stripeRows(index);
8304             //this.syncRowHeights(index, index);
8305             //this.layout();
8306             this.fireEvent("rowremoved", this, index, record);
8307         }
8308     },
8309     
8310     onAdd : function(ds, records, rowIndex)
8311     {
8312         //Roo.log('on Add called');
8313         // - note this does not handle multiple adding very well..
8314         var bt = this.mainBody.dom;
8315         for (var i =0 ; i < records.length;i++) {
8316             //Roo.log('call insert row Add called on ' + rowIndex + ':' + i);
8317             //Roo.log(records[i]);
8318             //Roo.log(this.store.getAt(rowIndex+i));
8319             this.insertRow(this.store, rowIndex + i, false);
8320             return;
8321         }
8322         
8323     },
8324     
8325     
8326     refreshRow : function(record){
8327         var ds = this.store, index;
8328         if(typeof record == 'number'){
8329             index = record;
8330             record = ds.getAt(index);
8331         }else{
8332             index = ds.indexOf(record);
8333             if (index < 0) {
8334                 return; // should not happen - but seems to 
8335             }
8336         }
8337         this.insertRow(ds, index, true);
8338         this.autoSize();
8339         this.onRemove(ds, record, index+1, true);
8340         this.autoSize();
8341         //this.syncRowHeights(index, index);
8342         //this.layout();
8343         this.fireEvent("rowupdated", this, index, record);
8344     },
8345     
8346     insertRow : function(dm, rowIndex, isUpdate){
8347         
8348         if(!isUpdate){
8349             this.fireEvent("beforerowsinserted", this, rowIndex);
8350         }
8351             //var s = this.getScrollState();
8352         var row = this.renderRow(this.cm, this.store, rowIndex);
8353         // insert before rowIndex..
8354         var e = this.mainBody.createChild(row,this.getRowDom(rowIndex));
8355         
8356         var _this = this;
8357                 
8358         if(row.cellObjects.length){
8359             Roo.each(row.cellObjects, function(r){
8360                 _this.renderCellObject(r);
8361             })
8362         }
8363             
8364         if(!isUpdate){
8365             this.fireEvent("rowsinserted", this, rowIndex);
8366             //this.syncRowHeights(firstRow, lastRow);
8367             //this.stripeRows(firstRow);
8368             //this.layout();
8369         }
8370         
8371     },
8372     
8373     
8374     getRowDom : function(rowIndex)
8375     {
8376         var rows = this.el.select('tbody > tr', true).elements;
8377         
8378         return (typeof(rows[rowIndex]) == 'undefined') ? false : rows[rowIndex];
8379         
8380     },
8381     // returns the object tree for a tr..
8382   
8383     
8384     renderRow : function(cm, ds, rowIndex) 
8385     {
8386         var d = ds.getAt(rowIndex);
8387         
8388         var row = {
8389             tag : 'tr',
8390             cls : 'x-row-' + rowIndex,
8391             cn : []
8392         };
8393             
8394         var cellObjects = [];
8395         
8396         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
8397             var config = cm.config[i];
8398             
8399             var renderer = cm.getRenderer(i);
8400             var value = '';
8401             var id = false;
8402             
8403             if(typeof(renderer) !== 'undefined'){
8404                 value = renderer(d.data[cm.getDataIndex(i)], false, d);
8405             }
8406             // if object are returned, then they are expected to be Roo.bootstrap.Component instances
8407             // and are rendered into the cells after the row is rendered - using the id for the element.
8408             
8409             if(typeof(value) === 'object'){
8410                 id = Roo.id();
8411                 cellObjects.push({
8412                     container : id,
8413                     cfg : value 
8414                 })
8415             }
8416             
8417             var rowcfg = {
8418                 record: d,
8419                 rowIndex : rowIndex,
8420                 colIndex : i,
8421                 rowClass : ''
8422             };
8423
8424             this.fireEvent('rowclass', this, rowcfg);
8425             
8426             var td = {
8427                 tag: 'td',
8428                 cls : rowcfg.rowClass + ' x-col-' + i,
8429                 style: '',
8430                 html: (typeof(value) === 'object') ? '' : value
8431             };
8432             
8433             if (id) {
8434                 td.id = id;
8435             }
8436             
8437             if(typeof(config.colspan) != 'undefined'){
8438                 td.colspan = config.colspan;
8439             }
8440             
8441             if(typeof(config.hidden) != 'undefined' && config.hidden){
8442                 td.style += ' display:none;';
8443             }
8444             
8445             if(typeof(config.align) != 'undefined' && config.align.length){
8446                 td.style += ' text-align:' + config.align + ';';
8447             }
8448             if(typeof(config.valign) != 'undefined' && config.valign.length){
8449                 td.style += ' vertical-align:' + config.valign + ';';
8450             }
8451             
8452             if(typeof(config.width) != 'undefined'){
8453                 td.style += ' width:' +  config.width + 'px;';
8454             }
8455             
8456             if(typeof(config.cursor) != 'undefined'){
8457                 td.style += ' cursor:' +  config.cursor + ';';
8458             }
8459             
8460             if(typeof(config.cls) != 'undefined'){
8461                 td.cls = (typeof(td.cls) == 'undefined') ? config.cls : (td.cls + ' ' + config.cls);
8462             }
8463             
8464             ['xs','sm','md','lg'].map(function(size){
8465                 
8466                 if(typeof(config[size]) == 'undefined'){
8467                     return;
8468                 }
8469                 
8470                 
8471                   
8472                 if (!config[size]) { // 0 = hidden
8473                     // BS 4 '0' is treated as hide that column and below.
8474                     td.cls += ' hidden-' + size + ' hidden' + size + '-down';
8475                     return;
8476                 }
8477                 
8478                 td.cls += ' col-' + size + '-' + config[size] + (
8479                     size == 'xs' ? (' col-' +   config[size] ) : '' // bs4 col-{num} replaces col-xs
8480                 );
8481                  
8482
8483             });
8484             
8485             row.cn.push(td);
8486            
8487         }
8488         
8489         row.cellObjects = cellObjects;
8490         
8491         return row;
8492           
8493     },
8494     
8495     
8496     
8497     onBeforeLoad : function()
8498     {
8499         
8500     },
8501      /**
8502      * Remove all rows
8503      */
8504     clear : function()
8505     {
8506         this.el.select('tbody', true).first().dom.innerHTML = '';
8507     },
8508     /**
8509      * Show or hide a row.
8510      * @param {Number} rowIndex to show or hide
8511      * @param {Boolean} state hide
8512      */
8513     setRowVisibility : function(rowIndex, state)
8514     {
8515         var bt = this.mainBody.dom;
8516         
8517         var rows = this.el.select('tbody > tr', true).elements;
8518         
8519         if(typeof(rows[rowIndex]) == 'undefined'){
8520             return;
8521         }
8522         rows[rowIndex].dom.style.display = state ? '' : 'none';
8523     },
8524     
8525     
8526     getSelectionModel : function(){
8527         if(!this.selModel){
8528             this.selModel = new Roo.bootstrap.Table.RowSelectionModel({grid: this});
8529         }
8530         return this.selModel;
8531     },
8532     /*
8533      * Render the Roo.bootstrap object from renderder
8534      */
8535     renderCellObject : function(r)
8536     {
8537         var _this = this;
8538         
8539         r.cfg.parentId = (typeof(r.container) == 'string') ? r.container : r.container.id;
8540         
8541         var t = r.cfg.render(r.container);
8542         
8543         if(r.cfg.cn){
8544             Roo.each(r.cfg.cn, function(c){
8545                 var child = {
8546                     container: t.getChildContainer(),
8547                     cfg: c
8548                 };
8549                 _this.renderCellObject(child);
8550             })
8551         }
8552     },
8553     
8554     getRowIndex : function(row)
8555     {
8556         var rowIndex = -1;
8557         
8558         Roo.each(this.el.select('tbody > tr', true).elements, function(el, index){
8559             if(el != row){
8560                 return;
8561             }
8562             
8563             rowIndex = index;
8564         });
8565         
8566         return rowIndex;
8567     },
8568      /**
8569      * Returns the grid's underlying element = used by panel.Grid
8570      * @return {Element} The element
8571      */
8572     getGridEl : function(){
8573         return this.el;
8574     },
8575      /**
8576      * Forces a resize - used by panel.Grid
8577      * @return {Element} The element
8578      */
8579     autoSize : function()
8580     {
8581         //var ctr = Roo.get(this.container.dom.parentElement);
8582         var ctr = Roo.get(this.el.dom);
8583         
8584         var thd = this.getGridEl().select('thead',true).first();
8585         var tbd = this.getGridEl().select('tbody', true).first();
8586         var tfd = this.getGridEl().select('tfoot', true).first();
8587         
8588         var cw = ctr.getWidth();
8589         
8590         if (tbd) {
8591             
8592             tbd.setWidth(ctr.getWidth());
8593             // if the body has a max height - and then scrolls - we should perhaps set up the height here
8594             // this needs fixing for various usage - currently only hydra job advers I think..
8595             //tdb.setHeight(
8596             //        ctr.getHeight() - ((thd ? thd.getHeight() : 0) + (tfd ? tfd.getHeight() : 0))
8597             //); 
8598             var barsize = (tbd.dom.offsetWidth - tbd.dom.clientWidth);
8599             cw -= barsize;
8600         }
8601         cw = Math.max(cw, this.totalWidth);
8602         this.getGridEl().select('tr',true).setWidth(cw);
8603         // resize 'expandable coloumn?
8604         
8605         return; // we doe not have a view in this design..
8606         
8607     },
8608     onBodyScroll: function()
8609     {
8610         //Roo.log("body scrolled');" + this.mainBody.dom.scrollLeft);
8611         if(this.mainHead){
8612             this.mainHead.setStyle({
8613                 'position' : 'relative',
8614                 'left': (-1* this.mainBody.dom.scrollLeft) + 'px'
8615             });
8616         }
8617         
8618         if(this.lazyLoad){
8619             
8620             var scrollHeight = this.mainBody.dom.scrollHeight;
8621             
8622             var scrollTop = Math.ceil(this.mainBody.getScroll().top);
8623             
8624             var height = this.mainBody.getHeight();
8625             
8626             if(scrollHeight - height == scrollTop) {
8627                 
8628                 var total = this.ds.getTotalCount();
8629                 
8630                 if(this.footer.cursor + this.footer.pageSize < total){
8631                     
8632                     this.footer.ds.load({
8633                         params : {
8634                             start : this.footer.cursor + this.footer.pageSize,
8635                             limit : this.footer.pageSize
8636                         },
8637                         add : true
8638                     });
8639                 }
8640             }
8641             
8642         }
8643     },
8644     
8645     onHeaderChange : function()
8646     {
8647         var header = this.renderHeader();
8648         var table = this.el.select('table', true).first();
8649         
8650         this.mainHead.remove();
8651         this.mainHead = table.createChild(header, this.mainBody, false);
8652     },
8653     
8654     onHiddenChange : function(colModel, colIndex, hidden)
8655     {
8656         var thSelector = '#' + this.id + ' .x-hcol-' + colIndex;
8657         var tdSelector = '#' + this.id + ' .x-col-' + colIndex;
8658         
8659         this.CSS.updateRule(thSelector, "display", "");
8660         this.CSS.updateRule(tdSelector, "display", "");
8661         
8662         if(hidden){
8663             this.CSS.updateRule(thSelector, "display", "none");
8664             this.CSS.updateRule(tdSelector, "display", "none");
8665         }
8666         
8667         this.onHeaderChange();
8668         this.onLoad();
8669     },
8670     
8671     setColumnWidth: function(col_index, width)
8672     {
8673         // width = "md-2 xs-2..."
8674         if(!this.colModel.config[col_index]) {
8675             return;
8676         }
8677         
8678         var w = width.split(" ");
8679         
8680         var rows = this.el.dom.getElementsByClassName("x-col-"+col_index);
8681         
8682         var h_row = this.el.dom.getElementsByClassName("x-hcol-"+col_index);
8683         
8684         
8685         for(var j = 0; j < w.length; j++) {
8686             
8687             if(!w[j]) {
8688                 continue;
8689             }
8690             
8691             var size_cls = w[j].split("-");
8692             
8693             if(!Number.isInteger(size_cls[1] * 1)) {
8694                 continue;
8695             }
8696             
8697             if(!this.colModel.config[col_index][size_cls[0]]) {
8698                 continue;
8699             }
8700             
8701             if(!h_row[0].classList.contains("col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]])) {
8702                 continue;
8703             }
8704             
8705             h_row[0].classList.replace(
8706                 "col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]],
8707                 "col-"+size_cls[0]+"-"+size_cls[1]
8708             );
8709             
8710             for(var i = 0; i < rows.length; i++) {
8711                 
8712                 var size_cls = w[j].split("-");
8713                 
8714                 if(!Number.isInteger(size_cls[1] * 1)) {
8715                     continue;
8716                 }
8717                 
8718                 if(!this.colModel.config[col_index][size_cls[0]]) {
8719                     continue;
8720                 }
8721                 
8722                 if(!rows[i].classList.contains("col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]])) {
8723                     continue;
8724                 }
8725                 
8726                 rows[i].classList.replace(
8727                     "col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]],
8728                     "col-"+size_cls[0]+"-"+size_cls[1]
8729                 );
8730             }
8731             
8732             this.colModel.config[col_index][size_cls[0]] = size_cls[1];
8733         }
8734     }
8735 });
8736
8737  
8738
8739  /*
8740  * - LGPL
8741  *
8742  * table cell
8743  * 
8744  */
8745
8746 /**
8747  * @class Roo.bootstrap.TableCell
8748  * @extends Roo.bootstrap.Component
8749  * Bootstrap TableCell class
8750  * @cfg {String} html cell contain text
8751  * @cfg {String} cls cell class
8752  * @cfg {String} tag cell tag (td|th) default td
8753  * @cfg {String} abbr Specifies an abbreviated version of the content in a cell
8754  * @cfg {String} align Aligns the content in a cell
8755  * @cfg {String} axis Categorizes cells
8756  * @cfg {String} bgcolor Specifies the background color of a cell
8757  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
8758  * @cfg {Number} colspan Specifies the number of columns a cell should span
8759  * @cfg {String} headers Specifies one or more header cells a cell is related to
8760  * @cfg {Number} height Sets the height of a cell
8761  * @cfg {String} nowrap Specifies that the content inside a cell should not wrap
8762  * @cfg {Number} rowspan Sets the number of rows a cell should span
8763  * @cfg {String} scope Defines a way to associate header cells and data cells in a table
8764  * @cfg {String} valign Vertical aligns the content in a cell
8765  * @cfg {Number} width Specifies the width of a cell
8766  * 
8767  * @constructor
8768  * Create a new TableCell
8769  * @param {Object} config The config object
8770  */
8771
8772 Roo.bootstrap.TableCell = function(config){
8773     Roo.bootstrap.TableCell.superclass.constructor.call(this, config);
8774 };
8775
8776 Roo.extend(Roo.bootstrap.TableCell, Roo.bootstrap.Component,  {
8777     
8778     html: false,
8779     cls: false,
8780     tag: false,
8781     abbr: false,
8782     align: false,
8783     axis: false,
8784     bgcolor: false,
8785     charoff: false,
8786     colspan: false,
8787     headers: false,
8788     height: false,
8789     nowrap: false,
8790     rowspan: false,
8791     scope: false,
8792     valign: false,
8793     width: false,
8794     
8795     
8796     getAutoCreate : function(){
8797         var cfg = Roo.apply({}, Roo.bootstrap.TableCell.superclass.getAutoCreate.call(this));
8798         
8799         cfg = {
8800             tag: 'td'
8801         };
8802         
8803         if(this.tag){
8804             cfg.tag = this.tag;
8805         }
8806         
8807         if (this.html) {
8808             cfg.html=this.html
8809         }
8810         if (this.cls) {
8811             cfg.cls=this.cls
8812         }
8813         if (this.abbr) {
8814             cfg.abbr=this.abbr
8815         }
8816         if (this.align) {
8817             cfg.align=this.align
8818         }
8819         if (this.axis) {
8820             cfg.axis=this.axis
8821         }
8822         if (this.bgcolor) {
8823             cfg.bgcolor=this.bgcolor
8824         }
8825         if (this.charoff) {
8826             cfg.charoff=this.charoff
8827         }
8828         if (this.colspan) {
8829             cfg.colspan=this.colspan
8830         }
8831         if (this.headers) {
8832             cfg.headers=this.headers
8833         }
8834         if (this.height) {
8835             cfg.height=this.height
8836         }
8837         if (this.nowrap) {
8838             cfg.nowrap=this.nowrap
8839         }
8840         if (this.rowspan) {
8841             cfg.rowspan=this.rowspan
8842         }
8843         if (this.scope) {
8844             cfg.scope=this.scope
8845         }
8846         if (this.valign) {
8847             cfg.valign=this.valign
8848         }
8849         if (this.width) {
8850             cfg.width=this.width
8851         }
8852         
8853         
8854         return cfg;
8855     }
8856    
8857 });
8858
8859  
8860
8861  /*
8862  * - LGPL
8863  *
8864  * table row
8865  * 
8866  */
8867
8868 /**
8869  * @class Roo.bootstrap.TableRow
8870  * @extends Roo.bootstrap.Component
8871  * Bootstrap TableRow class
8872  * @cfg {String} cls row class
8873  * @cfg {String} align Aligns the content in a table row
8874  * @cfg {String} bgcolor Specifies a background color for a table row
8875  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
8876  * @cfg {String} valign Vertical aligns the content in a table row
8877  * 
8878  * @constructor
8879  * Create a new TableRow
8880  * @param {Object} config The config object
8881  */
8882
8883 Roo.bootstrap.TableRow = function(config){
8884     Roo.bootstrap.TableRow.superclass.constructor.call(this, config);
8885 };
8886
8887 Roo.extend(Roo.bootstrap.TableRow, Roo.bootstrap.Component,  {
8888     
8889     cls: false,
8890     align: false,
8891     bgcolor: false,
8892     charoff: false,
8893     valign: false,
8894     
8895     getAutoCreate : function(){
8896         var cfg = Roo.apply({}, Roo.bootstrap.TableRow.superclass.getAutoCreate.call(this));
8897         
8898         cfg = {
8899             tag: 'tr'
8900         };
8901             
8902         if(this.cls){
8903             cfg.cls = this.cls;
8904         }
8905         if(this.align){
8906             cfg.align = this.align;
8907         }
8908         if(this.bgcolor){
8909             cfg.bgcolor = this.bgcolor;
8910         }
8911         if(this.charoff){
8912             cfg.charoff = this.charoff;
8913         }
8914         if(this.valign){
8915             cfg.valign = this.valign;
8916         }
8917         
8918         return cfg;
8919     }
8920    
8921 });
8922
8923  
8924
8925  /*
8926  * - LGPL
8927  *
8928  * table body
8929  * 
8930  */
8931
8932 /**
8933  * @class Roo.bootstrap.TableBody
8934  * @extends Roo.bootstrap.Component
8935  * Bootstrap TableBody class
8936  * @cfg {String} cls element class
8937  * @cfg {String} tag element tag (thead|tbody|tfoot) default tbody
8938  * @cfg {String} align Aligns the content inside the element
8939  * @cfg {Number} charoff Sets the number of characters the content inside the element will be aligned from the character specified by the char attribute
8940  * @cfg {String} valign Vertical aligns the content inside the <tbody> element
8941  * 
8942  * @constructor
8943  * Create a new TableBody
8944  * @param {Object} config The config object
8945  */
8946
8947 Roo.bootstrap.TableBody = function(config){
8948     Roo.bootstrap.TableBody.superclass.constructor.call(this, config);
8949 };
8950
8951 Roo.extend(Roo.bootstrap.TableBody, Roo.bootstrap.Component,  {
8952     
8953     cls: false,
8954     tag: false,
8955     align: false,
8956     charoff: false,
8957     valign: false,
8958     
8959     getAutoCreate : function(){
8960         var cfg = Roo.apply({}, Roo.bootstrap.TableBody.superclass.getAutoCreate.call(this));
8961         
8962         cfg = {
8963             tag: 'tbody'
8964         };
8965             
8966         if (this.cls) {
8967             cfg.cls=this.cls
8968         }
8969         if(this.tag){
8970             cfg.tag = this.tag;
8971         }
8972         
8973         if(this.align){
8974             cfg.align = this.align;
8975         }
8976         if(this.charoff){
8977             cfg.charoff = this.charoff;
8978         }
8979         if(this.valign){
8980             cfg.valign = this.valign;
8981         }
8982         
8983         return cfg;
8984     }
8985     
8986     
8987 //    initEvents : function()
8988 //    {
8989 //        
8990 //        if(!this.store){
8991 //            return;
8992 //        }
8993 //        
8994 //        this.store = Roo.factory(this.store, Roo.data);
8995 //        this.store.on('load', this.onLoad, this);
8996 //        
8997 //        this.store.load();
8998 //        
8999 //    },
9000 //    
9001 //    onLoad: function () 
9002 //    {   
9003 //        this.fireEvent('load', this);
9004 //    }
9005 //    
9006 //   
9007 });
9008
9009  
9010
9011  /*
9012  * Based on:
9013  * Ext JS Library 1.1.1
9014  * Copyright(c) 2006-2007, Ext JS, LLC.
9015  *
9016  * Originally Released Under LGPL - original licence link has changed is not relivant.
9017  *
9018  * Fork - LGPL
9019  * <script type="text/javascript">
9020  */
9021
9022 // as we use this in bootstrap.
9023 Roo.namespace('Roo.form');
9024  /**
9025  * @class Roo.form.Action
9026  * Internal Class used to handle form actions
9027  * @constructor
9028  * @param {Roo.form.BasicForm} el The form element or its id
9029  * @param {Object} config Configuration options
9030  */
9031
9032  
9033  
9034 // define the action interface
9035 Roo.form.Action = function(form, options){
9036     this.form = form;
9037     this.options = options || {};
9038 };
9039 /**
9040  * Client Validation Failed
9041  * @const 
9042  */
9043 Roo.form.Action.CLIENT_INVALID = 'client';
9044 /**
9045  * Server Validation Failed
9046  * @const 
9047  */
9048 Roo.form.Action.SERVER_INVALID = 'server';
9049  /**
9050  * Connect to Server Failed
9051  * @const 
9052  */
9053 Roo.form.Action.CONNECT_FAILURE = 'connect';
9054 /**
9055  * Reading Data from Server Failed
9056  * @const 
9057  */
9058 Roo.form.Action.LOAD_FAILURE = 'load';
9059
9060 Roo.form.Action.prototype = {
9061     type : 'default',
9062     failureType : undefined,
9063     response : undefined,
9064     result : undefined,
9065
9066     // interface method
9067     run : function(options){
9068
9069     },
9070
9071     // interface method
9072     success : function(response){
9073
9074     },
9075
9076     // interface method
9077     handleResponse : function(response){
9078
9079     },
9080
9081     // default connection failure
9082     failure : function(response){
9083         
9084         this.response = response;
9085         this.failureType = Roo.form.Action.CONNECT_FAILURE;
9086         this.form.afterAction(this, false);
9087     },
9088
9089     processResponse : function(response){
9090         this.response = response;
9091         if(!response.responseText){
9092             return true;
9093         }
9094         this.result = this.handleResponse(response);
9095         return this.result;
9096     },
9097
9098     // utility functions used internally
9099     getUrl : function(appendParams){
9100         var url = this.options.url || this.form.url || this.form.el.dom.action;
9101         if(appendParams){
9102             var p = this.getParams();
9103             if(p){
9104                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
9105             }
9106         }
9107         return url;
9108     },
9109
9110     getMethod : function(){
9111         return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
9112     },
9113
9114     getParams : function(){
9115         var bp = this.form.baseParams;
9116         var p = this.options.params;
9117         if(p){
9118             if(typeof p == "object"){
9119                 p = Roo.urlEncode(Roo.applyIf(p, bp));
9120             }else if(typeof p == 'string' && bp){
9121                 p += '&' + Roo.urlEncode(bp);
9122             }
9123         }else if(bp){
9124             p = Roo.urlEncode(bp);
9125         }
9126         return p;
9127     },
9128
9129     createCallback : function(){
9130         return {
9131             success: this.success,
9132             failure: this.failure,
9133             scope: this,
9134             timeout: (this.form.timeout*1000),
9135             upload: this.form.fileUpload ? this.success : undefined
9136         };
9137     }
9138 };
9139
9140 Roo.form.Action.Submit = function(form, options){
9141     Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
9142 };
9143
9144 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
9145     type : 'submit',
9146
9147     haveProgress : false,
9148     uploadComplete : false,
9149     
9150     // uploadProgress indicator.
9151     uploadProgress : function()
9152     {
9153         if (!this.form.progressUrl) {
9154             return;
9155         }
9156         
9157         if (!this.haveProgress) {
9158             Roo.MessageBox.progress("Uploading", "Uploading");
9159         }
9160         if (this.uploadComplete) {
9161            Roo.MessageBox.hide();
9162            return;
9163         }
9164         
9165         this.haveProgress = true;
9166    
9167         var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
9168         
9169         var c = new Roo.data.Connection();
9170         c.request({
9171             url : this.form.progressUrl,
9172             params: {
9173                 id : uid
9174             },
9175             method: 'GET',
9176             success : function(req){
9177                //console.log(data);
9178                 var rdata = false;
9179                 var edata;
9180                 try  {
9181                    rdata = Roo.decode(req.responseText)
9182                 } catch (e) {
9183                     Roo.log("Invalid data from server..");
9184                     Roo.log(edata);
9185                     return;
9186                 }
9187                 if (!rdata || !rdata.success) {
9188                     Roo.log(rdata);
9189                     Roo.MessageBox.alert(Roo.encode(rdata));
9190                     return;
9191                 }
9192                 var data = rdata.data;
9193                 
9194                 if (this.uploadComplete) {
9195                    Roo.MessageBox.hide();
9196                    return;
9197                 }
9198                    
9199                 if (data){
9200                     Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
9201                        Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
9202                     );
9203                 }
9204                 this.uploadProgress.defer(2000,this);
9205             },
9206        
9207             failure: function(data) {
9208                 Roo.log('progress url failed ');
9209                 Roo.log(data);
9210             },
9211             scope : this
9212         });
9213            
9214     },
9215     
9216     
9217     run : function()
9218     {
9219         // run get Values on the form, so it syncs any secondary forms.
9220         this.form.getValues();
9221         
9222         var o = this.options;
9223         var method = this.getMethod();
9224         var isPost = method == 'POST';
9225         if(o.clientValidation === false || this.form.isValid()){
9226             
9227             if (this.form.progressUrl) {
9228                 this.form.findField('UPLOAD_IDENTIFIER').setValue(
9229                     (new Date() * 1) + '' + Math.random());
9230                     
9231             } 
9232             
9233             
9234             Roo.Ajax.request(Roo.apply(this.createCallback(), {
9235                 form:this.form.el.dom,
9236                 url:this.getUrl(!isPost),
9237                 method: method,
9238                 params:isPost ? this.getParams() : null,
9239                 isUpload: this.form.fileUpload,
9240                 formData : this.form.formData
9241             }));
9242             
9243             this.uploadProgress();
9244
9245         }else if (o.clientValidation !== false){ // client validation failed
9246             this.failureType = Roo.form.Action.CLIENT_INVALID;
9247             this.form.afterAction(this, false);
9248         }
9249     },
9250
9251     success : function(response)
9252     {
9253         this.uploadComplete= true;
9254         if (this.haveProgress) {
9255             Roo.MessageBox.hide();
9256         }
9257         
9258         
9259         var result = this.processResponse(response);
9260         if(result === true || result.success){
9261             this.form.afterAction(this, true);
9262             return;
9263         }
9264         if(result.errors){
9265             this.form.markInvalid(result.errors);
9266             this.failureType = Roo.form.Action.SERVER_INVALID;
9267         }
9268         this.form.afterAction(this, false);
9269     },
9270     failure : function(response)
9271     {
9272         this.uploadComplete= true;
9273         if (this.haveProgress) {
9274             Roo.MessageBox.hide();
9275         }
9276         
9277         this.response = response;
9278         this.failureType = Roo.form.Action.CONNECT_FAILURE;
9279         this.form.afterAction(this, false);
9280     },
9281     
9282     handleResponse : function(response){
9283         if(this.form.errorReader){
9284             var rs = this.form.errorReader.read(response);
9285             var errors = [];
9286             if(rs.records){
9287                 for(var i = 0, len = rs.records.length; i < len; i++) {
9288                     var r = rs.records[i];
9289                     errors[i] = r.data;
9290                 }
9291             }
9292             if(errors.length < 1){
9293                 errors = null;
9294             }
9295             return {
9296                 success : rs.success,
9297                 errors : errors
9298             };
9299         }
9300         var ret = false;
9301         try {
9302             ret = Roo.decode(response.responseText);
9303         } catch (e) {
9304             ret = {
9305                 success: false,
9306                 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
9307                 errors : []
9308             };
9309         }
9310         return ret;
9311         
9312     }
9313 });
9314
9315
9316 Roo.form.Action.Load = function(form, options){
9317     Roo.form.Action.Load.superclass.constructor.call(this, form, options);
9318     this.reader = this.form.reader;
9319 };
9320
9321 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
9322     type : 'load',
9323
9324     run : function(){
9325         
9326         Roo.Ajax.request(Roo.apply(
9327                 this.createCallback(), {
9328                     method:this.getMethod(),
9329                     url:this.getUrl(false),
9330                     params:this.getParams()
9331         }));
9332     },
9333
9334     success : function(response){
9335         
9336         var result = this.processResponse(response);
9337         if(result === true || !result.success || !result.data){
9338             this.failureType = Roo.form.Action.LOAD_FAILURE;
9339             this.form.afterAction(this, false);
9340             return;
9341         }
9342         this.form.clearInvalid();
9343         this.form.setValues(result.data);
9344         this.form.afterAction(this, true);
9345     },
9346
9347     handleResponse : function(response){
9348         if(this.form.reader){
9349             var rs = this.form.reader.read(response);
9350             var data = rs.records && rs.records[0] ? rs.records[0].data : null;
9351             return {
9352                 success : rs.success,
9353                 data : data
9354             };
9355         }
9356         return Roo.decode(response.responseText);
9357     }
9358 });
9359
9360 Roo.form.Action.ACTION_TYPES = {
9361     'load' : Roo.form.Action.Load,
9362     'submit' : Roo.form.Action.Submit
9363 };/*
9364  * - LGPL
9365  *
9366  * form
9367  *
9368  */
9369
9370 /**
9371  * @class Roo.bootstrap.Form
9372  * @extends Roo.bootstrap.Component
9373  * Bootstrap Form class
9374  * @cfg {String} method  GET | POST (default POST)
9375  * @cfg {String} labelAlign top | left (default top)
9376  * @cfg {String} align left  | right - for navbars
9377  * @cfg {Boolean} loadMask load mask when submit (default true)
9378
9379  *
9380  * @constructor
9381  * Create a new Form
9382  * @param {Object} config The config object
9383  */
9384
9385
9386 Roo.bootstrap.Form = function(config){
9387     
9388     Roo.bootstrap.Form.superclass.constructor.call(this, config);
9389     
9390     Roo.bootstrap.Form.popover.apply();
9391     
9392     this.addEvents({
9393         /**
9394          * @event clientvalidation
9395          * If the monitorValid config option is true, this event fires repetitively to notify of valid state
9396          * @param {Form} this
9397          * @param {Boolean} valid true if the form has passed client-side validation
9398          */
9399         clientvalidation: true,
9400         /**
9401          * @event beforeaction
9402          * Fires before any action is performed. Return false to cancel the action.
9403          * @param {Form} this
9404          * @param {Action} action The action to be performed
9405          */
9406         beforeaction: true,
9407         /**
9408          * @event actionfailed
9409          * Fires when an action fails.
9410          * @param {Form} this
9411          * @param {Action} action The action that failed
9412          */
9413         actionfailed : true,
9414         /**
9415          * @event actioncomplete
9416          * Fires when an action is completed.
9417          * @param {Form} this
9418          * @param {Action} action The action that completed
9419          */
9420         actioncomplete : true
9421     });
9422 };
9423
9424 Roo.extend(Roo.bootstrap.Form, Roo.bootstrap.Component,  {
9425
9426      /**
9427      * @cfg {String} method
9428      * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
9429      */
9430     method : 'POST',
9431     /**
9432      * @cfg {String} url
9433      * The URL to use for form actions if one isn't supplied in the action options.
9434      */
9435     /**
9436      * @cfg {Boolean} fileUpload
9437      * Set to true if this form is a file upload.
9438      */
9439
9440     /**
9441      * @cfg {Object} baseParams
9442      * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
9443      */
9444
9445     /**
9446      * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
9447      */
9448     timeout: 30,
9449     /**
9450      * @cfg {Sting} align (left|right) for navbar forms
9451      */
9452     align : 'left',
9453
9454     // private
9455     activeAction : null,
9456
9457     /**
9458      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
9459      * element by passing it or its id or mask the form itself by passing in true.
9460      * @type Mixed
9461      */
9462     waitMsgTarget : false,
9463
9464     loadMask : true,
9465     
9466     /**
9467      * @cfg {Boolean} errorMask (true|false) default false
9468      */
9469     errorMask : false,
9470     
9471     /**
9472      * @cfg {Number} maskOffset Default 100
9473      */
9474     maskOffset : 100,
9475     
9476     /**
9477      * @cfg {Boolean} maskBody
9478      */
9479     maskBody : false,
9480
9481     getAutoCreate : function(){
9482
9483         var cfg = {
9484             tag: 'form',
9485             method : this.method || 'POST',
9486             id : this.id || Roo.id(),
9487             cls : ''
9488         };
9489         if (this.parent().xtype.match(/^Nav/)) {
9490             cfg.cls = 'navbar-form form-inline navbar-' + this.align;
9491
9492         }
9493
9494         if (this.labelAlign == 'left' ) {
9495             cfg.cls += ' form-horizontal';
9496         }
9497
9498
9499         return cfg;
9500     },
9501     initEvents : function()
9502     {
9503         this.el.on('submit', this.onSubmit, this);
9504         // this was added as random key presses on the form where triggering form submit.
9505         this.el.on('keypress', function(e) {
9506             if (e.getCharCode() != 13) {
9507                 return true;
9508             }
9509             // we might need to allow it for textareas.. and some other items.
9510             // check e.getTarget().
9511
9512             if(e.getTarget().nodeName.toLowerCase() === 'textarea'){
9513                 return true;
9514             }
9515
9516             Roo.log("keypress blocked");
9517
9518             e.preventDefault();
9519             return false;
9520         });
9521         
9522     },
9523     // private
9524     onSubmit : function(e){
9525         e.stopEvent();
9526     },
9527
9528      /**
9529      * Returns true if client-side validation on the form is successful.
9530      * @return Boolean
9531      */
9532     isValid : function(){
9533         var items = this.getItems();
9534         var valid = true;
9535         var target = false;
9536         
9537         items.each(function(f){
9538             
9539             if(f.validate()){
9540                 return;
9541             }
9542             
9543             Roo.log('invalid field: ' + f.name);
9544             
9545             valid = false;
9546
9547             if(!target && f.el.isVisible(true)){
9548                 target = f;
9549             }
9550            
9551         });
9552         
9553         if(this.errorMask && !valid){
9554             Roo.bootstrap.Form.popover.mask(this, target);
9555         }
9556         
9557         return valid;
9558     },
9559     
9560     /**
9561      * Returns true if any fields in this form have changed since their original load.
9562      * @return Boolean
9563      */
9564     isDirty : function(){
9565         var dirty = false;
9566         var items = this.getItems();
9567         items.each(function(f){
9568            if(f.isDirty()){
9569                dirty = true;
9570                return false;
9571            }
9572            return true;
9573         });
9574         return dirty;
9575     },
9576      /**
9577      * Performs a predefined action (submit or load) or custom actions you define on this form.
9578      * @param {String} actionName The name of the action type
9579      * @param {Object} options (optional) The options to pass to the action.  All of the config options listed
9580      * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
9581      * accept other config options):
9582      * <pre>
9583 Property          Type             Description
9584 ----------------  ---------------  ----------------------------------------------------------------------------------
9585 url               String           The url for the action (defaults to the form's url)
9586 method            String           The form method to use (defaults to the form's method, or POST if not defined)
9587 params            String/Object    The params to pass (defaults to the form's baseParams, or none if not defined)
9588 clientValidation  Boolean          Applies to submit only.  Pass true to call form.isValid() prior to posting to
9589                                    validate the form on the client (defaults to false)
9590      * </pre>
9591      * @return {BasicForm} this
9592      */
9593     doAction : function(action, options){
9594         if(typeof action == 'string'){
9595             action = new Roo.form.Action.ACTION_TYPES[action](this, options);
9596         }
9597         if(this.fireEvent('beforeaction', this, action) !== false){
9598             this.beforeAction(action);
9599             action.run.defer(100, action);
9600         }
9601         return this;
9602     },
9603
9604     // private
9605     beforeAction : function(action){
9606         var o = action.options;
9607         
9608         if(this.loadMask){
9609             
9610             if(this.maskBody){
9611                 Roo.get(document.body).mask(o.waitMsg || "Sending", 'x-mask-loading')
9612             } else {
9613                 this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
9614             }
9615         }
9616         // not really supported yet.. ??
9617
9618         //if(this.waitMsgTarget === true){
9619         //  this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
9620         //}else if(this.waitMsgTarget){
9621         //    this.waitMsgTarget = Roo.get(this.waitMsgTarget);
9622         //    this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
9623         //}else {
9624         //    Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
9625        // }
9626
9627     },
9628
9629     // private
9630     afterAction : function(action, success){
9631         this.activeAction = null;
9632         var o = action.options;
9633
9634         if(this.loadMask){
9635             
9636             if(this.maskBody){
9637                 Roo.get(document.body).unmask();
9638             } else {
9639                 this.el.unmask();
9640             }
9641         }
9642         
9643         //if(this.waitMsgTarget === true){
9644 //            this.el.unmask();
9645         //}else if(this.waitMsgTarget){
9646         //    this.waitMsgTarget.unmask();
9647         //}else{
9648         //    Roo.MessageBox.updateProgress(1);
9649         //    Roo.MessageBox.hide();
9650        // }
9651         //
9652         if(success){
9653             if(o.reset){
9654                 this.reset();
9655             }
9656             Roo.callback(o.success, o.scope, [this, action]);
9657             this.fireEvent('actioncomplete', this, action);
9658
9659         }else{
9660
9661             // failure condition..
9662             // we have a scenario where updates need confirming.
9663             // eg. if a locking scenario exists..
9664             // we look for { errors : { needs_confirm : true }} in the response.
9665             if (
9666                 (typeof(action.result) != 'undefined')  &&
9667                 (typeof(action.result.errors) != 'undefined')  &&
9668                 (typeof(action.result.errors.needs_confirm) != 'undefined')
9669            ){
9670                 var _t = this;
9671                 Roo.log("not supported yet");
9672                  /*
9673
9674                 Roo.MessageBox.confirm(
9675                     "Change requires confirmation",
9676                     action.result.errorMsg,
9677                     function(r) {
9678                         if (r != 'yes') {
9679                             return;
9680                         }
9681                         _t.doAction('submit', { params :  { _submit_confirmed : 1 } }  );
9682                     }
9683
9684                 );
9685                 */
9686
9687
9688                 return;
9689             }
9690
9691             Roo.callback(o.failure, o.scope, [this, action]);
9692             // show an error message if no failed handler is set..
9693             if (!this.hasListener('actionfailed')) {
9694                 Roo.log("need to add dialog support");
9695                 /*
9696                 Roo.MessageBox.alert("Error",
9697                     (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
9698                         action.result.errorMsg :
9699                         "Saving Failed, please check your entries or try again"
9700                 );
9701                 */
9702             }
9703
9704             this.fireEvent('actionfailed', this, action);
9705         }
9706
9707     },
9708     /**
9709      * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
9710      * @param {String} id The value to search for
9711      * @return Field
9712      */
9713     findField : function(id){
9714         var items = this.getItems();
9715         var field = items.get(id);
9716         if(!field){
9717              items.each(function(f){
9718                 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
9719                     field = f;
9720                     return false;
9721                 }
9722                 return true;
9723             });
9724         }
9725         return field || null;
9726     },
9727      /**
9728      * Mark fields in this form invalid in bulk.
9729      * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
9730      * @return {BasicForm} this
9731      */
9732     markInvalid : function(errors){
9733         if(errors instanceof Array){
9734             for(var i = 0, len = errors.length; i < len; i++){
9735                 var fieldError = errors[i];
9736                 var f = this.findField(fieldError.id);
9737                 if(f){
9738                     f.markInvalid(fieldError.msg);
9739                 }
9740             }
9741         }else{
9742             var field, id;
9743             for(id in errors){
9744                 if(typeof errors[id] != 'function' && (field = this.findField(id))){
9745                     field.markInvalid(errors[id]);
9746                 }
9747             }
9748         }
9749         //Roo.each(this.childForms || [], function (f) {
9750         //    f.markInvalid(errors);
9751         //});
9752
9753         return this;
9754     },
9755
9756     /**
9757      * Set values for fields in this form in bulk.
9758      * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
9759      * @return {BasicForm} this
9760      */
9761     setValues : function(values){
9762         if(values instanceof Array){ // array of objects
9763             for(var i = 0, len = values.length; i < len; i++){
9764                 var v = values[i];
9765                 var f = this.findField(v.id);
9766                 if(f){
9767                     f.setValue(v.value);
9768                     if(this.trackResetOnLoad){
9769                         f.originalValue = f.getValue();
9770                     }
9771                 }
9772             }
9773         }else{ // object hash
9774             var field, id;
9775             for(id in values){
9776                 if(typeof values[id] != 'function' && (field = this.findField(id))){
9777
9778                     if (field.setFromData &&
9779                         field.valueField &&
9780                         field.displayField &&
9781                         // combos' with local stores can
9782                         // be queried via setValue()
9783                         // to set their value..
9784                         (field.store && !field.store.isLocal)
9785                         ) {
9786                         // it's a combo
9787                         var sd = { };
9788                         sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
9789                         sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
9790                         field.setFromData(sd);
9791
9792                     } else if(field.setFromData && (field.store && !field.store.isLocal)) {
9793                         
9794                         field.setFromData(values);
9795                         
9796                     } else {
9797                         field.setValue(values[id]);
9798                     }
9799
9800
9801                     if(this.trackResetOnLoad){
9802                         field.originalValue = field.getValue();
9803                     }
9804                 }
9805             }
9806         }
9807
9808         //Roo.each(this.childForms || [], function (f) {
9809         //    f.setValues(values);
9810         //});
9811
9812         return this;
9813     },
9814
9815     /**
9816      * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
9817      * they are returned as an array.
9818      * @param {Boolean} asString
9819      * @return {Object}
9820      */
9821     getValues : function(asString){
9822         //if (this.childForms) {
9823             // copy values from the child forms
9824         //    Roo.each(this.childForms, function (f) {
9825         //        this.setValues(f.getValues());
9826         //    }, this);
9827         //}
9828
9829
9830
9831         var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
9832         if(asString === true){
9833             return fs;
9834         }
9835         return Roo.urlDecode(fs);
9836     },
9837
9838     /**
9839      * Returns the fields in this form as an object with key/value pairs.
9840      * This differs from getValues as it calls getValue on each child item, rather than using dom data.
9841      * @return {Object}
9842      */
9843     getFieldValues : function(with_hidden)
9844     {
9845         var items = this.getItems();
9846         var ret = {};
9847         items.each(function(f){
9848             
9849             if (!f.getName()) {
9850                 return;
9851             }
9852             
9853             var v = f.getValue();
9854             
9855             if (f.inputType =='radio') {
9856                 if (typeof(ret[f.getName()]) == 'undefined') {
9857                     ret[f.getName()] = ''; // empty..
9858                 }
9859
9860                 if (!f.el.dom.checked) {
9861                     return;
9862
9863                 }
9864                 v = f.el.dom.value;
9865
9866             }
9867             
9868             if(f.xtype == 'MoneyField'){
9869                 ret[f.currencyName] = f.getCurrency();
9870             }
9871
9872             // not sure if this supported any more..
9873             if ((typeof(v) == 'object') && f.getRawValue) {
9874                 v = f.getRawValue() ; // dates..
9875             }
9876             // combo boxes where name != hiddenName...
9877             if (f.name !== false && f.name != '' && f.name != f.getName()) {
9878                 ret[f.name] = f.getRawValue();
9879             }
9880             ret[f.getName()] = v;
9881         });
9882
9883         return ret;
9884     },
9885
9886     /**
9887      * Clears all invalid messages in this form.
9888      * @return {BasicForm} this
9889      */
9890     clearInvalid : function(){
9891         var items = this.getItems();
9892
9893         items.each(function(f){
9894            f.clearInvalid();
9895         });
9896
9897         return this;
9898     },
9899
9900     /**
9901      * Resets this form.
9902      * @return {BasicForm} this
9903      */
9904     reset : function(){
9905         var items = this.getItems();
9906         items.each(function(f){
9907             f.reset();
9908         });
9909
9910         Roo.each(this.childForms || [], function (f) {
9911             f.reset();
9912         });
9913
9914
9915         return this;
9916     },
9917     
9918     getItems : function()
9919     {
9920         var r=new Roo.util.MixedCollection(false, function(o){
9921             return o.id || (o.id = Roo.id());
9922         });
9923         var iter = function(el) {
9924             if (el.inputEl) {
9925                 r.add(el);
9926             }
9927             if (!el.items) {
9928                 return;
9929             }
9930             Roo.each(el.items,function(e) {
9931                 iter(e);
9932             });
9933         };
9934
9935         iter(this);
9936         return r;
9937     },
9938     
9939     hideFields : function(items)
9940     {
9941         Roo.each(items, function(i){
9942             
9943             var f = this.findField(i);
9944             
9945             if(!f){
9946                 return;
9947             }
9948             
9949             f.hide();
9950             
9951         }, this);
9952     },
9953     
9954     showFields : function(items)
9955     {
9956         Roo.each(items, function(i){
9957             
9958             var f = this.findField(i);
9959             
9960             if(!f){
9961                 return;
9962             }
9963             
9964             f.show();
9965             
9966         }, this);
9967     }
9968
9969 });
9970
9971 Roo.apply(Roo.bootstrap.Form, {
9972     
9973     popover : {
9974         
9975         padding : 5,
9976         
9977         isApplied : false,
9978         
9979         isMasked : false,
9980         
9981         form : false,
9982         
9983         target : false,
9984         
9985         toolTip : false,
9986         
9987         intervalID : false,
9988         
9989         maskEl : false,
9990         
9991         apply : function()
9992         {
9993             if(this.isApplied){
9994                 return;
9995             }
9996             
9997             this.maskEl = {
9998                 top : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-top-mask" }, true),
9999                 left : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-left-mask" }, true),
10000                 bottom : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-bottom-mask" }, true),
10001                 right : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-right-mask" }, true)
10002             };
10003             
10004             this.maskEl.top.enableDisplayMode("block");
10005             this.maskEl.left.enableDisplayMode("block");
10006             this.maskEl.bottom.enableDisplayMode("block");
10007             this.maskEl.right.enableDisplayMode("block");
10008             
10009             this.toolTip = new Roo.bootstrap.Tooltip({
10010                 cls : 'roo-form-error-popover',
10011                 alignment : {
10012                     'left' : ['r-l', [-2,0], 'right'],
10013                     'right' : ['l-r', [2,0], 'left'],
10014                     'bottom' : ['tl-bl', [0,2], 'top'],
10015                     'top' : [ 'bl-tl', [0,-2], 'bottom']
10016                 }
10017             });
10018             
10019             this.toolTip.render(Roo.get(document.body));
10020
10021             this.toolTip.el.enableDisplayMode("block");
10022             
10023             Roo.get(document.body).on('click', function(){
10024                 this.unmask();
10025             }, this);
10026             
10027             Roo.get(document.body).on('touchstart', function(){
10028                 this.unmask();
10029             }, this);
10030             
10031             this.isApplied = true
10032         },
10033         
10034         mask : function(form, target)
10035         {
10036             this.form = form;
10037             
10038             this.target = target;
10039             
10040             if(!this.form.errorMask || !target.el){
10041                 return;
10042             }
10043             
10044             var scrollable = this.target.el.findScrollableParent() || this.target.el.findParent('div.modal', 100, true) || Roo.get(document.body);
10045             
10046             Roo.log(scrollable);
10047             
10048             var ot = this.target.el.calcOffsetsTo(scrollable);
10049             
10050             var scrollTo = ot[1] - this.form.maskOffset;
10051             
10052             scrollTo = Math.min(scrollTo, scrollable.dom.scrollHeight);
10053             
10054             scrollable.scrollTo('top', scrollTo);
10055             
10056             var box = this.target.el.getBox();
10057             Roo.log(box);
10058             var zIndex = Roo.bootstrap.Modal.zIndex++;
10059
10060             
10061             this.maskEl.top.setStyle('position', 'absolute');
10062             this.maskEl.top.setStyle('z-index', zIndex);
10063             this.maskEl.top.setSize(Roo.lib.Dom.getDocumentWidth(), box.y - this.padding);
10064             this.maskEl.top.setLeft(0);
10065             this.maskEl.top.setTop(0);
10066             this.maskEl.top.show();
10067             
10068             this.maskEl.left.setStyle('position', 'absolute');
10069             this.maskEl.left.setStyle('z-index', zIndex);
10070             this.maskEl.left.setSize(box.x - this.padding, box.height + this.padding * 2);
10071             this.maskEl.left.setLeft(0);
10072             this.maskEl.left.setTop(box.y - this.padding);
10073             this.maskEl.left.show();
10074
10075             this.maskEl.bottom.setStyle('position', 'absolute');
10076             this.maskEl.bottom.setStyle('z-index', zIndex);
10077             this.maskEl.bottom.setSize(Roo.lib.Dom.getDocumentWidth(), Roo.lib.Dom.getDocumentHeight() - box.bottom - this.padding);
10078             this.maskEl.bottom.setLeft(0);
10079             this.maskEl.bottom.setTop(box.bottom + this.padding);
10080             this.maskEl.bottom.show();
10081
10082             this.maskEl.right.setStyle('position', 'absolute');
10083             this.maskEl.right.setStyle('z-index', zIndex);
10084             this.maskEl.right.setSize(Roo.lib.Dom.getDocumentWidth() - box.right - this.padding, box.height + this.padding * 2);
10085             this.maskEl.right.setLeft(box.right + this.padding);
10086             this.maskEl.right.setTop(box.y - this.padding);
10087             this.maskEl.right.show();
10088
10089             this.toolTip.bindEl = this.target.el;
10090
10091             this.toolTip.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
10092
10093             var tip = this.target.blankText;
10094
10095             if(this.target.getValue() !== '' ) {
10096                 
10097                 if (this.target.invalidText.length) {
10098                     tip = this.target.invalidText;
10099                 } else if (this.target.regexText.length){
10100                     tip = this.target.regexText;
10101                 }
10102             }
10103
10104             this.toolTip.show(tip);
10105
10106             this.intervalID = window.setInterval(function() {
10107                 Roo.bootstrap.Form.popover.unmask();
10108             }, 10000);
10109
10110             window.onwheel = function(){ return false;};
10111             
10112             (function(){ this.isMasked = true; }).defer(500, this);
10113             
10114         },
10115         
10116         unmask : function()
10117         {
10118             if(!this.isApplied || !this.isMasked || !this.form || !this.target || !this.form.errorMask){
10119                 return;
10120             }
10121             
10122             this.maskEl.top.setStyle('position', 'absolute');
10123             this.maskEl.top.setSize(0, 0).setXY([0, 0]);
10124             this.maskEl.top.hide();
10125
10126             this.maskEl.left.setStyle('position', 'absolute');
10127             this.maskEl.left.setSize(0, 0).setXY([0, 0]);
10128             this.maskEl.left.hide();
10129
10130             this.maskEl.bottom.setStyle('position', 'absolute');
10131             this.maskEl.bottom.setSize(0, 0).setXY([0, 0]);
10132             this.maskEl.bottom.hide();
10133
10134             this.maskEl.right.setStyle('position', 'absolute');
10135             this.maskEl.right.setSize(0, 0).setXY([0, 0]);
10136             this.maskEl.right.hide();
10137             
10138             this.toolTip.hide();
10139             
10140             this.toolTip.el.hide();
10141             
10142             window.onwheel = function(){ return true;};
10143             
10144             if(this.intervalID){
10145                 window.clearInterval(this.intervalID);
10146                 this.intervalID = false;
10147             }
10148             
10149             this.isMasked = false;
10150             
10151         }
10152         
10153     }
10154     
10155 });
10156
10157 /*
10158  * Based on:
10159  * Ext JS Library 1.1.1
10160  * Copyright(c) 2006-2007, Ext JS, LLC.
10161  *
10162  * Originally Released Under LGPL - original licence link has changed is not relivant.
10163  *
10164  * Fork - LGPL
10165  * <script type="text/javascript">
10166  */
10167 /**
10168  * @class Roo.form.VTypes
10169  * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
10170  * @singleton
10171  */
10172 Roo.form.VTypes = function(){
10173     // closure these in so they are only created once.
10174     var alpha = /^[a-zA-Z_]+$/;
10175     var alphanum = /^[a-zA-Z0-9_]+$/;
10176     var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,24}$/;
10177     var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
10178
10179     // All these messages and functions are configurable
10180     return {
10181         /**
10182          * The function used to validate email addresses
10183          * @param {String} value The email address
10184          */
10185         'email' : function(v){
10186             return email.test(v);
10187         },
10188         /**
10189          * The error text to display when the email validation function returns false
10190          * @type String
10191          */
10192         'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
10193         /**
10194          * The keystroke filter mask to be applied on email input
10195          * @type RegExp
10196          */
10197         'emailMask' : /[a-z0-9_\.\-@]/i,
10198
10199         /**
10200          * The function used to validate URLs
10201          * @param {String} value The URL
10202          */
10203         'url' : function(v){
10204             return url.test(v);
10205         },
10206         /**
10207          * The error text to display when the url validation function returns false
10208          * @type String
10209          */
10210         'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
10211         
10212         /**
10213          * The function used to validate alpha values
10214          * @param {String} value The value
10215          */
10216         'alpha' : function(v){
10217             return alpha.test(v);
10218         },
10219         /**
10220          * The error text to display when the alpha validation function returns false
10221          * @type String
10222          */
10223         'alphaText' : 'This field should only contain letters and _',
10224         /**
10225          * The keystroke filter mask to be applied on alpha input
10226          * @type RegExp
10227          */
10228         'alphaMask' : /[a-z_]/i,
10229
10230         /**
10231          * The function used to validate alphanumeric values
10232          * @param {String} value The value
10233          */
10234         'alphanum' : function(v){
10235             return alphanum.test(v);
10236         },
10237         /**
10238          * The error text to display when the alphanumeric validation function returns false
10239          * @type String
10240          */
10241         'alphanumText' : 'This field should only contain letters, numbers and _',
10242         /**
10243          * The keystroke filter mask to be applied on alphanumeric input
10244          * @type RegExp
10245          */
10246         'alphanumMask' : /[a-z0-9_]/i
10247     };
10248 }();/*
10249  * - LGPL
10250  *
10251  * Input
10252  * 
10253  */
10254
10255 /**
10256  * @class Roo.bootstrap.Input
10257  * @extends Roo.bootstrap.Component
10258  * Bootstrap Input class
10259  * @cfg {Boolean} disabled is it disabled
10260  * @cfg {String} (button|checkbox|email|file|hidden|image|number|password|radio|range|reset|search|submit|text) inputType 
10261  * @cfg {String} name name of the input
10262  * @cfg {string} fieldLabel - the label associated
10263  * @cfg {string} placeholder - placeholder to put in text.
10264  * @cfg {string}  before - input group add on before
10265  * @cfg {string} after - input group add on after
10266  * @cfg {string} size - (lg|sm) or leave empty..
10267  * @cfg {Number} xs colspan out of 12 for mobile-sized screens
10268  * @cfg {Number} sm colspan out of 12 for tablet-sized screens
10269  * @cfg {Number} md colspan out of 12 for computer-sized screens
10270  * @cfg {Number} lg colspan out of 12 for large computer-sized screens
10271  * @cfg {string} value default value of the input
10272  * @cfg {Number} labelWidth set the width of label 
10273  * @cfg {Number} labellg set the width of label (1-12)
10274  * @cfg {Number} labelmd set the width of label (1-12)
10275  * @cfg {Number} labelsm set the width of label (1-12)
10276  * @cfg {Number} labelxs set the width of label (1-12)
10277  * @cfg {String} labelAlign (top|left)
10278  * @cfg {Boolean} readOnly Specifies that the field should be read-only
10279  * @cfg {String} autocomplete - default is new-password see: https://developers.google.com/web/fundamentals/input/form/label-and-name-inputs?hl=en
10280  * @cfg {String} indicatorpos (left|right) default left
10281  * @cfg {String} capture (user|camera) use for file input only. (default empty)
10282  * @cfg {String} accept (image|video|audio) use for file input only. (default empty)
10283  * @cfg {Boolean} preventMark Do not show tick or cross if error/success
10284
10285  * @cfg {String} align (left|center|right) Default left
10286  * @cfg {Boolean} forceFeedback (true|false) Default false
10287  * 
10288  * @constructor
10289  * Create a new Input
10290  * @param {Object} config The config object
10291  */
10292
10293 Roo.bootstrap.Input = function(config){
10294     
10295     Roo.bootstrap.Input.superclass.constructor.call(this, config);
10296     
10297     this.addEvents({
10298         /**
10299          * @event focus
10300          * Fires when this field receives input focus.
10301          * @param {Roo.form.Field} this
10302          */
10303         focus : true,
10304         /**
10305          * @event blur
10306          * Fires when this field loses input focus.
10307          * @param {Roo.form.Field} this
10308          */
10309         blur : true,
10310         /**
10311          * @event specialkey
10312          * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
10313          * {@link Roo.EventObject#getKey} to determine which key was pressed.
10314          * @param {Roo.form.Field} this
10315          * @param {Roo.EventObject} e The event object
10316          */
10317         specialkey : true,
10318         /**
10319          * @event change
10320          * Fires just before the field blurs if the field value has changed.
10321          * @param {Roo.form.Field} this
10322          * @param {Mixed} newValue The new value
10323          * @param {Mixed} oldValue The original value
10324          */
10325         change : true,
10326         /**
10327          * @event invalid
10328          * Fires after the field has been marked as invalid.
10329          * @param {Roo.form.Field} this
10330          * @param {String} msg The validation message
10331          */
10332         invalid : true,
10333         /**
10334          * @event valid
10335          * Fires after the field has been validated with no errors.
10336          * @param {Roo.form.Field} this
10337          */
10338         valid : true,
10339          /**
10340          * @event keyup
10341          * Fires after the key up
10342          * @param {Roo.form.Field} this
10343          * @param {Roo.EventObject}  e The event Object
10344          */
10345         keyup : true
10346     });
10347 };
10348
10349 Roo.extend(Roo.bootstrap.Input, Roo.bootstrap.Component,  {
10350      /**
10351      * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
10352       automatic validation (defaults to "keyup").
10353      */
10354     validationEvent : "keyup",
10355      /**
10356      * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
10357      */
10358     validateOnBlur : true,
10359     /**
10360      * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
10361      */
10362     validationDelay : 250,
10363      /**
10364      * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
10365      */
10366     focusClass : "x-form-focus",  // not needed???
10367     
10368        
10369     /**
10370      * @cfg {String} invalidClass DEPRICATED - code uses BS4 - is-valid / is-invalid
10371      */
10372     invalidClass : "has-warning",
10373     
10374     /**
10375      * @cfg {String} validClass DEPRICATED - code uses BS4 - is-valid / is-invalid
10376      */
10377     validClass : "has-success",
10378     
10379     /**
10380      * @cfg {Boolean} hasFeedback (true|false) default true
10381      */
10382     hasFeedback : true,
10383     
10384     /**
10385      * @cfg {String} invalidFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
10386      */
10387     invalidFeedbackClass : "glyphicon-warning-sign",
10388     
10389     /**
10390      * @cfg {String} validFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
10391      */
10392     validFeedbackClass : "glyphicon-ok",
10393     
10394     /**
10395      * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
10396      */
10397     selectOnFocus : false,
10398     
10399      /**
10400      * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
10401      */
10402     maskRe : null,
10403        /**
10404      * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
10405      */
10406     vtype : null,
10407     
10408       /**
10409      * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
10410      */
10411     disableKeyFilter : false,
10412     
10413        /**
10414      * @cfg {Boolean} disabled True to disable the field (defaults to false).
10415      */
10416     disabled : false,
10417      /**
10418      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
10419      */
10420     allowBlank : true,
10421     /**
10422      * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
10423      */
10424     blankText : "Please complete this mandatory field",
10425     
10426      /**
10427      * @cfg {Number} minLength Minimum input field length required (defaults to 0)
10428      */
10429     minLength : 0,
10430     /**
10431      * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
10432      */
10433     maxLength : Number.MAX_VALUE,
10434     /**
10435      * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
10436      */
10437     minLengthText : "The minimum length for this field is {0}",
10438     /**
10439      * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
10440      */
10441     maxLengthText : "The maximum length for this field is {0}",
10442   
10443     
10444     /**
10445      * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
10446      * If available, this function will be called only after the basic validators all return true, and will be passed the
10447      * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
10448      */
10449     validator : null,
10450     /**
10451      * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
10452      * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
10453      * current field value.  If the test fails, the field will be marked invalid using {@link #regexText}.
10454      */
10455     regex : null,
10456     /**
10457      * @cfg {String} regexText -- Depricated - use Invalid Text
10458      */
10459     regexText : "",
10460     
10461     /**
10462      * @cfg {String} invalidText The error text to display if {@link #validator} test fails during validation (defaults to "")
10463      */
10464     invalidText : "",
10465     
10466     
10467     
10468     autocomplete: false,
10469     
10470     
10471     fieldLabel : '',
10472     inputType : 'text',
10473     
10474     name : false,
10475     placeholder: false,
10476     before : false,
10477     after : false,
10478     size : false,
10479     hasFocus : false,
10480     preventMark: false,
10481     isFormField : true,
10482     value : '',
10483     labelWidth : 2,
10484     labelAlign : false,
10485     readOnly : false,
10486     align : false,
10487     formatedValue : false,
10488     forceFeedback : false,
10489     
10490     indicatorpos : 'left',
10491     
10492     labellg : 0,
10493     labelmd : 0,
10494     labelsm : 0,
10495     labelxs : 0,
10496     
10497     capture : '',
10498     accept : '',
10499     
10500     parentLabelAlign : function()
10501     {
10502         var parent = this;
10503         while (parent.parent()) {
10504             parent = parent.parent();
10505             if (typeof(parent.labelAlign) !='undefined') {
10506                 return parent.labelAlign;
10507             }
10508         }
10509         return 'left';
10510         
10511     },
10512     
10513     getAutoCreate : function()
10514     {
10515         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
10516         
10517         var id = Roo.id();
10518         
10519         var cfg = {};
10520         
10521         if(this.inputType != 'hidden'){
10522             cfg.cls = 'form-group' //input-group
10523         }
10524         
10525         var input =  {
10526             tag: 'input',
10527             id : id,
10528             type : this.inputType,
10529             value : this.value,
10530             cls : 'form-control',
10531             placeholder : this.placeholder || '',
10532             autocomplete : this.autocomplete || 'new-password'
10533         };
10534         if (this.inputType == 'file') {
10535             input.style = 'overflow:hidden'; // why not in CSS?
10536         }
10537         
10538         if(this.capture.length){
10539             input.capture = this.capture;
10540         }
10541         
10542         if(this.accept.length){
10543             input.accept = this.accept + "/*";
10544         }
10545         
10546         if(this.align){
10547             input.style = (typeof(input.style) == 'undefined') ? ('text-align:' + this.align) : (input.style + 'text-align:' + this.align);
10548         }
10549         
10550         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
10551             input.maxLength = this.maxLength;
10552         }
10553         
10554         if (this.disabled) {
10555             input.disabled=true;
10556         }
10557         
10558         if (this.readOnly) {
10559             input.readonly=true;
10560         }
10561         
10562         if (this.name) {
10563             input.name = this.name;
10564         }
10565         
10566         if (this.size) {
10567             input.cls += ' input-' + this.size;
10568         }
10569         
10570         var settings=this;
10571         ['xs','sm','md','lg'].map(function(size){
10572             if (settings[size]) {
10573                 cfg.cls += ' col-' + size + '-' + settings[size];
10574             }
10575         });
10576         
10577         var inputblock = input;
10578         
10579         var feedback = {
10580             tag: 'span',
10581             cls: 'glyphicon form-control-feedback'
10582         };
10583             
10584         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
10585             
10586             inputblock = {
10587                 cls : 'has-feedback',
10588                 cn :  [
10589                     input,
10590                     feedback
10591                 ] 
10592             };  
10593         }
10594         
10595         if (this.before || this.after) {
10596             
10597             inputblock = {
10598                 cls : 'input-group',
10599                 cn :  [] 
10600             };
10601             
10602             if (this.before && typeof(this.before) == 'string') {
10603                 
10604                 inputblock.cn.push({
10605                     tag :'span',
10606                     cls : 'roo-input-before input-group-addon input-group-prepend input-group-text',
10607                     html : this.before
10608                 });
10609             }
10610             if (this.before && typeof(this.before) == 'object') {
10611                 this.before = Roo.factory(this.before);
10612                 
10613                 inputblock.cn.push({
10614                     tag :'span',
10615                     cls : 'roo-input-before input-group-prepend   input-group-' +
10616                         (this.before.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
10617                 });
10618             }
10619             
10620             inputblock.cn.push(input);
10621             
10622             if (this.after && typeof(this.after) == 'string') {
10623                 inputblock.cn.push({
10624                     tag :'span',
10625                     cls : 'roo-input-after input-group-append input-group-text input-group-addon',
10626                     html : this.after
10627                 });
10628             }
10629             if (this.after && typeof(this.after) == 'object') {
10630                 this.after = Roo.factory(this.after);
10631                 
10632                 inputblock.cn.push({
10633                     tag :'span',
10634                     cls : 'roo-input-after input-group-append  input-group-' +
10635                         (this.after.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
10636                 });
10637             }
10638             
10639             if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
10640                 inputblock.cls += ' has-feedback';
10641                 inputblock.cn.push(feedback);
10642             }
10643         };
10644         var indicator = {
10645             tag : 'i',
10646             cls : 'roo-required-indicator ' + (this.indicatorpos == 'right'  ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
10647             tooltip : 'This field is required'
10648         };
10649         if (this.allowBlank ) {
10650             indicator.style = this.allowBlank ? ' display:none' : '';
10651         }
10652         if (align ==='left' && this.fieldLabel.length) {
10653             
10654             cfg.cls += ' roo-form-group-label-left'  + (Roo.bootstrap.version == 4 ? ' row' : '');
10655             
10656             cfg.cn = [
10657                 indicator,
10658                 {
10659                     tag: 'label',
10660                     'for' :  id,
10661                     cls : 'control-label col-form-label',
10662                     html : this.fieldLabel
10663
10664                 },
10665                 {
10666                     cls : "", 
10667                     cn: [
10668                         inputblock
10669                     ]
10670                 }
10671             ];
10672             
10673             var labelCfg = cfg.cn[1];
10674             var contentCfg = cfg.cn[2];
10675             
10676             if(this.indicatorpos == 'right'){
10677                 cfg.cn = [
10678                     {
10679                         tag: 'label',
10680                         'for' :  id,
10681                         cls : 'control-label col-form-label',
10682                         cn : [
10683                             {
10684                                 tag : 'span',
10685                                 html : this.fieldLabel
10686                             },
10687                             indicator
10688                         ]
10689                     },
10690                     {
10691                         cls : "",
10692                         cn: [
10693                             inputblock
10694                         ]
10695                     }
10696
10697                 ];
10698                 
10699                 labelCfg = cfg.cn[0];
10700                 contentCfg = cfg.cn[1];
10701             
10702             }
10703             
10704             if(this.labelWidth > 12){
10705                 labelCfg.style = "width: " + this.labelWidth + 'px';
10706             }
10707             
10708             if(this.labelWidth < 13 && this.labelmd == 0){
10709                 this.labelmd = this.labelWidth;
10710             }
10711             
10712             if(this.labellg > 0){
10713                 labelCfg.cls += ' col-lg-' + this.labellg;
10714                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
10715             }
10716             
10717             if(this.labelmd > 0){
10718                 labelCfg.cls += ' col-md-' + this.labelmd;
10719                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
10720             }
10721             
10722             if(this.labelsm > 0){
10723                 labelCfg.cls += ' col-sm-' + this.labelsm;
10724                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
10725             }
10726             
10727             if(this.labelxs > 0){
10728                 labelCfg.cls += ' col-xs-' + this.labelxs;
10729                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
10730             }
10731             
10732             
10733         } else if ( this.fieldLabel.length) {
10734                 
10735             
10736             
10737             cfg.cn = [
10738                 {
10739                     tag : 'i',
10740                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
10741                     tooltip : 'This field is required',
10742                     style : this.allowBlank ? ' display:none' : '' 
10743                 },
10744                 {
10745                     tag: 'label',
10746                    //cls : 'input-group-addon',
10747                     html : this.fieldLabel
10748
10749                 },
10750
10751                inputblock
10752
10753            ];
10754            
10755            if(this.indicatorpos == 'right'){
10756        
10757                 cfg.cn = [
10758                     {
10759                         tag: 'label',
10760                        //cls : 'input-group-addon',
10761                         html : this.fieldLabel
10762
10763                     },
10764                     {
10765                         tag : 'i',
10766                         cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
10767                         tooltip : 'This field is required',
10768                         style : this.allowBlank ? ' display:none' : '' 
10769                     },
10770
10771                    inputblock
10772
10773                ];
10774
10775             }
10776
10777         } else {
10778             
10779             cfg.cn = [
10780
10781                     inputblock
10782
10783             ];
10784                 
10785                 
10786         };
10787         
10788         if (this.parentType === 'Navbar' &&  this.parent().bar) {
10789            cfg.cls += ' navbar-form';
10790         }
10791         
10792         if (this.parentType === 'NavGroup' && !(Roo.bootstrap.version == 4 && this.parent().form)) {
10793             // on BS4 we do this only if not form 
10794             cfg.cls += ' navbar-form';
10795             cfg.tag = 'li';
10796         }
10797         
10798         return cfg;
10799         
10800     },
10801     /**
10802      * return the real input element.
10803      */
10804     inputEl: function ()
10805     {
10806         return this.el.select('input.form-control',true).first();
10807     },
10808     
10809     tooltipEl : function()
10810     {
10811         return this.inputEl();
10812     },
10813     
10814     indicatorEl : function()
10815     {
10816         if (Roo.bootstrap.version == 4) {
10817             return false; // not enabled in v4 yet.
10818         }
10819         
10820         var indicator = this.el.select('i.roo-required-indicator',true).first();
10821         
10822         if(!indicator){
10823             return false;
10824         }
10825         
10826         return indicator;
10827         
10828     },
10829     
10830     setDisabled : function(v)
10831     {
10832         var i  = this.inputEl().dom;
10833         if (!v) {
10834             i.removeAttribute('disabled');
10835             return;
10836             
10837         }
10838         i.setAttribute('disabled','true');
10839     },
10840     initEvents : function()
10841     {
10842           
10843         this.inputEl().on("keydown" , this.fireKey,  this);
10844         this.inputEl().on("focus", this.onFocus,  this);
10845         this.inputEl().on("blur", this.onBlur,  this);
10846         
10847         this.inputEl().relayEvent('keyup', this);
10848         
10849         this.indicator = this.indicatorEl();
10850         
10851         if(this.indicator){
10852             this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible'); // changed from invisible??? - 
10853         }
10854  
10855         // reference to original value for reset
10856         this.originalValue = this.getValue();
10857         //Roo.form.TextField.superclass.initEvents.call(this);
10858         if(this.validationEvent == 'keyup'){
10859             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
10860             this.inputEl().on('keyup', this.filterValidation, this);
10861         }
10862         else if(this.validationEvent !== false){
10863             this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
10864         }
10865         
10866         if(this.selectOnFocus){
10867             this.on("focus", this.preFocus, this);
10868             
10869         }
10870         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
10871             this.inputEl().on("keypress", this.filterKeys, this);
10872         } else {
10873             this.inputEl().relayEvent('keypress', this);
10874         }
10875        /* if(this.grow){
10876             this.el.on("keyup", this.onKeyUp,  this, {buffer:50});
10877             this.el.on("click", this.autoSize,  this);
10878         }
10879         */
10880         if(this.inputEl().is('input[type=password]') && Roo.isSafari){
10881             this.inputEl().on('keydown', this.SafariOnKeyDown, this);
10882         }
10883         
10884         if (typeof(this.before) == 'object') {
10885             this.before.render(this.el.select('.roo-input-before',true).first());
10886         }
10887         if (typeof(this.after) == 'object') {
10888             this.after.render(this.el.select('.roo-input-after',true).first());
10889         }
10890         
10891         this.inputEl().on('change', this.onChange, this);
10892         
10893     },
10894     filterValidation : function(e){
10895         if(!e.isNavKeyPress()){
10896             this.validationTask.delay(this.validationDelay);
10897         }
10898     },
10899      /**
10900      * Validates the field value
10901      * @return {Boolean} True if the value is valid, else false
10902      */
10903     validate : function(){
10904         //if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
10905         if(this.disabled || this.validateValue(this.getRawValue())){
10906             this.markValid();
10907             return true;
10908         }
10909         
10910         this.markInvalid();
10911         return false;
10912     },
10913     
10914     
10915     /**
10916      * Validates a value according to the field's validation rules and marks the field as invalid
10917      * if the validation fails
10918      * @param {Mixed} value The value to validate
10919      * @return {Boolean} True if the value is valid, else false
10920      */
10921     validateValue : function(value)
10922     {
10923         if(this.getVisibilityEl().hasClass('hidden')){
10924             return true;
10925         }
10926         
10927         if(value.length < 1)  { // if it's blank
10928             if(this.allowBlank){
10929                 return true;
10930             }
10931             return false;
10932         }
10933         
10934         if(value.length < this.minLength){
10935             return false;
10936         }
10937         if(value.length > this.maxLength){
10938             return false;
10939         }
10940         if(this.vtype){
10941             var vt = Roo.form.VTypes;
10942             if(!vt[this.vtype](value, this)){
10943                 return false;
10944             }
10945         }
10946         if(typeof this.validator == "function"){
10947             var msg = this.validator(value);
10948             if(msg !== true){
10949                 return false;
10950             }
10951             if (typeof(msg) == 'string') {
10952                 this.invalidText = msg;
10953             }
10954         }
10955         
10956         if(this.regex && !this.regex.test(value)){
10957             return false;
10958         }
10959         
10960         return true;
10961     },
10962     
10963      // private
10964     fireKey : function(e){
10965         //Roo.log('field ' + e.getKey());
10966         if(e.isNavKeyPress()){
10967             this.fireEvent("specialkey", this, e);
10968         }
10969     },
10970     focus : function (selectText){
10971         if(this.rendered){
10972             this.inputEl().focus();
10973             if(selectText === true){
10974                 this.inputEl().dom.select();
10975             }
10976         }
10977         return this;
10978     } ,
10979     
10980     onFocus : function(){
10981         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
10982            // this.el.addClass(this.focusClass);
10983         }
10984         if(!this.hasFocus){
10985             this.hasFocus = true;
10986             this.startValue = this.getValue();
10987             this.fireEvent("focus", this);
10988         }
10989     },
10990     
10991     beforeBlur : Roo.emptyFn,
10992
10993     
10994     // private
10995     onBlur : function(){
10996         this.beforeBlur();
10997         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
10998             //this.el.removeClass(this.focusClass);
10999         }
11000         this.hasFocus = false;
11001         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
11002             this.validate();
11003         }
11004         var v = this.getValue();
11005         if(String(v) !== String(this.startValue)){
11006             this.fireEvent('change', this, v, this.startValue);
11007         }
11008         this.fireEvent("blur", this);
11009     },
11010     
11011     onChange : function(e)
11012     {
11013         var v = this.getValue();
11014         if(String(v) !== String(this.startValue)){
11015             this.fireEvent('change', this, v, this.startValue);
11016         }
11017         
11018     },
11019     
11020     /**
11021      * Resets the current field value to the originally loaded value and clears any validation messages
11022      */
11023     reset : function(){
11024         this.setValue(this.originalValue);
11025         this.validate();
11026     },
11027      /**
11028      * Returns the name of the field
11029      * @return {Mixed} name The name field
11030      */
11031     getName: function(){
11032         return this.name;
11033     },
11034      /**
11035      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
11036      * @return {Mixed} value The field value
11037      */
11038     getValue : function(){
11039         
11040         var v = this.inputEl().getValue();
11041         
11042         return v;
11043     },
11044     /**
11045      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
11046      * @return {Mixed} value The field value
11047      */
11048     getRawValue : function(){
11049         var v = this.inputEl().getValue();
11050         
11051         return v;
11052     },
11053     
11054     /**
11055      * Sets the underlying DOM field's value directly, bypassing validation.  To set the value with validation see {@link #setValue}.
11056      * @param {Mixed} value The value to set
11057      */
11058     setRawValue : function(v){
11059         return this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
11060     },
11061     
11062     selectText : function(start, end){
11063         var v = this.getRawValue();
11064         if(v.length > 0){
11065             start = start === undefined ? 0 : start;
11066             end = end === undefined ? v.length : end;
11067             var d = this.inputEl().dom;
11068             if(d.setSelectionRange){
11069                 d.setSelectionRange(start, end);
11070             }else if(d.createTextRange){
11071                 var range = d.createTextRange();
11072                 range.moveStart("character", start);
11073                 range.moveEnd("character", v.length-end);
11074                 range.select();
11075             }
11076         }
11077     },
11078     
11079     /**
11080      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
11081      * @param {Mixed} value The value to set
11082      */
11083     setValue : function(v){
11084         this.value = v;
11085         if(this.rendered){
11086             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
11087             this.validate();
11088         }
11089     },
11090     
11091     /*
11092     processValue : function(value){
11093         if(this.stripCharsRe){
11094             var newValue = value.replace(this.stripCharsRe, '');
11095             if(newValue !== value){
11096                 this.setRawValue(newValue);
11097                 return newValue;
11098             }
11099         }
11100         return value;
11101     },
11102   */
11103     preFocus : function(){
11104         
11105         if(this.selectOnFocus){
11106             this.inputEl().dom.select();
11107         }
11108     },
11109     filterKeys : function(e){
11110         var k = e.getKey();
11111         if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
11112             return;
11113         }
11114         var c = e.getCharCode(), cc = String.fromCharCode(c);
11115         if(Roo.isIE && (e.isSpecialKey() || !cc)){
11116             return;
11117         }
11118         if(!this.maskRe.test(cc)){
11119             e.stopEvent();
11120         }
11121     },
11122      /**
11123      * Clear any invalid styles/messages for this field
11124      */
11125     clearInvalid : function(){
11126         
11127         if(!this.el || this.preventMark){ // not rendered
11128             return;
11129         }
11130         
11131         
11132         this.el.removeClass([this.invalidClass, 'is-invalid']);
11133         
11134         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
11135             
11136             var feedback = this.el.select('.form-control-feedback', true).first();
11137             
11138             if(feedback){
11139                 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
11140             }
11141             
11142         }
11143         
11144         if(this.indicator){
11145             this.indicator.removeClass('visible');
11146             this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
11147         }
11148         
11149         this.fireEvent('valid', this);
11150     },
11151     
11152      /**
11153      * Mark this field as valid
11154      */
11155     markValid : function()
11156     {
11157         if(!this.el  || this.preventMark){ // not rendered...
11158             return;
11159         }
11160         
11161         this.el.removeClass([this.invalidClass, this.validClass]);
11162         this.inputEl().removeClass(['is-valid', 'is-invalid']);
11163
11164         var feedback = this.el.select('.form-control-feedback', true).first();
11165             
11166         if(feedback){
11167             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
11168         }
11169         
11170         if(this.indicator){
11171             this.indicator.removeClass('visible');
11172             this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
11173         }
11174         
11175         if(this.disabled){
11176             return;
11177         }
11178         
11179            
11180         if(this.allowBlank && !this.getRawValue().length){
11181             return;
11182         }
11183         if (Roo.bootstrap.version == 3) {
11184             this.el.addClass(this.validClass);
11185         } else {
11186             this.inputEl().addClass('is-valid');
11187         }
11188
11189         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
11190             
11191             var feedback = this.el.select('.form-control-feedback', true).first();
11192             
11193             if(feedback){
11194                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
11195                 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
11196             }
11197             
11198         }
11199         
11200         this.fireEvent('valid', this);
11201     },
11202     
11203      /**
11204      * Mark this field as invalid
11205      * @param {String} msg The validation message
11206      */
11207     markInvalid : function(msg)
11208     {
11209         if(!this.el  || this.preventMark){ // not rendered
11210             return;
11211         }
11212         
11213         this.el.removeClass([this.invalidClass, this.validClass]);
11214         this.inputEl().removeClass(['is-valid', 'is-invalid']);
11215         
11216         var feedback = this.el.select('.form-control-feedback', true).first();
11217             
11218         if(feedback){
11219             this.el.select('.form-control-feedback', true).first().removeClass(
11220                     [this.invalidFeedbackClass, this.validFeedbackClass]);
11221         }
11222
11223         if(this.disabled){
11224             return;
11225         }
11226         
11227         if(this.allowBlank && !this.getRawValue().length){
11228             return;
11229         }
11230         
11231         if(this.indicator){
11232             this.indicator.removeClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
11233             this.indicator.addClass('visible');
11234         }
11235         if (Roo.bootstrap.version == 3) {
11236             this.el.addClass(this.invalidClass);
11237         } else {
11238             this.inputEl().addClass('is-invalid');
11239         }
11240         
11241         
11242         
11243         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
11244             
11245             var feedback = this.el.select('.form-control-feedback', true).first();
11246             
11247             if(feedback){
11248                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
11249                 
11250                 if(this.getValue().length || this.forceFeedback){
11251                     this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
11252                 }
11253                 
11254             }
11255             
11256         }
11257         
11258         this.fireEvent('invalid', this, msg);
11259     },
11260     // private
11261     SafariOnKeyDown : function(event)
11262     {
11263         // this is a workaround for a password hang bug on chrome/ webkit.
11264         if (this.inputEl().dom.type != 'password') {
11265             return;
11266         }
11267         
11268         var isSelectAll = false;
11269         
11270         if(this.inputEl().dom.selectionEnd > 0){
11271             isSelectAll = (this.inputEl().dom.selectionEnd - this.inputEl().dom.selectionStart - this.getValue().length == 0) ? true : false;
11272         }
11273         if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
11274             event.preventDefault();
11275             this.setValue('');
11276             return;
11277         }
11278         
11279         if(isSelectAll  && event.getCharCode() > 31 && !event.ctrlKey) { // not backspace and delete key (or ctrl-v)
11280             
11281             event.preventDefault();
11282             // this is very hacky as keydown always get's upper case.
11283             //
11284             var cc = String.fromCharCode(event.getCharCode());
11285             this.setValue( event.shiftKey ?  cc : cc.toLowerCase());
11286             
11287         }
11288     },
11289     adjustWidth : function(tag, w){
11290         tag = tag.toLowerCase();
11291         if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
11292             if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
11293                 if(tag == 'input'){
11294                     return w + 2;
11295                 }
11296                 if(tag == 'textarea'){
11297                     return w-2;
11298                 }
11299             }else if(Roo.isOpera){
11300                 if(tag == 'input'){
11301                     return w + 2;
11302                 }
11303                 if(tag == 'textarea'){
11304                     return w-2;
11305                 }
11306             }
11307         }
11308         return w;
11309     },
11310     
11311     setFieldLabel : function(v)
11312     {
11313         if(!this.rendered){
11314             return;
11315         }
11316         
11317         if(this.indicatorEl()){
11318             var ar = this.el.select('label > span',true);
11319             
11320             if (ar.elements.length) {
11321                 this.el.select('label > span',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
11322                 this.fieldLabel = v;
11323                 return;
11324             }
11325             
11326             var br = this.el.select('label',true);
11327             
11328             if(br.elements.length) {
11329                 this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
11330                 this.fieldLabel = v;
11331                 return;
11332             }
11333             
11334             Roo.log('Cannot Found any of label > span || label in input');
11335             return;
11336         }
11337         
11338         this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
11339         this.fieldLabel = v;
11340         
11341         
11342     }
11343 });
11344
11345  
11346 /*
11347  * - LGPL
11348  *
11349  * Input
11350  * 
11351  */
11352
11353 /**
11354  * @class Roo.bootstrap.TextArea
11355  * @extends Roo.bootstrap.Input
11356  * Bootstrap TextArea class
11357  * @cfg {Number} cols Specifies the visible width of a text area
11358  * @cfg {Number} rows Specifies the visible number of lines in a text area
11359  * @cfg {string} wrap (soft|hard)Specifies how the text in a text area is to be wrapped when submitted in a form
11360  * @cfg {string} resize (none|both|horizontal|vertical|inherit|initial)
11361  * @cfg {string} html text
11362  * 
11363  * @constructor
11364  * Create a new TextArea
11365  * @param {Object} config The config object
11366  */
11367
11368 Roo.bootstrap.TextArea = function(config){
11369     Roo.bootstrap.TextArea.superclass.constructor.call(this, config);
11370    
11371 };
11372
11373 Roo.extend(Roo.bootstrap.TextArea, Roo.bootstrap.Input,  {
11374      
11375     cols : false,
11376     rows : 5,
11377     readOnly : false,
11378     warp : 'soft',
11379     resize : false,
11380     value: false,
11381     html: false,
11382     
11383     getAutoCreate : function(){
11384         
11385         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
11386         
11387         var id = Roo.id();
11388         
11389         var cfg = {};
11390         
11391         if(this.inputType != 'hidden'){
11392             cfg.cls = 'form-group' //input-group
11393         }
11394         
11395         var input =  {
11396             tag: 'textarea',
11397             id : id,
11398             warp : this.warp,
11399             rows : this.rows,
11400             value : this.value || '',
11401             html: this.html || '',
11402             cls : 'form-control',
11403             placeholder : this.placeholder || '' 
11404             
11405         };
11406         
11407         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
11408             input.maxLength = this.maxLength;
11409         }
11410         
11411         if(this.resize){
11412             input.style = (typeof(input.style) == 'undefined') ? 'resize:' + this.resize : input.style + 'resize:' + this.resize;
11413         }
11414         
11415         if(this.cols){
11416             input.cols = this.cols;
11417         }
11418         
11419         if (this.readOnly) {
11420             input.readonly = true;
11421         }
11422         
11423         if (this.name) {
11424             input.name = this.name;
11425         }
11426         
11427         if (this.size) {
11428             input.cls = (typeof(input.cls) == 'undefined') ? 'input-' + this.size : input.cls + ' input-' + this.size;
11429         }
11430         
11431         var settings=this;
11432         ['xs','sm','md','lg'].map(function(size){
11433             if (settings[size]) {
11434                 cfg.cls += ' col-' + size + '-' + settings[size];
11435             }
11436         });
11437         
11438         var inputblock = input;
11439         
11440         if(this.hasFeedback && !this.allowBlank){
11441             
11442             var feedback = {
11443                 tag: 'span',
11444                 cls: 'glyphicon form-control-feedback'
11445             };
11446
11447             inputblock = {
11448                 cls : 'has-feedback',
11449                 cn :  [
11450                     input,
11451                     feedback
11452                 ] 
11453             };  
11454         }
11455         
11456         
11457         if (this.before || this.after) {
11458             
11459             inputblock = {
11460                 cls : 'input-group',
11461                 cn :  [] 
11462             };
11463             if (this.before) {
11464                 inputblock.cn.push({
11465                     tag :'span',
11466                     cls : 'input-group-addon',
11467                     html : this.before
11468                 });
11469             }
11470             
11471             inputblock.cn.push(input);
11472             
11473             if(this.hasFeedback && !this.allowBlank){
11474                 inputblock.cls += ' has-feedback';
11475                 inputblock.cn.push(feedback);
11476             }
11477             
11478             if (this.after) {
11479                 inputblock.cn.push({
11480                     tag :'span',
11481                     cls : 'input-group-addon',
11482                     html : this.after
11483                 });
11484             }
11485             
11486         }
11487         
11488         if (align ==='left' && this.fieldLabel.length) {
11489             cfg.cn = [
11490                 {
11491                     tag: 'label',
11492                     'for' :  id,
11493                     cls : 'control-label',
11494                     html : this.fieldLabel
11495                 },
11496                 {
11497                     cls : "",
11498                     cn: [
11499                         inputblock
11500                     ]
11501                 }
11502
11503             ];
11504             
11505             if(this.labelWidth > 12){
11506                 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
11507             }
11508
11509             if(this.labelWidth < 13 && this.labelmd == 0){
11510                 this.labelmd = this.labelWidth;
11511             }
11512
11513             if(this.labellg > 0){
11514                 cfg.cn[0].cls += ' col-lg-' + this.labellg;
11515                 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
11516             }
11517
11518             if(this.labelmd > 0){
11519                 cfg.cn[0].cls += ' col-md-' + this.labelmd;
11520                 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
11521             }
11522
11523             if(this.labelsm > 0){
11524                 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
11525                 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
11526             }
11527
11528             if(this.labelxs > 0){
11529                 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
11530                 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
11531             }
11532             
11533         } else if ( this.fieldLabel.length) {
11534             cfg.cn = [
11535
11536                {
11537                    tag: 'label',
11538                    //cls : 'input-group-addon',
11539                    html : this.fieldLabel
11540
11541                },
11542
11543                inputblock
11544
11545            ];
11546
11547         } else {
11548
11549             cfg.cn = [
11550
11551                 inputblock
11552
11553             ];
11554                 
11555         }
11556         
11557         if (this.disabled) {
11558             input.disabled=true;
11559         }
11560         
11561         return cfg;
11562         
11563     },
11564     /**
11565      * return the real textarea element.
11566      */
11567     inputEl: function ()
11568     {
11569         return this.el.select('textarea.form-control',true).first();
11570     },
11571     
11572     /**
11573      * Clear any invalid styles/messages for this field
11574      */
11575     clearInvalid : function()
11576     {
11577         
11578         if(!this.el || this.preventMark){ // not rendered
11579             return;
11580         }
11581         
11582         var label = this.el.select('label', true).first();
11583         var icon = this.el.select('i.fa-star', true).first();
11584         
11585         if(label && icon){
11586             icon.remove();
11587         }
11588         this.el.removeClass( this.validClass);
11589         this.inputEl().removeClass('is-invalid');
11590          
11591         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
11592             
11593             var feedback = this.el.select('.form-control-feedback', true).first();
11594             
11595             if(feedback){
11596                 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
11597             }
11598             
11599         }
11600         
11601         this.fireEvent('valid', this);
11602     },
11603     
11604      /**
11605      * Mark this field as valid
11606      */
11607     markValid : function()
11608     {
11609         if(!this.el  || this.preventMark){ // not rendered
11610             return;
11611         }
11612         
11613         this.el.removeClass([this.invalidClass, this.validClass]);
11614         this.inputEl().removeClass(['is-valid', 'is-invalid']);
11615         
11616         var feedback = this.el.select('.form-control-feedback', true).first();
11617             
11618         if(feedback){
11619             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
11620         }
11621
11622         if(this.disabled || this.allowBlank){
11623             return;
11624         }
11625         
11626         var label = this.el.select('label', true).first();
11627         var icon = this.el.select('i.fa-star', true).first();
11628         
11629         if(label && icon){
11630             icon.remove();
11631         }
11632         if (Roo.bootstrap.version == 3) {
11633             this.el.addClass(this.validClass);
11634         } else {
11635             this.inputEl().addClass('is-valid');
11636         }
11637         
11638         
11639         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
11640             
11641             var feedback = this.el.select('.form-control-feedback', true).first();
11642             
11643             if(feedback){
11644                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
11645                 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
11646             }
11647             
11648         }
11649         
11650         this.fireEvent('valid', this);
11651     },
11652     
11653      /**
11654      * Mark this field as invalid
11655      * @param {String} msg The validation message
11656      */
11657     markInvalid : function(msg)
11658     {
11659         if(!this.el  || this.preventMark){ // not rendered
11660             return;
11661         }
11662         
11663         this.el.removeClass([this.invalidClass, this.validClass]);
11664         this.inputEl().removeClass(['is-valid', 'is-invalid']);
11665         
11666         var feedback = this.el.select('.form-control-feedback', true).first();
11667             
11668         if(feedback){
11669             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
11670         }
11671
11672         if(this.disabled || this.allowBlank){
11673             return;
11674         }
11675         
11676         var label = this.el.select('label', true).first();
11677         var icon = this.el.select('i.fa-star', true).first();
11678         
11679         if(!this.getValue().length && label && !icon){
11680             this.el.createChild({
11681                 tag : 'i',
11682                 cls : 'text-danger fa fa-lg fa-star',
11683                 tooltip : 'This field is required',
11684                 style : 'margin-right:5px;'
11685             }, label, true);
11686         }
11687         
11688         if (Roo.bootstrap.version == 3) {
11689             this.el.addClass(this.invalidClass);
11690         } else {
11691             this.inputEl().addClass('is-invalid');
11692         }
11693         
11694         // fixme ... this may be depricated need to test..
11695         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
11696             
11697             var feedback = this.el.select('.form-control-feedback', true).first();
11698             
11699             if(feedback){
11700                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
11701                 
11702                 if(this.getValue().length || this.forceFeedback){
11703                     this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
11704                 }
11705                 
11706             }
11707             
11708         }
11709         
11710         this.fireEvent('invalid', this, msg);
11711     }
11712 });
11713
11714  
11715 /*
11716  * - LGPL
11717  *
11718  * trigger field - base class for combo..
11719  * 
11720  */
11721  
11722 /**
11723  * @class Roo.bootstrap.TriggerField
11724  * @extends Roo.bootstrap.Input
11725  * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
11726  * The trigger has no default action, so you must assign a function to implement the trigger click handler by
11727  * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
11728  * for which you can provide a custom implementation.  For example:
11729  * <pre><code>
11730 var trigger = new Roo.bootstrap.TriggerField();
11731 trigger.onTriggerClick = myTriggerFn;
11732 trigger.applyTo('my-field');
11733 </code></pre>
11734  *
11735  * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
11736  * {@link Roo.bootstrap.DateField} and {@link Roo.bootstrap.ComboBox} are perfect examples of this.
11737  * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
11738  * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
11739  * @cfg {String} caret (search|calendar) BS3 only - carat fa name
11740
11741  * @constructor
11742  * Create a new TriggerField.
11743  * @param {Object} config Configuration options (valid {@Roo.bootstrap.Input} config options will also be applied
11744  * to the base TextField)
11745  */
11746 Roo.bootstrap.TriggerField = function(config){
11747     this.mimicing = false;
11748     Roo.bootstrap.TriggerField.superclass.constructor.call(this, config);
11749 };
11750
11751 Roo.extend(Roo.bootstrap.TriggerField, Roo.bootstrap.Input,  {
11752     /**
11753      * @cfg {String} triggerClass A CSS class to apply to the trigger
11754      */
11755      /**
11756      * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
11757      */
11758     hideTrigger:false,
11759
11760     /**
11761      * @cfg {Boolean} removable (true|false) special filter default false
11762      */
11763     removable : false,
11764     
11765     /** @cfg {Boolean} grow @hide */
11766     /** @cfg {Number} growMin @hide */
11767     /** @cfg {Number} growMax @hide */
11768
11769     /**
11770      * @hide 
11771      * @method
11772      */
11773     autoSize: Roo.emptyFn,
11774     // private
11775     monitorTab : true,
11776     // private
11777     deferHeight : true,
11778
11779     
11780     actionMode : 'wrap',
11781     
11782     caret : false,
11783     
11784     
11785     getAutoCreate : function(){
11786        
11787         var align = this.labelAlign || this.parentLabelAlign();
11788         
11789         var id = Roo.id();
11790         
11791         var cfg = {
11792             cls: 'form-group' //input-group
11793         };
11794         
11795         
11796         var input =  {
11797             tag: 'input',
11798             id : id,
11799             type : this.inputType,
11800             cls : 'form-control',
11801             autocomplete: 'new-password',
11802             placeholder : this.placeholder || '' 
11803             
11804         };
11805         if (this.name) {
11806             input.name = this.name;
11807         }
11808         if (this.size) {
11809             input.cls += ' input-' + this.size;
11810         }
11811         
11812         if (this.disabled) {
11813             input.disabled=true;
11814         }
11815         
11816         var inputblock = input;
11817         
11818         if(this.hasFeedback && !this.allowBlank){
11819             
11820             var feedback = {
11821                 tag: 'span',
11822                 cls: 'glyphicon form-control-feedback'
11823             };
11824             
11825             if(this.removable && !this.editable  ){
11826                 inputblock = {
11827                     cls : 'has-feedback',
11828                     cn :  [
11829                         inputblock,
11830                         {
11831                             tag: 'button',
11832                             html : 'x',
11833                             cls : 'roo-combo-removable-btn close'
11834                         },
11835                         feedback
11836                     ] 
11837                 };
11838             } else {
11839                 inputblock = {
11840                     cls : 'has-feedback',
11841                     cn :  [
11842                         inputblock,
11843                         feedback
11844                     ] 
11845                 };
11846             }
11847
11848         } else {
11849             if(this.removable && !this.editable ){
11850                 inputblock = {
11851                     cls : 'roo-removable',
11852                     cn :  [
11853                         inputblock,
11854                         {
11855                             tag: 'button',
11856                             html : 'x',
11857                             cls : 'roo-combo-removable-btn close'
11858                         }
11859                     ] 
11860                 };
11861             }
11862         }
11863         
11864         if (this.before || this.after) {
11865             
11866             inputblock = {
11867                 cls : 'input-group',
11868                 cn :  [] 
11869             };
11870             if (this.before) {
11871                 inputblock.cn.push({
11872                     tag :'span',
11873                     cls : 'input-group-addon input-group-prepend input-group-text',
11874                     html : this.before
11875                 });
11876             }
11877             
11878             inputblock.cn.push(input);
11879             
11880             if(this.hasFeedback && !this.allowBlank){
11881                 inputblock.cls += ' has-feedback';
11882                 inputblock.cn.push(feedback);
11883             }
11884             
11885             if (this.after) {
11886                 inputblock.cn.push({
11887                     tag :'span',
11888                     cls : 'input-group-addon input-group-append input-group-text',
11889                     html : this.after
11890                 });
11891             }
11892             
11893         };
11894         
11895       
11896         
11897         var ibwrap = inputblock;
11898         
11899         if(this.multiple){
11900             ibwrap = {
11901                 tag: 'ul',
11902                 cls: 'roo-select2-choices',
11903                 cn:[
11904                     {
11905                         tag: 'li',
11906                         cls: 'roo-select2-search-field',
11907                         cn: [
11908
11909                             inputblock
11910                         ]
11911                     }
11912                 ]
11913             };
11914                 
11915         }
11916         
11917         var combobox = {
11918             cls: 'roo-select2-container input-group',
11919             cn: [
11920                  {
11921                     tag: 'input',
11922                     type : 'hidden',
11923                     cls: 'form-hidden-field'
11924                 },
11925                 ibwrap
11926             ]
11927         };
11928         
11929         if(!this.multiple && this.showToggleBtn){
11930             
11931             var caret = {
11932                         tag: 'span',
11933                         cls: 'caret'
11934              };
11935             if (this.caret != false) {
11936                 caret = {
11937                      tag: 'i',
11938                      cls: 'fa fa-' + this.caret
11939                 };
11940                 
11941             }
11942             
11943             combobox.cn.push({
11944                 tag :'span',
11945                 cls : 'input-group-addon input-group-append input-group-text btn dropdown-toggle',
11946                 cn : [
11947                     Roo.bootstrap.version == 3 ? caret : '',
11948                     {
11949                         tag: 'span',
11950                         cls: 'combobox-clear',
11951                         cn  : [
11952                             {
11953                                 tag : 'i',
11954                                 cls: 'icon-remove'
11955                             }
11956                         ]
11957                     }
11958                 ]
11959
11960             })
11961         }
11962         
11963         if(this.multiple){
11964             combobox.cls += ' roo-select2-container-multi';
11965         }
11966          var indicator = {
11967             tag : 'i',
11968             cls : 'roo-required-indicator ' + (this.indicatorpos == 'right'  ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
11969             tooltip : 'This field is required'
11970         };
11971         if (Roo.bootstrap.version == 4) {
11972             indicator = {
11973                 tag : 'i',
11974                 style : 'display:none'
11975             };
11976         }
11977         
11978         
11979         if (align ==='left' && this.fieldLabel.length) {
11980             
11981             cfg.cls += ' roo-form-group-label-left'  + (Roo.bootstrap.version == 4 ? ' row' : '');
11982
11983             cfg.cn = [
11984                 indicator,
11985                 {
11986                     tag: 'label',
11987                     'for' :  id,
11988                     cls : 'control-label',
11989                     html : this.fieldLabel
11990
11991                 },
11992                 {
11993                     cls : "", 
11994                     cn: [
11995                         combobox
11996                     ]
11997                 }
11998
11999             ];
12000             
12001             var labelCfg = cfg.cn[1];
12002             var contentCfg = cfg.cn[2];
12003             
12004             if(this.indicatorpos == 'right'){
12005                 cfg.cn = [
12006                     {
12007                         tag: 'label',
12008                         'for' :  id,
12009                         cls : 'control-label',
12010                         cn : [
12011                             {
12012                                 tag : 'span',
12013                                 html : this.fieldLabel
12014                             },
12015                             indicator
12016                         ]
12017                     },
12018                     {
12019                         cls : "", 
12020                         cn: [
12021                             combobox
12022                         ]
12023                     }
12024
12025                 ];
12026                 
12027                 labelCfg = cfg.cn[0];
12028                 contentCfg = cfg.cn[1];
12029             }
12030             
12031             if(this.labelWidth > 12){
12032                 labelCfg.style = "width: " + this.labelWidth + 'px';
12033             }
12034             
12035             if(this.labelWidth < 13 && this.labelmd == 0){
12036                 this.labelmd = this.labelWidth;
12037             }
12038             
12039             if(this.labellg > 0){
12040                 labelCfg.cls += ' col-lg-' + this.labellg;
12041                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
12042             }
12043             
12044             if(this.labelmd > 0){
12045                 labelCfg.cls += ' col-md-' + this.labelmd;
12046                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
12047             }
12048             
12049             if(this.labelsm > 0){
12050                 labelCfg.cls += ' col-sm-' + this.labelsm;
12051                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
12052             }
12053             
12054             if(this.labelxs > 0){
12055                 labelCfg.cls += ' col-xs-' + this.labelxs;
12056                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
12057             }
12058             
12059         } else if ( this.fieldLabel.length) {
12060 //                Roo.log(" label");
12061             cfg.cn = [
12062                 indicator,
12063                {
12064                    tag: 'label',
12065                    //cls : 'input-group-addon',
12066                    html : this.fieldLabel
12067
12068                },
12069
12070                combobox
12071
12072             ];
12073             
12074             if(this.indicatorpos == 'right'){
12075                 
12076                 cfg.cn = [
12077                     {
12078                        tag: 'label',
12079                        cn : [
12080                            {
12081                                tag : 'span',
12082                                html : this.fieldLabel
12083                            },
12084                            indicator
12085                        ]
12086
12087                     },
12088                     combobox
12089
12090                 ];
12091
12092             }
12093
12094         } else {
12095             
12096 //                Roo.log(" no label && no align");
12097                 cfg = combobox
12098                      
12099                 
12100         }
12101         
12102         var settings=this;
12103         ['xs','sm','md','lg'].map(function(size){
12104             if (settings[size]) {
12105                 cfg.cls += ' col-' + size + '-' + settings[size];
12106             }
12107         });
12108         
12109         return cfg;
12110         
12111     },
12112     
12113     
12114     
12115     // private
12116     onResize : function(w, h){
12117 //        Roo.bootstrap.TriggerField.superclass.onResize.apply(this, arguments);
12118 //        if(typeof w == 'number'){
12119 //            var x = w - this.trigger.getWidth();
12120 //            this.inputEl().setWidth(this.adjustWidth('input', x));
12121 //            this.trigger.setStyle('left', x+'px');
12122 //        }
12123     },
12124
12125     // private
12126     adjustSize : Roo.BoxComponent.prototype.adjustSize,
12127
12128     // private
12129     getResizeEl : function(){
12130         return this.inputEl();
12131     },
12132
12133     // private
12134     getPositionEl : function(){
12135         return this.inputEl();
12136     },
12137
12138     // private
12139     alignErrorIcon : function(){
12140         this.errorIcon.alignTo(this.inputEl(), 'tl-tr', [2, 0]);
12141     },
12142
12143     // private
12144     initEvents : function(){
12145         
12146         this.createList();
12147         
12148         Roo.bootstrap.TriggerField.superclass.initEvents.call(this);
12149         //this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
12150         if(!this.multiple && this.showToggleBtn){
12151             this.trigger = this.el.select('span.dropdown-toggle',true).first();
12152             if(this.hideTrigger){
12153                 this.trigger.setDisplayed(false);
12154             }
12155             this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
12156         }
12157         
12158         if(this.multiple){
12159             this.inputEl().on("click", this.onTriggerClick, this, {preventDefault:true});
12160         }
12161         
12162         if(this.removable && !this.editable && !this.tickable){
12163             var close = this.closeTriggerEl();
12164             
12165             if(close){
12166                 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
12167                 close.on('click', this.removeBtnClick, this, close);
12168             }
12169         }
12170         
12171         //this.trigger.addClassOnOver('x-form-trigger-over');
12172         //this.trigger.addClassOnClick('x-form-trigger-click');
12173         
12174         //if(!this.width){
12175         //    this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
12176         //}
12177     },
12178     
12179     closeTriggerEl : function()
12180     {
12181         var close = this.el.select('.roo-combo-removable-btn', true).first();
12182         return close ? close : false;
12183     },
12184     
12185     removeBtnClick : function(e, h, el)
12186     {
12187         e.preventDefault();
12188         
12189         if(this.fireEvent("remove", this) !== false){
12190             this.reset();
12191             this.fireEvent("afterremove", this)
12192         }
12193     },
12194     
12195     createList : function()
12196     {
12197         this.list = Roo.get(document.body).createChild({
12198             tag: Roo.bootstrap.version == 4 ? 'div' : 'ul',
12199             cls: 'typeahead typeahead-long dropdown-menu',
12200             style: 'display:none'
12201         });
12202         
12203         this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';;
12204         
12205     },
12206
12207     // private
12208     initTrigger : function(){
12209        
12210     },
12211
12212     // private
12213     onDestroy : function(){
12214         if(this.trigger){
12215             this.trigger.removeAllListeners();
12216           //  this.trigger.remove();
12217         }
12218         //if(this.wrap){
12219         //    this.wrap.remove();
12220         //}
12221         Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
12222     },
12223
12224     // private
12225     onFocus : function(){
12226         Roo.bootstrap.TriggerField.superclass.onFocus.call(this);
12227         /*
12228         if(!this.mimicing){
12229             this.wrap.addClass('x-trigger-wrap-focus');
12230             this.mimicing = true;
12231             Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
12232             if(this.monitorTab){
12233                 this.el.on("keydown", this.checkTab, this);
12234             }
12235         }
12236         */
12237     },
12238
12239     // private
12240     checkTab : function(e){
12241         if(e.getKey() == e.TAB){
12242             this.triggerBlur();
12243         }
12244     },
12245
12246     // private
12247     onBlur : function(){
12248         // do nothing
12249     },
12250
12251     // private
12252     mimicBlur : function(e, t){
12253         /*
12254         if(!this.wrap.contains(t) && this.validateBlur()){
12255             this.triggerBlur();
12256         }
12257         */
12258     },
12259
12260     // private
12261     triggerBlur : function(){
12262         this.mimicing = false;
12263         Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
12264         if(this.monitorTab){
12265             this.el.un("keydown", this.checkTab, this);
12266         }
12267         //this.wrap.removeClass('x-trigger-wrap-focus');
12268         Roo.bootstrap.TriggerField.superclass.onBlur.call(this);
12269     },
12270
12271     // private
12272     // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
12273     validateBlur : function(e, t){
12274         return true;
12275     },
12276
12277     // private
12278     onDisable : function(){
12279         this.inputEl().dom.disabled = true;
12280         //Roo.bootstrap.TriggerField.superclass.onDisable.call(this);
12281         //if(this.wrap){
12282         //    this.wrap.addClass('x-item-disabled');
12283         //}
12284     },
12285
12286     // private
12287     onEnable : function(){
12288         this.inputEl().dom.disabled = false;
12289         //Roo.bootstrap.TriggerField.superclass.onEnable.call(this);
12290         //if(this.wrap){
12291         //    this.el.removeClass('x-item-disabled');
12292         //}
12293     },
12294
12295     // private
12296     onShow : function(){
12297         var ae = this.getActionEl();
12298         
12299         if(ae){
12300             ae.dom.style.display = '';
12301             ae.dom.style.visibility = 'visible';
12302         }
12303     },
12304
12305     // private
12306     
12307     onHide : function(){
12308         var ae = this.getActionEl();
12309         ae.dom.style.display = 'none';
12310     },
12311
12312     /**
12313      * The function that should handle the trigger's click event.  This method does nothing by default until overridden
12314      * by an implementing function.
12315      * @method
12316      * @param {EventObject} e
12317      */
12318     onTriggerClick : Roo.emptyFn
12319 });
12320  
12321 /*
12322 * Licence: LGPL
12323 */
12324
12325 /**
12326  * @class Roo.bootstrap.CardUploader
12327  * @extends Roo.bootstrap.Button
12328  * Bootstrap Card Uploader class - it's a button which when you add files to it, adds cards below with preview and the name...
12329  * @cfg {Number} errorTimeout default 3000
12330  * @cfg {Array}  images  an array of ?? Img objects ??? when loading existing files..
12331  * @cfg {Array}  html The button text.
12332
12333  *
12334  * @constructor
12335  * Create a new CardUploader
12336  * @param {Object} config The config object
12337  */
12338
12339 Roo.bootstrap.CardUploader = function(config){
12340     
12341  
12342     
12343     Roo.bootstrap.CardUploader.superclass.constructor.call(this, config);
12344     
12345     
12346     this.fileCollection   = new Roo.util.MixedCollection(false,function(r) {
12347         return r.data.id
12348         });
12349     
12350     
12351 };
12352
12353 Roo.extend(Roo.bootstrap.CardUploader, Roo.bootstrap.Input,  {
12354     
12355      
12356     errorTimeout : 3000,
12357      
12358     images : false,
12359    
12360     fileCollection : false,
12361     allowBlank : true,
12362     
12363     getAutoCreate : function()
12364     {
12365         
12366         var cfg =  {
12367             cls :'form-group' ,
12368             cn : [
12369                
12370                 {
12371                     tag: 'label',
12372                    //cls : 'input-group-addon',
12373                     html : this.fieldLabel
12374
12375                 },
12376
12377                 {
12378                     tag: 'input',
12379                     type : 'hidden',
12380                     value : this.value,
12381                     cls : 'd-none  form-control'
12382                 },
12383                 
12384                 {
12385                     tag: 'input',
12386                     multiple : 'multiple',
12387                     type : 'file',
12388                     cls : 'd-none  roo-card-upload-selector'
12389                 },
12390                 
12391                 {
12392                     cls : 'roo-card-uploader-button-container w-100 mb-2'
12393                 },
12394                 {
12395                     cls : 'card-columns roo-card-uploader-container'
12396                 }
12397
12398             ]
12399         };
12400            
12401          
12402         return cfg;
12403     },
12404     
12405     getChildContainer : function() /// what children are added to.
12406     {
12407         return this.containerEl;
12408     },
12409    
12410     getButtonContainer : function() /// what children are added to.
12411     {
12412         return this.el.select(".roo-card-uploader-button-container").first();
12413     },
12414    
12415     initEvents : function()
12416     {
12417         
12418         Roo.bootstrap.Input.prototype.initEvents.call(this);
12419         
12420         var t = this;
12421         this.addxtype({
12422             xns: Roo.bootstrap,
12423
12424             xtype : 'Button',
12425             container_method : 'getButtonContainer' ,            
12426             html :  this.html, // fix changable?
12427             cls : 'w-100 ',
12428             listeners : {
12429                 'click' : function(btn, e) {
12430                     t.onClick(e);
12431                 }
12432             }
12433         });
12434         
12435         
12436         
12437         
12438         this.urlAPI = (window.createObjectURL && window) || 
12439                                 (window.URL && URL.revokeObjectURL && URL) || 
12440                                 (window.webkitURL && webkitURL);
12441                         
12442          
12443          
12444          
12445         this.selectorEl = this.el.select('.roo-card-upload-selector', true).first();
12446         
12447         this.selectorEl.on('change', this.onFileSelected, this);
12448         if (this.images) {
12449             var t = this;
12450             this.images.forEach(function(img) {
12451                 t.addCard(img)
12452             });
12453             this.images = false;
12454         }
12455         this.containerEl = this.el.select('.roo-card-uploader-container', true).first();
12456          
12457        
12458     },
12459     
12460    
12461     onClick : function(e)
12462     {
12463         e.preventDefault();
12464          
12465         this.selectorEl.dom.click();
12466          
12467     },
12468     
12469     onFileSelected : function(e)
12470     {
12471         e.preventDefault();
12472         
12473         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
12474             return;
12475         }
12476         
12477         Roo.each(this.selectorEl.dom.files, function(file){    
12478             this.addFile(file);
12479         }, this);
12480          
12481     },
12482     
12483       
12484     
12485       
12486     
12487     addFile : function(file)
12488     {
12489            
12490         if(typeof(file) === 'string'){
12491             throw "Add file by name?"; // should not happen
12492             return;
12493         }
12494         
12495         if(!file || !this.urlAPI){
12496             return;
12497         }
12498         
12499         // file;
12500         // file.type;
12501         
12502         var _this = this;
12503         
12504         
12505         var url = _this.urlAPI.createObjectURL( file);
12506            
12507         this.addCard({
12508             id : Roo.bootstrap.CardUploader.ID--,
12509             is_uploaded : false,
12510             src : url,
12511             title : file.name,
12512             mimetype : file.type,
12513             preview : false,
12514             is_deleted : 0
12515         })
12516         
12517     },
12518     
12519     addCard : function (data)
12520     {
12521         // hidden input element?
12522         // if the file is not an image...
12523         //then we need to use something other that and header_image
12524         var t = this;
12525         //   remove.....
12526         var footer = [
12527             {
12528                 xns : Roo.bootstrap,
12529                 xtype : 'CardFooter',
12530                 items: [
12531                     {
12532                         xns : Roo.bootstrap,
12533                         xtype : 'Element',
12534                         cls : 'd-flex',
12535                         items : [
12536                             
12537                             {
12538                                 xns : Roo.bootstrap,
12539                                 xtype : 'Button',
12540                                 html : String.format("<small>{0}</small>", data.title),
12541                                 cls : 'col-11 text-left',
12542                                 size: 'sm',
12543                                 weight: 'link',
12544                                 fa : 'download',
12545                                 listeners : {
12546                                     click : function() {
12547                                         this.downloadCard(data.id)
12548                                     }
12549                                 }
12550                             },
12551                           
12552                             {
12553                                 xns : Roo.bootstrap,
12554                                 xtype : 'Button',
12555                                 
12556                                 size : 'sm',
12557                                 weight: 'danger',
12558                                 cls : 'col-1',
12559                                 fa : 'times',
12560                                 listeners : {
12561                                     click : function() {
12562                                         t.removeCard(data.id)
12563                                     }
12564                                 }
12565                             }
12566                         ]
12567                     }
12568                     
12569                 ] 
12570             }
12571             
12572         ];
12573
12574         var cn = this.addxtype(
12575             {
12576                  
12577                 xns : Roo.bootstrap,
12578                 xtype : 'Card',
12579                 closeable : true,
12580                 header : !data.mimetype.match(/image/) && !data.preview ? "Document": false,
12581                 header_image : data.mimetype.match(/image/) ? data.src  : data.preview,
12582                 header_image_fit_square: true, // fixme  - we probably need to use the 'Img' element to do stuff like this.
12583                 data : data,
12584                 html : false,
12585                  
12586                 items : footer,
12587                 initEvents : function() {
12588                     Roo.bootstrap.Card.prototype.initEvents.call(this);
12589                     this.imgEl = this.el.select('.card-img-top').first();
12590                     if (this.imgEl) {
12591                         this.imgEl.on('click', function() { t.previewCard( data.id); }, this);
12592                         this.imgEl.set({ 'pointer' : 'cursor' });
12593                                   
12594                     }
12595                     
12596                   
12597                 }
12598                 
12599             }
12600         );
12601         // dont' really need ot update items.
12602         // this.items.push(cn);
12603         this.fileCollection.add(cn);
12604         this.updateInput();
12605         
12606     },
12607     removeCard : function(id)
12608     {
12609         
12610         var card  = this.fileCollection.get(id);
12611         card.data.is_deleted = 1;
12612         card.data.src = ''; /// delete the source - so it reduces size of not uploaded images etc.
12613         this.fileCollection.remove(card);
12614         //this.items = this.items.filter(function(e) { return e != card });
12615         // dont' really need ot update items.
12616         card.el.dom.parentNode.removeChild(card.el.dom);
12617         
12618     },
12619     reset: function()
12620     {
12621         this.fileCollection.each(function(card) {
12622             card.el.dom.parentNode.removeChild(card.el.dom);    
12623         });
12624         this.fileCollection.clear();
12625         this.updateInput();
12626     },
12627     
12628     updateInput : function()
12629     {
12630         var data = [];
12631         this.fileCollection.each(function(e) {
12632             data.push(e.data);
12633         });
12634         
12635         this.inputEl().dom.value = JSON.stringify(data);
12636     }
12637     
12638     
12639 });
12640
12641
12642 Roo.bootstrap.CardUploader.ID = -1;/*
12643  * Based on:
12644  * Ext JS Library 1.1.1
12645  * Copyright(c) 2006-2007, Ext JS, LLC.
12646  *
12647  * Originally Released Under LGPL - original licence link has changed is not relivant.
12648  *
12649  * Fork - LGPL
12650  * <script type="text/javascript">
12651  */
12652
12653
12654 /**
12655  * @class Roo.data.SortTypes
12656  * @singleton
12657  * Defines the default sorting (casting?) comparison functions used when sorting data.
12658  */
12659 Roo.data.SortTypes = {
12660     /**
12661      * Default sort that does nothing
12662      * @param {Mixed} s The value being converted
12663      * @return {Mixed} The comparison value
12664      */
12665     none : function(s){
12666         return s;
12667     },
12668     
12669     /**
12670      * The regular expression used to strip tags
12671      * @type {RegExp}
12672      * @property
12673      */
12674     stripTagsRE : /<\/?[^>]+>/gi,
12675     
12676     /**
12677      * Strips all HTML tags to sort on text only
12678      * @param {Mixed} s The value being converted
12679      * @return {String} The comparison value
12680      */
12681     asText : function(s){
12682         return String(s).replace(this.stripTagsRE, "");
12683     },
12684     
12685     /**
12686      * Strips all HTML tags to sort on text only - Case insensitive
12687      * @param {Mixed} s The value being converted
12688      * @return {String} The comparison value
12689      */
12690     asUCText : function(s){
12691         return String(s).toUpperCase().replace(this.stripTagsRE, "");
12692     },
12693     
12694     /**
12695      * Case insensitive string
12696      * @param {Mixed} s The value being converted
12697      * @return {String} The comparison value
12698      */
12699     asUCString : function(s) {
12700         return String(s).toUpperCase();
12701     },
12702     
12703     /**
12704      * Date sorting
12705      * @param {Mixed} s The value being converted
12706      * @return {Number} The comparison value
12707      */
12708     asDate : function(s) {
12709         if(!s){
12710             return 0;
12711         }
12712         if(s instanceof Date){
12713             return s.getTime();
12714         }
12715         return Date.parse(String(s));
12716     },
12717     
12718     /**
12719      * Float sorting
12720      * @param {Mixed} s The value being converted
12721      * @return {Float} The comparison value
12722      */
12723     asFloat : function(s) {
12724         var val = parseFloat(String(s).replace(/,/g, ""));
12725         if(isNaN(val)) {
12726             val = 0;
12727         }
12728         return val;
12729     },
12730     
12731     /**
12732      * Integer sorting
12733      * @param {Mixed} s The value being converted
12734      * @return {Number} The comparison value
12735      */
12736     asInt : function(s) {
12737         var val = parseInt(String(s).replace(/,/g, ""));
12738         if(isNaN(val)) {
12739             val = 0;
12740         }
12741         return val;
12742     }
12743 };/*
12744  * Based on:
12745  * Ext JS Library 1.1.1
12746  * Copyright(c) 2006-2007, Ext JS, LLC.
12747  *
12748  * Originally Released Under LGPL - original licence link has changed is not relivant.
12749  *
12750  * Fork - LGPL
12751  * <script type="text/javascript">
12752  */
12753
12754 /**
12755 * @class Roo.data.Record
12756  * Instances of this class encapsulate both record <em>definition</em> information, and record
12757  * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
12758  * to access Records cached in an {@link Roo.data.Store} object.<br>
12759  * <p>
12760  * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
12761  * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
12762  * objects.<br>
12763  * <p>
12764  * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
12765  * @constructor
12766  * This constructor should not be used to create Record objects. Instead, use the constructor generated by
12767  * {@link #create}. The parameters are the same.
12768  * @param {Array} data An associative Array of data values keyed by the field name.
12769  * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
12770  * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
12771  * not specified an integer id is generated.
12772  */
12773 Roo.data.Record = function(data, id){
12774     this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
12775     this.data = data;
12776 };
12777
12778 /**
12779  * Generate a constructor for a specific record layout.
12780  * @param {Array} o An Array of field definition objects which specify field names, and optionally,
12781  * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
12782  * Each field definition object may contain the following properties: <ul>
12783  * <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,
12784  * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
12785  * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
12786  * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
12787  * is being used, then this is a string containing the javascript expression to reference the data relative to 
12788  * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
12789  * to the data item relative to the record element. If the mapping expression is the same as the field name,
12790  * this may be omitted.</p></li>
12791  * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
12792  * <ul><li>auto (Default, implies no conversion)</li>
12793  * <li>string</li>
12794  * <li>int</li>
12795  * <li>float</li>
12796  * <li>boolean</li>
12797  * <li>date</li></ul></p></li>
12798  * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
12799  * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
12800  * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
12801  * by the Reader into an object that will be stored in the Record. It is passed the
12802  * following parameters:<ul>
12803  * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
12804  * </ul></p></li>
12805  * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
12806  * </ul>
12807  * <br>usage:<br><pre><code>
12808 var TopicRecord = Roo.data.Record.create(
12809     {name: 'title', mapping: 'topic_title'},
12810     {name: 'author', mapping: 'username'},
12811     {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
12812     {name: 'lastPost', mapping: 'post_time', type: 'date'},
12813     {name: 'lastPoster', mapping: 'user2'},
12814     {name: 'excerpt', mapping: 'post_text'}
12815 );
12816
12817 var myNewRecord = new TopicRecord({
12818     title: 'Do my job please',
12819     author: 'noobie',
12820     totalPosts: 1,
12821     lastPost: new Date(),
12822     lastPoster: 'Animal',
12823     excerpt: 'No way dude!'
12824 });
12825 myStore.add(myNewRecord);
12826 </code></pre>
12827  * @method create
12828  * @static
12829  */
12830 Roo.data.Record.create = function(o){
12831     var f = function(){
12832         f.superclass.constructor.apply(this, arguments);
12833     };
12834     Roo.extend(f, Roo.data.Record);
12835     var p = f.prototype;
12836     p.fields = new Roo.util.MixedCollection(false, function(field){
12837         return field.name;
12838     });
12839     for(var i = 0, len = o.length; i < len; i++){
12840         p.fields.add(new Roo.data.Field(o[i]));
12841     }
12842     f.getField = function(name){
12843         return p.fields.get(name);  
12844     };
12845     return f;
12846 };
12847
12848 Roo.data.Record.AUTO_ID = 1000;
12849 Roo.data.Record.EDIT = 'edit';
12850 Roo.data.Record.REJECT = 'reject';
12851 Roo.data.Record.COMMIT = 'commit';
12852
12853 Roo.data.Record.prototype = {
12854     /**
12855      * Readonly flag - true if this record has been modified.
12856      * @type Boolean
12857      */
12858     dirty : false,
12859     editing : false,
12860     error: null,
12861     modified: null,
12862
12863     // private
12864     join : function(store){
12865         this.store = store;
12866     },
12867
12868     /**
12869      * Set the named field to the specified value.
12870      * @param {String} name The name of the field to set.
12871      * @param {Object} value The value to set the field to.
12872      */
12873     set : function(name, value){
12874         if(this.data[name] == value){
12875             return;
12876         }
12877         this.dirty = true;
12878         if(!this.modified){
12879             this.modified = {};
12880         }
12881         if(typeof this.modified[name] == 'undefined'){
12882             this.modified[name] = this.data[name];
12883         }
12884         this.data[name] = value;
12885         if(!this.editing && this.store){
12886             this.store.afterEdit(this);
12887         }       
12888     },
12889
12890     /**
12891      * Get the value of the named field.
12892      * @param {String} name The name of the field to get the value of.
12893      * @return {Object} The value of the field.
12894      */
12895     get : function(name){
12896         return this.data[name]; 
12897     },
12898
12899     // private
12900     beginEdit : function(){
12901         this.editing = true;
12902         this.modified = {}; 
12903     },
12904
12905     // private
12906     cancelEdit : function(){
12907         this.editing = false;
12908         delete this.modified;
12909     },
12910
12911     // private
12912     endEdit : function(){
12913         this.editing = false;
12914         if(this.dirty && this.store){
12915             this.store.afterEdit(this);
12916         }
12917     },
12918
12919     /**
12920      * Usually called by the {@link Roo.data.Store} which owns the Record.
12921      * Rejects all changes made to the Record since either creation, or the last commit operation.
12922      * Modified fields are reverted to their original values.
12923      * <p>
12924      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
12925      * of reject operations.
12926      */
12927     reject : function(){
12928         var m = this.modified;
12929         for(var n in m){
12930             if(typeof m[n] != "function"){
12931                 this.data[n] = m[n];
12932             }
12933         }
12934         this.dirty = false;
12935         delete this.modified;
12936         this.editing = false;
12937         if(this.store){
12938             this.store.afterReject(this);
12939         }
12940     },
12941
12942     /**
12943      * Usually called by the {@link Roo.data.Store} which owns the Record.
12944      * Commits all changes made to the Record since either creation, or the last commit operation.
12945      * <p>
12946      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
12947      * of commit operations.
12948      */
12949     commit : function(){
12950         this.dirty = false;
12951         delete this.modified;
12952         this.editing = false;
12953         if(this.store){
12954             this.store.afterCommit(this);
12955         }
12956     },
12957
12958     // private
12959     hasError : function(){
12960         return this.error != null;
12961     },
12962
12963     // private
12964     clearError : function(){
12965         this.error = null;
12966     },
12967
12968     /**
12969      * Creates a copy of this record.
12970      * @param {String} id (optional) A new record id if you don't want to use this record's id
12971      * @return {Record}
12972      */
12973     copy : function(newId) {
12974         return new this.constructor(Roo.apply({}, this.data), newId || this.id);
12975     }
12976 };/*
12977  * Based on:
12978  * Ext JS Library 1.1.1
12979  * Copyright(c) 2006-2007, Ext JS, LLC.
12980  *
12981  * Originally Released Under LGPL - original licence link has changed is not relivant.
12982  *
12983  * Fork - LGPL
12984  * <script type="text/javascript">
12985  */
12986
12987
12988
12989 /**
12990  * @class Roo.data.Store
12991  * @extends Roo.util.Observable
12992  * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
12993  * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
12994  * <p>
12995  * 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
12996  * has no knowledge of the format of the data returned by the Proxy.<br>
12997  * <p>
12998  * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
12999  * instances from the data object. These records are cached and made available through accessor functions.
13000  * @constructor
13001  * Creates a new Store.
13002  * @param {Object} config A config object containing the objects needed for the Store to access data,
13003  * and read the data into Records.
13004  */
13005 Roo.data.Store = function(config){
13006     this.data = new Roo.util.MixedCollection(false);
13007     this.data.getKey = function(o){
13008         return o.id;
13009     };
13010     this.baseParams = {};
13011     // private
13012     this.paramNames = {
13013         "start" : "start",
13014         "limit" : "limit",
13015         "sort" : "sort",
13016         "dir" : "dir",
13017         "multisort" : "_multisort"
13018     };
13019
13020     if(config && config.data){
13021         this.inlineData = config.data;
13022         delete config.data;
13023     }
13024
13025     Roo.apply(this, config);
13026     
13027     if(this.reader){ // reader passed
13028         this.reader = Roo.factory(this.reader, Roo.data);
13029         this.reader.xmodule = this.xmodule || false;
13030         if(!this.recordType){
13031             this.recordType = this.reader.recordType;
13032         }
13033         if(this.reader.onMetaChange){
13034             this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
13035         }
13036     }
13037
13038     if(this.recordType){
13039         this.fields = this.recordType.prototype.fields;
13040     }
13041     this.modified = [];
13042
13043     this.addEvents({
13044         /**
13045          * @event datachanged
13046          * Fires when the data cache has changed, and a widget which is using this Store
13047          * as a Record cache should refresh its view.
13048          * @param {Store} this
13049          */
13050         datachanged : true,
13051         /**
13052          * @event metachange
13053          * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
13054          * @param {Store} this
13055          * @param {Object} meta The JSON metadata
13056          */
13057         metachange : true,
13058         /**
13059          * @event add
13060          * Fires when Records have been added to the Store
13061          * @param {Store} this
13062          * @param {Roo.data.Record[]} records The array of Records added
13063          * @param {Number} index The index at which the record(s) were added
13064          */
13065         add : true,
13066         /**
13067          * @event remove
13068          * Fires when a Record has been removed from the Store
13069          * @param {Store} this
13070          * @param {Roo.data.Record} record The Record that was removed
13071          * @param {Number} index The index at which the record was removed
13072          */
13073         remove : true,
13074         /**
13075          * @event update
13076          * Fires when a Record has been updated
13077          * @param {Store} this
13078          * @param {Roo.data.Record} record The Record that was updated
13079          * @param {String} operation The update operation being performed.  Value may be one of:
13080          * <pre><code>
13081  Roo.data.Record.EDIT
13082  Roo.data.Record.REJECT
13083  Roo.data.Record.COMMIT
13084          * </code></pre>
13085          */
13086         update : true,
13087         /**
13088          * @event clear
13089          * Fires when the data cache has been cleared.
13090          * @param {Store} this
13091          */
13092         clear : true,
13093         /**
13094          * @event beforeload
13095          * Fires before a request is made for a new data object.  If the beforeload handler returns false
13096          * the load action will be canceled.
13097          * @param {Store} this
13098          * @param {Object} options The loading options that were specified (see {@link #load} for details)
13099          */
13100         beforeload : true,
13101         /**
13102          * @event beforeloadadd
13103          * Fires after a new set of Records has been loaded.
13104          * @param {Store} this
13105          * @param {Roo.data.Record[]} records The Records that were loaded
13106          * @param {Object} options The loading options that were specified (see {@link #load} for details)
13107          */
13108         beforeloadadd : true,
13109         /**
13110          * @event load
13111          * Fires after a new set of Records has been loaded, before they are added to the store.
13112          * @param {Store} this
13113          * @param {Roo.data.Record[]} records The Records that were loaded
13114          * @param {Object} options The loading options that were specified (see {@link #load} for details)
13115          * @params {Object} return from reader
13116          */
13117         load : true,
13118         /**
13119          * @event loadexception
13120          * Fires if an exception occurs in the Proxy during loading.
13121          * Called with the signature of the Proxy's "loadexception" event.
13122          * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
13123          * 
13124          * @param {Proxy} 
13125          * @param {Object} return from JsonData.reader() - success, totalRecords, records
13126          * @param {Object} load options 
13127          * @param {Object} jsonData from your request (normally this contains the Exception)
13128          */
13129         loadexception : true
13130     });
13131     
13132     if(this.proxy){
13133         this.proxy = Roo.factory(this.proxy, Roo.data);
13134         this.proxy.xmodule = this.xmodule || false;
13135         this.relayEvents(this.proxy,  ["loadexception"]);
13136     }
13137     this.sortToggle = {};
13138     this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
13139
13140     Roo.data.Store.superclass.constructor.call(this);
13141
13142     if(this.inlineData){
13143         this.loadData(this.inlineData);
13144         delete this.inlineData;
13145     }
13146 };
13147
13148 Roo.extend(Roo.data.Store, Roo.util.Observable, {
13149      /**
13150     * @cfg {boolean} isLocal   flag if data is locally available (and can be always looked up
13151     * without a remote query - used by combo/forms at present.
13152     */
13153     
13154     /**
13155     * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
13156     */
13157     /**
13158     * @cfg {Array} data Inline data to be loaded when the store is initialized.
13159     */
13160     /**
13161     * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
13162     * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
13163     */
13164     /**
13165     * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
13166     * on any HTTP request
13167     */
13168     /**
13169     * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
13170     */
13171     /**
13172     * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
13173     */
13174     multiSort: false,
13175     /**
13176     * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
13177     * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
13178     */
13179     remoteSort : false,
13180
13181     /**
13182     * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
13183      * loaded or when a record is removed. (defaults to false).
13184     */
13185     pruneModifiedRecords : false,
13186
13187     // private
13188     lastOptions : null,
13189
13190     /**
13191      * Add Records to the Store and fires the add event.
13192      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
13193      */
13194     add : function(records){
13195         records = [].concat(records);
13196         for(var i = 0, len = records.length; i < len; i++){
13197             records[i].join(this);
13198         }
13199         var index = this.data.length;
13200         this.data.addAll(records);
13201         this.fireEvent("add", this, records, index);
13202     },
13203
13204     /**
13205      * Remove a Record from the Store and fires the remove event.
13206      * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
13207      */
13208     remove : function(record){
13209         var index = this.data.indexOf(record);
13210         this.data.removeAt(index);
13211  
13212         if(this.pruneModifiedRecords){
13213             this.modified.remove(record);
13214         }
13215         this.fireEvent("remove", this, record, index);
13216     },
13217
13218     /**
13219      * Remove all Records from the Store and fires the clear event.
13220      */
13221     removeAll : function(){
13222         this.data.clear();
13223         if(this.pruneModifiedRecords){
13224             this.modified = [];
13225         }
13226         this.fireEvent("clear", this);
13227     },
13228
13229     /**
13230      * Inserts Records to the Store at the given index and fires the add event.
13231      * @param {Number} index The start index at which to insert the passed Records.
13232      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
13233      */
13234     insert : function(index, records){
13235         records = [].concat(records);
13236         for(var i = 0, len = records.length; i < len; i++){
13237             this.data.insert(index, records[i]);
13238             records[i].join(this);
13239         }
13240         this.fireEvent("add", this, records, index);
13241     },
13242
13243     /**
13244      * Get the index within the cache of the passed Record.
13245      * @param {Roo.data.Record} record The Roo.data.Record object to to find.
13246      * @return {Number} The index of the passed Record. Returns -1 if not found.
13247      */
13248     indexOf : function(record){
13249         return this.data.indexOf(record);
13250     },
13251
13252     /**
13253      * Get the index within the cache of the Record with the passed id.
13254      * @param {String} id The id of the Record to find.
13255      * @return {Number} The index of the Record. Returns -1 if not found.
13256      */
13257     indexOfId : function(id){
13258         return this.data.indexOfKey(id);
13259     },
13260
13261     /**
13262      * Get the Record with the specified id.
13263      * @param {String} id The id of the Record to find.
13264      * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
13265      */
13266     getById : function(id){
13267         return this.data.key(id);
13268     },
13269
13270     /**
13271      * Get the Record at the specified index.
13272      * @param {Number} index The index of the Record to find.
13273      * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
13274      */
13275     getAt : function(index){
13276         return this.data.itemAt(index);
13277     },
13278
13279     /**
13280      * Returns a range of Records between specified indices.
13281      * @param {Number} startIndex (optional) The starting index (defaults to 0)
13282      * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
13283      * @return {Roo.data.Record[]} An array of Records
13284      */
13285     getRange : function(start, end){
13286         return this.data.getRange(start, end);
13287     },
13288
13289     // private
13290     storeOptions : function(o){
13291         o = Roo.apply({}, o);
13292         delete o.callback;
13293         delete o.scope;
13294         this.lastOptions = o;
13295     },
13296
13297     /**
13298      * Loads the Record cache from the configured Proxy using the configured Reader.
13299      * <p>
13300      * If using remote paging, then the first load call must specify the <em>start</em>
13301      * and <em>limit</em> properties in the options.params property to establish the initial
13302      * position within the dataset, and the number of Records to cache on each read from the Proxy.
13303      * <p>
13304      * <strong>It is important to note that for remote data sources, loading is asynchronous,
13305      * and this call will return before the new data has been loaded. Perform any post-processing
13306      * in a callback function, or in a "load" event handler.</strong>
13307      * <p>
13308      * @param {Object} options An object containing properties which control loading options:<ul>
13309      * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
13310      * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
13311      * passed the following arguments:<ul>
13312      * <li>r : Roo.data.Record[]</li>
13313      * <li>options: Options object from the load call</li>
13314      * <li>success: Boolean success indicator</li></ul></li>
13315      * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
13316      * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
13317      * </ul>
13318      */
13319     load : function(options){
13320         options = options || {};
13321         if(this.fireEvent("beforeload", this, options) !== false){
13322             this.storeOptions(options);
13323             var p = Roo.apply(options.params || {}, this.baseParams);
13324             // if meta was not loaded from remote source.. try requesting it.
13325             if (!this.reader.metaFromRemote) {
13326                 p._requestMeta = 1;
13327             }
13328             if(this.sortInfo && this.remoteSort){
13329                 var pn = this.paramNames;
13330                 p[pn["sort"]] = this.sortInfo.field;
13331                 p[pn["dir"]] = this.sortInfo.direction;
13332             }
13333             if (this.multiSort) {
13334                 var pn = this.paramNames;
13335                 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
13336             }
13337             
13338             this.proxy.load(p, this.reader, this.loadRecords, this, options);
13339         }
13340     },
13341
13342     /**
13343      * Reloads the Record cache from the configured Proxy using the configured Reader and
13344      * the options from the last load operation performed.
13345      * @param {Object} options (optional) An object containing properties which may override the options
13346      * used in the last load operation. See {@link #load} for details (defaults to null, in which case
13347      * the most recently used options are reused).
13348      */
13349     reload : function(options){
13350         this.load(Roo.applyIf(options||{}, this.lastOptions));
13351     },
13352
13353     // private
13354     // Called as a callback by the Reader during a load operation.
13355     loadRecords : function(o, options, success){
13356         if(!o || success === false){
13357             if(success !== false){
13358                 this.fireEvent("load", this, [], options, o);
13359             }
13360             if(options.callback){
13361                 options.callback.call(options.scope || this, [], options, false);
13362             }
13363             return;
13364         }
13365         // if data returned failure - throw an exception.
13366         if (o.success === false) {
13367             // show a message if no listener is registered.
13368             if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
13369                     Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
13370             }
13371             // loadmask wil be hooked into this..
13372             this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
13373             return;
13374         }
13375         var r = o.records, t = o.totalRecords || r.length;
13376         
13377         this.fireEvent("beforeloadadd", this, r, options, o);
13378         
13379         if(!options || options.add !== true){
13380             if(this.pruneModifiedRecords){
13381                 this.modified = [];
13382             }
13383             for(var i = 0, len = r.length; i < len; i++){
13384                 r[i].join(this);
13385             }
13386             if(this.snapshot){
13387                 this.data = this.snapshot;
13388                 delete this.snapshot;
13389             }
13390             this.data.clear();
13391             this.data.addAll(r);
13392             this.totalLength = t;
13393             this.applySort();
13394             this.fireEvent("datachanged", this);
13395         }else{
13396             this.totalLength = Math.max(t, this.data.length+r.length);
13397             this.add(r);
13398         }
13399         
13400         if(this.parent && !Roo.isIOS && !this.useNativeIOS && this.parent.emptyTitle.length) {
13401                 
13402             var e = new Roo.data.Record({});
13403
13404             e.set(this.parent.displayField, this.parent.emptyTitle);
13405             e.set(this.parent.valueField, '');
13406
13407             this.insert(0, e);
13408         }
13409             
13410         this.fireEvent("load", this, r, options, o);
13411         if(options.callback){
13412             options.callback.call(options.scope || this, r, options, true);
13413         }
13414     },
13415
13416
13417     /**
13418      * Loads data from a passed data block. A Reader which understands the format of the data
13419      * must have been configured in the constructor.
13420      * @param {Object} data The data block from which to read the Records.  The format of the data expected
13421      * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
13422      * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
13423      */
13424     loadData : function(o, append){
13425         var r = this.reader.readRecords(o);
13426         this.loadRecords(r, {add: append}, true);
13427     },
13428     
13429      /**
13430      * using 'cn' the nested child reader read the child array into it's child stores.
13431      * @param {Object} rec The record with a 'children array
13432      */
13433     loadDataFromChildren : function(rec)
13434     {
13435         this.loadData(this.reader.toLoadData(rec));
13436     },
13437     
13438
13439     /**
13440      * Gets the number of cached records.
13441      * <p>
13442      * <em>If using paging, this may not be the total size of the dataset. If the data object
13443      * used by the Reader contains the dataset size, then the getTotalCount() function returns
13444      * the data set size</em>
13445      */
13446     getCount : function(){
13447         return this.data.length || 0;
13448     },
13449
13450     /**
13451      * Gets the total number of records in the dataset as returned by the server.
13452      * <p>
13453      * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
13454      * the dataset size</em>
13455      */
13456     getTotalCount : function(){
13457         return this.totalLength || 0;
13458     },
13459
13460     /**
13461      * Returns the sort state of the Store as an object with two properties:
13462      * <pre><code>
13463  field {String} The name of the field by which the Records are sorted
13464  direction {String} The sort order, "ASC" or "DESC"
13465      * </code></pre>
13466      */
13467     getSortState : function(){
13468         return this.sortInfo;
13469     },
13470
13471     // private
13472     applySort : function(){
13473         if(this.sortInfo && !this.remoteSort){
13474             var s = this.sortInfo, f = s.field;
13475             var st = this.fields.get(f).sortType;
13476             var fn = function(r1, r2){
13477                 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
13478                 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
13479             };
13480             this.data.sort(s.direction, fn);
13481             if(this.snapshot && this.snapshot != this.data){
13482                 this.snapshot.sort(s.direction, fn);
13483             }
13484         }
13485     },
13486
13487     /**
13488      * Sets the default sort column and order to be used by the next load operation.
13489      * @param {String} fieldName The name of the field to sort by.
13490      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
13491      */
13492     setDefaultSort : function(field, dir){
13493         this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
13494     },
13495
13496     /**
13497      * Sort the Records.
13498      * If remote sorting is used, the sort is performed on the server, and the cache is
13499      * reloaded. If local sorting is used, the cache is sorted internally.
13500      * @param {String} fieldName The name of the field to sort by.
13501      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
13502      */
13503     sort : function(fieldName, dir){
13504         var f = this.fields.get(fieldName);
13505         if(!dir){
13506             this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
13507             
13508             if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
13509                 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
13510             }else{
13511                 dir = f.sortDir;
13512             }
13513         }
13514         this.sortToggle[f.name] = dir;
13515         this.sortInfo = {field: f.name, direction: dir};
13516         if(!this.remoteSort){
13517             this.applySort();
13518             this.fireEvent("datachanged", this);
13519         }else{
13520             this.load(this.lastOptions);
13521         }
13522     },
13523
13524     /**
13525      * Calls the specified function for each of the Records in the cache.
13526      * @param {Function} fn The function to call. The Record is passed as the first parameter.
13527      * Returning <em>false</em> aborts and exits the iteration.
13528      * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
13529      */
13530     each : function(fn, scope){
13531         this.data.each(fn, scope);
13532     },
13533
13534     /**
13535      * Gets all records modified since the last commit.  Modified records are persisted across load operations
13536      * (e.g., during paging).
13537      * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
13538      */
13539     getModifiedRecords : function(){
13540         return this.modified;
13541     },
13542
13543     // private
13544     createFilterFn : function(property, value, anyMatch){
13545         if(!value.exec){ // not a regex
13546             value = String(value);
13547             if(value.length == 0){
13548                 return false;
13549             }
13550             value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
13551         }
13552         return function(r){
13553             return value.test(r.data[property]);
13554         };
13555     },
13556
13557     /**
13558      * Sums the value of <i>property</i> for each record between start and end and returns the result.
13559      * @param {String} property A field on your records
13560      * @param {Number} start The record index to start at (defaults to 0)
13561      * @param {Number} end The last record index to include (defaults to length - 1)
13562      * @return {Number} The sum
13563      */
13564     sum : function(property, start, end){
13565         var rs = this.data.items, v = 0;
13566         start = start || 0;
13567         end = (end || end === 0) ? end : rs.length-1;
13568
13569         for(var i = start; i <= end; i++){
13570             v += (rs[i].data[property] || 0);
13571         }
13572         return v;
13573     },
13574
13575     /**
13576      * Filter the records by a specified property.
13577      * @param {String} field A field on your records
13578      * @param {String/RegExp} value Either a string that the field
13579      * should start with or a RegExp to test against the field
13580      * @param {Boolean} anyMatch True to match any part not just the beginning
13581      */
13582     filter : function(property, value, anyMatch){
13583         var fn = this.createFilterFn(property, value, anyMatch);
13584         return fn ? this.filterBy(fn) : this.clearFilter();
13585     },
13586
13587     /**
13588      * Filter by a function. The specified function will be called with each
13589      * record in this data source. If the function returns true the record is included,
13590      * otherwise it is filtered.
13591      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
13592      * @param {Object} scope (optional) The scope of the function (defaults to this)
13593      */
13594     filterBy : function(fn, scope){
13595         this.snapshot = this.snapshot || this.data;
13596         this.data = this.queryBy(fn, scope||this);
13597         this.fireEvent("datachanged", this);
13598     },
13599
13600     /**
13601      * Query the records by a specified property.
13602      * @param {String} field A field on your records
13603      * @param {String/RegExp} value Either a string that the field
13604      * should start with or a RegExp to test against the field
13605      * @param {Boolean} anyMatch True to match any part not just the beginning
13606      * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
13607      */
13608     query : function(property, value, anyMatch){
13609         var fn = this.createFilterFn(property, value, anyMatch);
13610         return fn ? this.queryBy(fn) : this.data.clone();
13611     },
13612
13613     /**
13614      * Query by a function. The specified function will be called with each
13615      * record in this data source. If the function returns true the record is included
13616      * in the results.
13617      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
13618      * @param {Object} scope (optional) The scope of the function (defaults to this)
13619       @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
13620      **/
13621     queryBy : function(fn, scope){
13622         var data = this.snapshot || this.data;
13623         return data.filterBy(fn, scope||this);
13624     },
13625
13626     /**
13627      * Collects unique values for a particular dataIndex from this store.
13628      * @param {String} dataIndex The property to collect
13629      * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
13630      * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
13631      * @return {Array} An array of the unique values
13632      **/
13633     collect : function(dataIndex, allowNull, bypassFilter){
13634         var d = (bypassFilter === true && this.snapshot) ?
13635                 this.snapshot.items : this.data.items;
13636         var v, sv, r = [], l = {};
13637         for(var i = 0, len = d.length; i < len; i++){
13638             v = d[i].data[dataIndex];
13639             sv = String(v);
13640             if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
13641                 l[sv] = true;
13642                 r[r.length] = v;
13643             }
13644         }
13645         return r;
13646     },
13647
13648     /**
13649      * Revert to a view of the Record cache with no filtering applied.
13650      * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
13651      */
13652     clearFilter : function(suppressEvent){
13653         if(this.snapshot && this.snapshot != this.data){
13654             this.data = this.snapshot;
13655             delete this.snapshot;
13656             if(suppressEvent !== true){
13657                 this.fireEvent("datachanged", this);
13658             }
13659         }
13660     },
13661
13662     // private
13663     afterEdit : function(record){
13664         if(this.modified.indexOf(record) == -1){
13665             this.modified.push(record);
13666         }
13667         this.fireEvent("update", this, record, Roo.data.Record.EDIT);
13668     },
13669     
13670     // private
13671     afterReject : function(record){
13672         this.modified.remove(record);
13673         this.fireEvent("update", this, record, Roo.data.Record.REJECT);
13674     },
13675
13676     // private
13677     afterCommit : function(record){
13678         this.modified.remove(record);
13679         this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
13680     },
13681
13682     /**
13683      * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
13684      * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
13685      */
13686     commitChanges : function(){
13687         var m = this.modified.slice(0);
13688         this.modified = [];
13689         for(var i = 0, len = m.length; i < len; i++){
13690             m[i].commit();
13691         }
13692     },
13693
13694     /**
13695      * Cancel outstanding changes on all changed records.
13696      */
13697     rejectChanges : function(){
13698         var m = this.modified.slice(0);
13699         this.modified = [];
13700         for(var i = 0, len = m.length; i < len; i++){
13701             m[i].reject();
13702         }
13703     },
13704
13705     onMetaChange : function(meta, rtype, o){
13706         this.recordType = rtype;
13707         this.fields = rtype.prototype.fields;
13708         delete this.snapshot;
13709         this.sortInfo = meta.sortInfo || this.sortInfo;
13710         this.modified = [];
13711         this.fireEvent('metachange', this, this.reader.meta);
13712     },
13713     
13714     moveIndex : function(data, type)
13715     {
13716         var index = this.indexOf(data);
13717         
13718         var newIndex = index + type;
13719         
13720         this.remove(data);
13721         
13722         this.insert(newIndex, data);
13723         
13724     }
13725 });/*
13726  * Based on:
13727  * Ext JS Library 1.1.1
13728  * Copyright(c) 2006-2007, Ext JS, LLC.
13729  *
13730  * Originally Released Under LGPL - original licence link has changed is not relivant.
13731  *
13732  * Fork - LGPL
13733  * <script type="text/javascript">
13734  */
13735
13736 /**
13737  * @class Roo.data.SimpleStore
13738  * @extends Roo.data.Store
13739  * Small helper class to make creating Stores from Array data easier.
13740  * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
13741  * @cfg {Array} fields An array of field definition objects, or field name strings.
13742  * @cfg {Object} an existing reader (eg. copied from another store)
13743  * @cfg {Array} data The multi-dimensional array of data
13744  * @constructor
13745  * @param {Object} config
13746  */
13747 Roo.data.SimpleStore = function(config)
13748 {
13749     Roo.data.SimpleStore.superclass.constructor.call(this, {
13750         isLocal : true,
13751         reader: typeof(config.reader) != 'undefined' ? config.reader : new Roo.data.ArrayReader({
13752                 id: config.id
13753             },
13754             Roo.data.Record.create(config.fields)
13755         ),
13756         proxy : new Roo.data.MemoryProxy(config.data)
13757     });
13758     this.load();
13759 };
13760 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
13761  * Based on:
13762  * Ext JS Library 1.1.1
13763  * Copyright(c) 2006-2007, Ext JS, LLC.
13764  *
13765  * Originally Released Under LGPL - original licence link has changed is not relivant.
13766  *
13767  * Fork - LGPL
13768  * <script type="text/javascript">
13769  */
13770
13771 /**
13772 /**
13773  * @extends Roo.data.Store
13774  * @class Roo.data.JsonStore
13775  * Small helper class to make creating Stores for JSON data easier. <br/>
13776 <pre><code>
13777 var store = new Roo.data.JsonStore({
13778     url: 'get-images.php',
13779     root: 'images',
13780     fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
13781 });
13782 </code></pre>
13783  * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
13784  * JsonReader and HttpProxy (unless inline data is provided).</b>
13785  * @cfg {Array} fields An array of field definition objects, or field name strings.
13786  * @constructor
13787  * @param {Object} config
13788  */
13789 Roo.data.JsonStore = function(c){
13790     Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
13791         proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
13792         reader: new Roo.data.JsonReader(c, c.fields)
13793     }));
13794 };
13795 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
13796  * Based on:
13797  * Ext JS Library 1.1.1
13798  * Copyright(c) 2006-2007, Ext JS, LLC.
13799  *
13800  * Originally Released Under LGPL - original licence link has changed is not relivant.
13801  *
13802  * Fork - LGPL
13803  * <script type="text/javascript">
13804  */
13805
13806  
13807 Roo.data.Field = function(config){
13808     if(typeof config == "string"){
13809         config = {name: config};
13810     }
13811     Roo.apply(this, config);
13812     
13813     if(!this.type){
13814         this.type = "auto";
13815     }
13816     
13817     var st = Roo.data.SortTypes;
13818     // named sortTypes are supported, here we look them up
13819     if(typeof this.sortType == "string"){
13820         this.sortType = st[this.sortType];
13821     }
13822     
13823     // set default sortType for strings and dates
13824     if(!this.sortType){
13825         switch(this.type){
13826             case "string":
13827                 this.sortType = st.asUCString;
13828                 break;
13829             case "date":
13830                 this.sortType = st.asDate;
13831                 break;
13832             default:
13833                 this.sortType = st.none;
13834         }
13835     }
13836
13837     // define once
13838     var stripRe = /[\$,%]/g;
13839
13840     // prebuilt conversion function for this field, instead of
13841     // switching every time we're reading a value
13842     if(!this.convert){
13843         var cv, dateFormat = this.dateFormat;
13844         switch(this.type){
13845             case "":
13846             case "auto":
13847             case undefined:
13848                 cv = function(v){ return v; };
13849                 break;
13850             case "string":
13851                 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
13852                 break;
13853             case "int":
13854                 cv = function(v){
13855                     return v !== undefined && v !== null && v !== '' ?
13856                            parseInt(String(v).replace(stripRe, ""), 10) : '';
13857                     };
13858                 break;
13859             case "float":
13860                 cv = function(v){
13861                     return v !== undefined && v !== null && v !== '' ?
13862                            parseFloat(String(v).replace(stripRe, ""), 10) : ''; 
13863                     };
13864                 break;
13865             case "bool":
13866             case "boolean":
13867                 cv = function(v){ return v === true || v === "true" || v == 1; };
13868                 break;
13869             case "date":
13870                 cv = function(v){
13871                     if(!v){
13872                         return '';
13873                     }
13874                     if(v instanceof Date){
13875                         return v;
13876                     }
13877                     if(dateFormat){
13878                         if(dateFormat == "timestamp"){
13879                             return new Date(v*1000);
13880                         }
13881                         return Date.parseDate(v, dateFormat);
13882                     }
13883                     var parsed = Date.parse(v);
13884                     return parsed ? new Date(parsed) : null;
13885                 };
13886              break;
13887             
13888         }
13889         this.convert = cv;
13890     }
13891 };
13892
13893 Roo.data.Field.prototype = {
13894     dateFormat: null,
13895     defaultValue: "",
13896     mapping: null,
13897     sortType : null,
13898     sortDir : "ASC"
13899 };/*
13900  * Based on:
13901  * Ext JS Library 1.1.1
13902  * Copyright(c) 2006-2007, Ext JS, LLC.
13903  *
13904  * Originally Released Under LGPL - original licence link has changed is not relivant.
13905  *
13906  * Fork - LGPL
13907  * <script type="text/javascript">
13908  */
13909  
13910 // Base class for reading structured data from a data source.  This class is intended to be
13911 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
13912
13913 /**
13914  * @class Roo.data.DataReader
13915  * Base class for reading structured data from a data source.  This class is intended to be
13916  * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
13917  */
13918
13919 Roo.data.DataReader = function(meta, recordType){
13920     
13921     this.meta = meta;
13922     
13923     this.recordType = recordType instanceof Array ? 
13924         Roo.data.Record.create(recordType) : recordType;
13925 };
13926
13927 Roo.data.DataReader.prototype = {
13928     
13929     
13930     readerType : 'Data',
13931      /**
13932      * Create an empty record
13933      * @param {Object} data (optional) - overlay some values
13934      * @return {Roo.data.Record} record created.
13935      */
13936     newRow :  function(d) {
13937         var da =  {};
13938         this.recordType.prototype.fields.each(function(c) {
13939             switch( c.type) {
13940                 case 'int' : da[c.name] = 0; break;
13941                 case 'date' : da[c.name] = new Date(); break;
13942                 case 'float' : da[c.name] = 0.0; break;
13943                 case 'boolean' : da[c.name] = false; break;
13944                 default : da[c.name] = ""; break;
13945             }
13946             
13947         });
13948         return new this.recordType(Roo.apply(da, d));
13949     }
13950     
13951     
13952 };/*
13953  * Based on:
13954  * Ext JS Library 1.1.1
13955  * Copyright(c) 2006-2007, Ext JS, LLC.
13956  *
13957  * Originally Released Under LGPL - original licence link has changed is not relivant.
13958  *
13959  * Fork - LGPL
13960  * <script type="text/javascript">
13961  */
13962
13963 /**
13964  * @class Roo.data.DataProxy
13965  * @extends Roo.data.Observable
13966  * This class is an abstract base class for implementations which provide retrieval of
13967  * unformatted data objects.<br>
13968  * <p>
13969  * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
13970  * (of the appropriate type which knows how to parse the data object) to provide a block of
13971  * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
13972  * <p>
13973  * Custom implementations must implement the load method as described in
13974  * {@link Roo.data.HttpProxy#load}.
13975  */
13976 Roo.data.DataProxy = function(){
13977     this.addEvents({
13978         /**
13979          * @event beforeload
13980          * Fires before a network request is made to retrieve a data object.
13981          * @param {Object} This DataProxy object.
13982          * @param {Object} params The params parameter to the load function.
13983          */
13984         beforeload : true,
13985         /**
13986          * @event load
13987          * Fires before the load method's callback is called.
13988          * @param {Object} This DataProxy object.
13989          * @param {Object} o The data object.
13990          * @param {Object} arg The callback argument object passed to the load function.
13991          */
13992         load : true,
13993         /**
13994          * @event loadexception
13995          * Fires if an Exception occurs during data retrieval.
13996          * @param {Object} This DataProxy object.
13997          * @param {Object} o The data object.
13998          * @param {Object} arg The callback argument object passed to the load function.
13999          * @param {Object} e The Exception.
14000          */
14001         loadexception : true
14002     });
14003     Roo.data.DataProxy.superclass.constructor.call(this);
14004 };
14005
14006 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
14007
14008     /**
14009      * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
14010      */
14011 /*
14012  * Based on:
14013  * Ext JS Library 1.1.1
14014  * Copyright(c) 2006-2007, Ext JS, LLC.
14015  *
14016  * Originally Released Under LGPL - original licence link has changed is not relivant.
14017  *
14018  * Fork - LGPL
14019  * <script type="text/javascript">
14020  */
14021 /**
14022  * @class Roo.data.MemoryProxy
14023  * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
14024  * to the Reader when its load method is called.
14025  * @constructor
14026  * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
14027  */
14028 Roo.data.MemoryProxy = function(data){
14029     if (data.data) {
14030         data = data.data;
14031     }
14032     Roo.data.MemoryProxy.superclass.constructor.call(this);
14033     this.data = data;
14034 };
14035
14036 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
14037     
14038     /**
14039      * Load data from the requested source (in this case an in-memory
14040      * data object passed to the constructor), read the data object into
14041      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
14042      * process that block using the passed callback.
14043      * @param {Object} params This parameter is not used by the MemoryProxy class.
14044      * @param {Roo.data.DataReader} reader The Reader object which converts the data
14045      * object into a block of Roo.data.Records.
14046      * @param {Function} callback The function into which to pass the block of Roo.data.records.
14047      * The function must be passed <ul>
14048      * <li>The Record block object</li>
14049      * <li>The "arg" argument from the load function</li>
14050      * <li>A boolean success indicator</li>
14051      * </ul>
14052      * @param {Object} scope The scope in which to call the callback
14053      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
14054      */
14055     load : function(params, reader, callback, scope, arg){
14056         params = params || {};
14057         var result;
14058         try {
14059             result = reader.readRecords(params.data ? params.data :this.data);
14060         }catch(e){
14061             this.fireEvent("loadexception", this, arg, null, e);
14062             callback.call(scope, null, arg, false);
14063             return;
14064         }
14065         callback.call(scope, result, arg, true);
14066     },
14067     
14068     // private
14069     update : function(params, records){
14070         
14071     }
14072 });/*
14073  * Based on:
14074  * Ext JS Library 1.1.1
14075  * Copyright(c) 2006-2007, Ext JS, LLC.
14076  *
14077  * Originally Released Under LGPL - original licence link has changed is not relivant.
14078  *
14079  * Fork - LGPL
14080  * <script type="text/javascript">
14081  */
14082 /**
14083  * @class Roo.data.HttpProxy
14084  * @extends Roo.data.DataProxy
14085  * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
14086  * configured to reference a certain URL.<br><br>
14087  * <p>
14088  * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
14089  * from which the running page was served.<br><br>
14090  * <p>
14091  * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
14092  * <p>
14093  * Be aware that to enable the browser to parse an XML document, the server must set
14094  * the Content-Type header in the HTTP response to "text/xml".
14095  * @constructor
14096  * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
14097  * an {@link Roo.data.Connection} object.  If a Connection config is passed, the singleton {@link Roo.Ajax} object
14098  * will be used to make the request.
14099  */
14100 Roo.data.HttpProxy = function(conn){
14101     Roo.data.HttpProxy.superclass.constructor.call(this);
14102     // is conn a conn config or a real conn?
14103     this.conn = conn;
14104     this.useAjax = !conn || !conn.events;
14105   
14106 };
14107
14108 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
14109     // thse are take from connection...
14110     
14111     /**
14112      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
14113      */
14114     /**
14115      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
14116      * extra parameters to each request made by this object. (defaults to undefined)
14117      */
14118     /**
14119      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
14120      *  to each request made by this object. (defaults to undefined)
14121      */
14122     /**
14123      * @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)
14124      */
14125     /**
14126      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
14127      */
14128      /**
14129      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
14130      * @type Boolean
14131      */
14132   
14133
14134     /**
14135      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
14136      * @type Boolean
14137      */
14138     /**
14139      * Return the {@link Roo.data.Connection} object being used by this Proxy.
14140      * @return {Connection} The Connection object. This object may be used to subscribe to events on
14141      * a finer-grained basis than the DataProxy events.
14142      */
14143     getConnection : function(){
14144         return this.useAjax ? Roo.Ajax : this.conn;
14145     },
14146
14147     /**
14148      * Load data from the configured {@link Roo.data.Connection}, read the data object into
14149      * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
14150      * process that block using the passed callback.
14151      * @param {Object} params An object containing properties which are to be used as HTTP parameters
14152      * for the request to the remote server.
14153      * @param {Roo.data.DataReader} reader The Reader object which converts the data
14154      * object into a block of Roo.data.Records.
14155      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
14156      * The function must be passed <ul>
14157      * <li>The Record block object</li>
14158      * <li>The "arg" argument from the load function</li>
14159      * <li>A boolean success indicator</li>
14160      * </ul>
14161      * @param {Object} scope The scope in which to call the callback
14162      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
14163      */
14164     load : function(params, reader, callback, scope, arg){
14165         if(this.fireEvent("beforeload", this, params) !== false){
14166             var  o = {
14167                 params : params || {},
14168                 request: {
14169                     callback : callback,
14170                     scope : scope,
14171                     arg : arg
14172                 },
14173                 reader: reader,
14174                 callback : this.loadResponse,
14175                 scope: this
14176             };
14177             if(this.useAjax){
14178                 Roo.applyIf(o, this.conn);
14179                 if(this.activeRequest){
14180                     Roo.Ajax.abort(this.activeRequest);
14181                 }
14182                 this.activeRequest = Roo.Ajax.request(o);
14183             }else{
14184                 this.conn.request(o);
14185             }
14186         }else{
14187             callback.call(scope||this, null, arg, false);
14188         }
14189     },
14190
14191     // private
14192     loadResponse : function(o, success, response){
14193         delete this.activeRequest;
14194         if(!success){
14195             this.fireEvent("loadexception", this, o, response);
14196             o.request.callback.call(o.request.scope, null, o.request.arg, false);
14197             return;
14198         }
14199         var result;
14200         try {
14201             result = o.reader.read(response);
14202         }catch(e){
14203             this.fireEvent("loadexception", this, o, response, e);
14204             o.request.callback.call(o.request.scope, null, o.request.arg, false);
14205             return;
14206         }
14207         
14208         this.fireEvent("load", this, o, o.request.arg);
14209         o.request.callback.call(o.request.scope, result, o.request.arg, true);
14210     },
14211
14212     // private
14213     update : function(dataSet){
14214
14215     },
14216
14217     // private
14218     updateResponse : function(dataSet){
14219
14220     }
14221 });/*
14222  * Based on:
14223  * Ext JS Library 1.1.1
14224  * Copyright(c) 2006-2007, Ext JS, LLC.
14225  *
14226  * Originally Released Under LGPL - original licence link has changed is not relivant.
14227  *
14228  * Fork - LGPL
14229  * <script type="text/javascript">
14230  */
14231
14232 /**
14233  * @class Roo.data.ScriptTagProxy
14234  * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
14235  * other than the originating domain of the running page.<br><br>
14236  * <p>
14237  * <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
14238  * of the running page, you must use this class, rather than DataProxy.</em><br><br>
14239  * <p>
14240  * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
14241  * source code that is used as the source inside a &lt;script> tag.<br><br>
14242  * <p>
14243  * In order for the browser to process the returned data, the server must wrap the data object
14244  * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
14245  * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
14246  * depending on whether the callback name was passed:
14247  * <p>
14248  * <pre><code>
14249 boolean scriptTag = false;
14250 String cb = request.getParameter("callback");
14251 if (cb != null) {
14252     scriptTag = true;
14253     response.setContentType("text/javascript");
14254 } else {
14255     response.setContentType("application/x-json");
14256 }
14257 Writer out = response.getWriter();
14258 if (scriptTag) {
14259     out.write(cb + "(");
14260 }
14261 out.print(dataBlock.toJsonString());
14262 if (scriptTag) {
14263     out.write(");");
14264 }
14265 </pre></code>
14266  *
14267  * @constructor
14268  * @param {Object} config A configuration object.
14269  */
14270 Roo.data.ScriptTagProxy = function(config){
14271     Roo.data.ScriptTagProxy.superclass.constructor.call(this);
14272     Roo.apply(this, config);
14273     this.head = document.getElementsByTagName("head")[0];
14274 };
14275
14276 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
14277
14278 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
14279     /**
14280      * @cfg {String} url The URL from which to request the data object.
14281      */
14282     /**
14283      * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
14284      */
14285     timeout : 30000,
14286     /**
14287      * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
14288      * the server the name of the callback function set up by the load call to process the returned data object.
14289      * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
14290      * javascript output which calls this named function passing the data object as its only parameter.
14291      */
14292     callbackParam : "callback",
14293     /**
14294      *  @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
14295      * name to the request.
14296      */
14297     nocache : true,
14298
14299     /**
14300      * Load data from the configured URL, read the data object into
14301      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
14302      * process that block using the passed callback.
14303      * @param {Object} params An object containing properties which are to be used as HTTP parameters
14304      * for the request to the remote server.
14305      * @param {Roo.data.DataReader} reader The Reader object which converts the data
14306      * object into a block of Roo.data.Records.
14307      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
14308      * The function must be passed <ul>
14309      * <li>The Record block object</li>
14310      * <li>The "arg" argument from the load function</li>
14311      * <li>A boolean success indicator</li>
14312      * </ul>
14313      * @param {Object} scope The scope in which to call the callback
14314      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
14315      */
14316     load : function(params, reader, callback, scope, arg){
14317         if(this.fireEvent("beforeload", this, params) !== false){
14318
14319             var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
14320
14321             var url = this.url;
14322             url += (url.indexOf("?") != -1 ? "&" : "?") + p;
14323             if(this.nocache){
14324                 url += "&_dc=" + (new Date().getTime());
14325             }
14326             var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
14327             var trans = {
14328                 id : transId,
14329                 cb : "stcCallback"+transId,
14330                 scriptId : "stcScript"+transId,
14331                 params : params,
14332                 arg : arg,
14333                 url : url,
14334                 callback : callback,
14335                 scope : scope,
14336                 reader : reader
14337             };
14338             var conn = this;
14339
14340             window[trans.cb] = function(o){
14341                 conn.handleResponse(o, trans);
14342             };
14343
14344             url += String.format("&{0}={1}", this.callbackParam, trans.cb);
14345
14346             if(this.autoAbort !== false){
14347                 this.abort();
14348             }
14349
14350             trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
14351
14352             var script = document.createElement("script");
14353             script.setAttribute("src", url);
14354             script.setAttribute("type", "text/javascript");
14355             script.setAttribute("id", trans.scriptId);
14356             this.head.appendChild(script);
14357
14358             this.trans = trans;
14359         }else{
14360             callback.call(scope||this, null, arg, false);
14361         }
14362     },
14363
14364     // private
14365     isLoading : function(){
14366         return this.trans ? true : false;
14367     },
14368
14369     /**
14370      * Abort the current server request.
14371      */
14372     abort : function(){
14373         if(this.isLoading()){
14374             this.destroyTrans(this.trans);
14375         }
14376     },
14377
14378     // private
14379     destroyTrans : function(trans, isLoaded){
14380         this.head.removeChild(document.getElementById(trans.scriptId));
14381         clearTimeout(trans.timeoutId);
14382         if(isLoaded){
14383             window[trans.cb] = undefined;
14384             try{
14385                 delete window[trans.cb];
14386             }catch(e){}
14387         }else{
14388             // if hasn't been loaded, wait for load to remove it to prevent script error
14389             window[trans.cb] = function(){
14390                 window[trans.cb] = undefined;
14391                 try{
14392                     delete window[trans.cb];
14393                 }catch(e){}
14394             };
14395         }
14396     },
14397
14398     // private
14399     handleResponse : function(o, trans){
14400         this.trans = false;
14401         this.destroyTrans(trans, true);
14402         var result;
14403         try {
14404             result = trans.reader.readRecords(o);
14405         }catch(e){
14406             this.fireEvent("loadexception", this, o, trans.arg, e);
14407             trans.callback.call(trans.scope||window, null, trans.arg, false);
14408             return;
14409         }
14410         this.fireEvent("load", this, o, trans.arg);
14411         trans.callback.call(trans.scope||window, result, trans.arg, true);
14412     },
14413
14414     // private
14415     handleFailure : function(trans){
14416         this.trans = false;
14417         this.destroyTrans(trans, false);
14418         this.fireEvent("loadexception", this, null, trans.arg);
14419         trans.callback.call(trans.scope||window, null, trans.arg, false);
14420     }
14421 });/*
14422  * Based on:
14423  * Ext JS Library 1.1.1
14424  * Copyright(c) 2006-2007, Ext JS, LLC.
14425  *
14426  * Originally Released Under LGPL - original licence link has changed is not relivant.
14427  *
14428  * Fork - LGPL
14429  * <script type="text/javascript">
14430  */
14431
14432 /**
14433  * @class Roo.data.JsonReader
14434  * @extends Roo.data.DataReader
14435  * Data reader class to create an Array of Roo.data.Record objects from a JSON response
14436  * based on mappings in a provided Roo.data.Record constructor.
14437  * 
14438  * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
14439  * in the reply previously. 
14440  * 
14441  * <p>
14442  * Example code:
14443  * <pre><code>
14444 var RecordDef = Roo.data.Record.create([
14445     {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
14446     {name: 'occupation'}                 // This field will use "occupation" as the mapping.
14447 ]);
14448 var myReader = new Roo.data.JsonReader({
14449     totalProperty: "results",    // The property which contains the total dataset size (optional)
14450     root: "rows",                // The property which contains an Array of row objects
14451     id: "id"                     // The property within each row object that provides an ID for the record (optional)
14452 }, RecordDef);
14453 </code></pre>
14454  * <p>
14455  * This would consume a JSON file like this:
14456  * <pre><code>
14457 { 'results': 2, 'rows': [
14458     { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
14459     { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
14460 }
14461 </code></pre>
14462  * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
14463  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
14464  * paged from the remote server.
14465  * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
14466  * @cfg {String} root name of the property which contains the Array of row objects.
14467  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
14468  * @cfg {Array} fields Array of field definition objects
14469  * @constructor
14470  * Create a new JsonReader
14471  * @param {Object} meta Metadata configuration options
14472  * @param {Object} recordType Either an Array of field definition objects,
14473  * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
14474  */
14475 Roo.data.JsonReader = function(meta, recordType){
14476     
14477     meta = meta || {};
14478     // set some defaults:
14479     Roo.applyIf(meta, {
14480         totalProperty: 'total',
14481         successProperty : 'success',
14482         root : 'data',
14483         id : 'id'
14484     });
14485     
14486     Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
14487 };
14488 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
14489     
14490     readerType : 'Json',
14491     
14492     /**
14493      * @prop {Boolean} metaFromRemote  - if the meta data was loaded from the remote source.
14494      * Used by Store query builder to append _requestMeta to params.
14495      * 
14496      */
14497     metaFromRemote : false,
14498     /**
14499      * This method is only used by a DataProxy which has retrieved data from a remote server.
14500      * @param {Object} response The XHR object which contains the JSON data in its responseText.
14501      * @return {Object} data A data block which is used by an Roo.data.Store object as
14502      * a cache of Roo.data.Records.
14503      */
14504     read : function(response){
14505         var json = response.responseText;
14506        
14507         var o = /* eval:var:o */ eval("("+json+")");
14508         if(!o) {
14509             throw {message: "JsonReader.read: Json object not found"};
14510         }
14511         
14512         if(o.metaData){
14513             
14514             delete this.ef;
14515             this.metaFromRemote = true;
14516             this.meta = o.metaData;
14517             this.recordType = Roo.data.Record.create(o.metaData.fields);
14518             this.onMetaChange(this.meta, this.recordType, o);
14519         }
14520         return this.readRecords(o);
14521     },
14522
14523     // private function a store will implement
14524     onMetaChange : function(meta, recordType, o){
14525
14526     },
14527
14528     /**
14529          * @ignore
14530          */
14531     simpleAccess: function(obj, subsc) {
14532         return obj[subsc];
14533     },
14534
14535         /**
14536          * @ignore
14537          */
14538     getJsonAccessor: function(){
14539         var re = /[\[\.]/;
14540         return function(expr) {
14541             try {
14542                 return(re.test(expr))
14543                     ? new Function("obj", "return obj." + expr)
14544                     : function(obj){
14545                         return obj[expr];
14546                     };
14547             } catch(e){}
14548             return Roo.emptyFn;
14549         };
14550     }(),
14551
14552     /**
14553      * Create a data block containing Roo.data.Records from an XML document.
14554      * @param {Object} o An object which contains an Array of row objects in the property specified
14555      * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
14556      * which contains the total size of the dataset.
14557      * @return {Object} data A data block which is used by an Roo.data.Store object as
14558      * a cache of Roo.data.Records.
14559      */
14560     readRecords : function(o){
14561         /**
14562          * After any data loads, the raw JSON data is available for further custom processing.
14563          * @type Object
14564          */
14565         this.o = o;
14566         var s = this.meta, Record = this.recordType,
14567             f = Record ? Record.prototype.fields : null, fi = f ? f.items : [], fl = f ? f.length : 0;
14568
14569 //      Generate extraction functions for the totalProperty, the root, the id, and for each field
14570         if (!this.ef) {
14571             if(s.totalProperty) {
14572                     this.getTotal = this.getJsonAccessor(s.totalProperty);
14573                 }
14574                 if(s.successProperty) {
14575                     this.getSuccess = this.getJsonAccessor(s.successProperty);
14576                 }
14577                 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
14578                 if (s.id) {
14579                         var g = this.getJsonAccessor(s.id);
14580                         this.getId = function(rec) {
14581                                 var r = g(rec);  
14582                                 return (r === undefined || r === "") ? null : r;
14583                         };
14584                 } else {
14585                         this.getId = function(){return null;};
14586                 }
14587             this.ef = [];
14588             for(var jj = 0; jj < fl; jj++){
14589                 f = fi[jj];
14590                 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
14591                 this.ef[jj] = this.getJsonAccessor(map);
14592             }
14593         }
14594
14595         var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
14596         if(s.totalProperty){
14597             var vt = parseInt(this.getTotal(o), 10);
14598             if(!isNaN(vt)){
14599                 totalRecords = vt;
14600             }
14601         }
14602         if(s.successProperty){
14603             var vs = this.getSuccess(o);
14604             if(vs === false || vs === 'false'){
14605                 success = false;
14606             }
14607         }
14608         var records = [];
14609         for(var i = 0; i < c; i++){
14610                 var n = root[i];
14611             var values = {};
14612             var id = this.getId(n);
14613             for(var j = 0; j < fl; j++){
14614                 f = fi[j];
14615             var v = this.ef[j](n);
14616             if (!f.convert) {
14617                 Roo.log('missing convert for ' + f.name);
14618                 Roo.log(f);
14619                 continue;
14620             }
14621             values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
14622             }
14623             var record = new Record(values, id);
14624             record.json = n;
14625             records[i] = record;
14626         }
14627         return {
14628             raw : o,
14629             success : success,
14630             records : records,
14631             totalRecords : totalRecords
14632         };
14633     },
14634     // used when loading children.. @see loadDataFromChildren
14635     toLoadData: function(rec)
14636     {
14637         // expect rec just to be an array.. eg [a,b,c, [...] << cn ]
14638         var data = typeof(rec.data.cn) == 'undefined' ? [] : rec.data.cn;
14639         return { data : data, total : data.length };
14640         
14641     }
14642 });/*
14643  * Based on:
14644  * Ext JS Library 1.1.1
14645  * Copyright(c) 2006-2007, Ext JS, LLC.
14646  *
14647  * Originally Released Under LGPL - original licence link has changed is not relivant.
14648  *
14649  * Fork - LGPL
14650  * <script type="text/javascript">
14651  */
14652
14653 /**
14654  * @class Roo.data.ArrayReader
14655  * @extends Roo.data.DataReader
14656  * Data reader class to create an Array of Roo.data.Record objects from an Array.
14657  * Each element of that Array represents a row of data fields. The
14658  * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
14659  * of the field definition if it exists, or the field's ordinal position in the definition.<br>
14660  * <p>
14661  * Example code:.
14662  * <pre><code>
14663 var RecordDef = Roo.data.Record.create([
14664     {name: 'name', mapping: 1},         // "mapping" only needed if an "id" field is present which
14665     {name: 'occupation', mapping: 2}    // precludes using the ordinal position as the index.
14666 ]);
14667 var myReader = new Roo.data.ArrayReader({
14668     id: 0                     // The subscript within row Array that provides an ID for the Record (optional)
14669 }, RecordDef);
14670 </code></pre>
14671  * <p>
14672  * This would consume an Array like this:
14673  * <pre><code>
14674 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
14675   </code></pre>
14676  
14677  * @constructor
14678  * Create a new JsonReader
14679  * @param {Object} meta Metadata configuration options.
14680  * @param {Object|Array} recordType Either an Array of field definition objects
14681  * 
14682  * @cfg {Array} fields Array of field definition objects
14683  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
14684  * as specified to {@link Roo.data.Record#create},
14685  * or an {@link Roo.data.Record} object
14686  *
14687  * 
14688  * created using {@link Roo.data.Record#create}.
14689  */
14690 Roo.data.ArrayReader = function(meta, recordType)
14691 {    
14692     Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType||meta.fields);
14693 };
14694
14695 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
14696     
14697       /**
14698      * Create a data block containing Roo.data.Records from an XML document.
14699      * @param {Object} o An Array of row objects which represents the dataset.
14700      * @return {Object} A data block which is used by an {@link Roo.data.Store} object as
14701      * a cache of Roo.data.Records.
14702      */
14703     readRecords : function(o)
14704     {
14705         var sid = this.meta ? this.meta.id : null;
14706         var recordType = this.recordType, fields = recordType.prototype.fields;
14707         var records = [];
14708         var root = o;
14709         for(var i = 0; i < root.length; i++){
14710                 var n = root[i];
14711             var values = {};
14712             var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
14713             for(var j = 0, jlen = fields.length; j < jlen; j++){
14714                 var f = fields.items[j];
14715                 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
14716                 var v = n[k] !== undefined ? n[k] : f.defaultValue;
14717                 v = f.convert(v);
14718                 values[f.name] = v;
14719             }
14720             var record = new recordType(values, id);
14721             record.json = n;
14722             records[records.length] = record;
14723         }
14724         return {
14725             records : records,
14726             totalRecords : records.length
14727         };
14728     },
14729     // used when loading children.. @see loadDataFromChildren
14730     toLoadData: function(rec)
14731     {
14732         // expect rec just to be an array.. eg [a,b,c, [...] << cn ]
14733         return typeof(rec.data.cn) == 'undefined' ? [] : rec.data.cn;
14734         
14735     }
14736     
14737     
14738 });/*
14739  * - LGPL
14740  * * 
14741  */
14742
14743 /**
14744  * @class Roo.bootstrap.ComboBox
14745  * @extends Roo.bootstrap.TriggerField
14746  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
14747  * @cfg {Boolean} append (true|false) default false
14748  * @cfg {Boolean} autoFocus (true|false) auto focus the first item, default true
14749  * @cfg {Boolean} tickable ComboBox with tickable selections (true|false), default false
14750  * @cfg {Boolean} triggerList trigger show the list or not (true|false) default true
14751  * @cfg {Boolean} showToggleBtn show toggle button or not (true|false) default true
14752  * @cfg {String} btnPosition set the position of the trigger button (left | right) default right
14753  * @cfg {Boolean} animate default true
14754  * @cfg {Boolean} emptyResultText only for touch device
14755  * @cfg {String} triggerText multiple combobox trigger button text default 'Select'
14756  * @cfg {String} emptyTitle default ''
14757  * @cfg {Number} width fixed with? experimental
14758  * @constructor
14759  * Create a new ComboBox.
14760  * @param {Object} config Configuration options
14761  */
14762 Roo.bootstrap.ComboBox = function(config){
14763     Roo.bootstrap.ComboBox.superclass.constructor.call(this, config);
14764     this.addEvents({
14765         /**
14766          * @event expand
14767          * Fires when the dropdown list is expanded
14768         * @param {Roo.bootstrap.ComboBox} combo This combo box
14769         */
14770         'expand' : true,
14771         /**
14772          * @event collapse
14773          * Fires when the dropdown list is collapsed
14774         * @param {Roo.bootstrap.ComboBox} combo This combo box
14775         */
14776         'collapse' : true,
14777         /**
14778          * @event beforeselect
14779          * Fires before a list item is selected. Return false to cancel the selection.
14780         * @param {Roo.bootstrap.ComboBox} combo This combo box
14781         * @param {Roo.data.Record} record The data record returned from the underlying store
14782         * @param {Number} index The index of the selected item in the dropdown list
14783         */
14784         'beforeselect' : true,
14785         /**
14786          * @event select
14787          * Fires when a list item is selected
14788         * @param {Roo.bootstrap.ComboBox} combo This combo box
14789         * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
14790         * @param {Number} index The index of the selected item in the dropdown list
14791         */
14792         'select' : true,
14793         /**
14794          * @event beforequery
14795          * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
14796          * The event object passed has these properties:
14797         * @param {Roo.bootstrap.ComboBox} combo This combo box
14798         * @param {String} query The query
14799         * @param {Boolean} forceAll true to force "all" query
14800         * @param {Boolean} cancel true to cancel the query
14801         * @param {Object} e The query event object
14802         */
14803         'beforequery': true,
14804          /**
14805          * @event add
14806          * Fires when the 'add' icon is pressed (add a listener to enable add button)
14807         * @param {Roo.bootstrap.ComboBox} combo This combo box
14808         */
14809         'add' : true,
14810         /**
14811          * @event edit
14812          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
14813         * @param {Roo.bootstrap.ComboBox} combo This combo box
14814         * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
14815         */
14816         'edit' : true,
14817         /**
14818          * @event remove
14819          * Fires when the remove value from the combobox array
14820         * @param {Roo.bootstrap.ComboBox} combo This combo box
14821         */
14822         'remove' : true,
14823         /**
14824          * @event afterremove
14825          * Fires when the remove value from the combobox array
14826         * @param {Roo.bootstrap.ComboBox} combo This combo box
14827         */
14828         'afterremove' : true,
14829         /**
14830          * @event specialfilter
14831          * Fires when specialfilter
14832             * @param {Roo.bootstrap.ComboBox} combo This combo box
14833             */
14834         'specialfilter' : true,
14835         /**
14836          * @event tick
14837          * Fires when tick the element
14838             * @param {Roo.bootstrap.ComboBox} combo This combo box
14839             */
14840         'tick' : true,
14841         /**
14842          * @event touchviewdisplay
14843          * Fires when touch view require special display (default is using displayField)
14844             * @param {Roo.bootstrap.ComboBox} combo This combo box
14845             * @param {Object} cfg set html .
14846             */
14847         'touchviewdisplay' : true
14848         
14849     });
14850     
14851     this.item = [];
14852     this.tickItems = [];
14853     
14854     this.selectedIndex = -1;
14855     if(this.mode == 'local'){
14856         if(config.queryDelay === undefined){
14857             this.queryDelay = 10;
14858         }
14859         if(config.minChars === undefined){
14860             this.minChars = 0;
14861         }
14862     }
14863 };
14864
14865 Roo.extend(Roo.bootstrap.ComboBox, Roo.bootstrap.TriggerField, {
14866      
14867     /**
14868      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
14869      * rendering into an Roo.Editor, defaults to false)
14870      */
14871     /**
14872      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
14873      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
14874      */
14875     /**
14876      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
14877      */
14878     /**
14879      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
14880      * the dropdown list (defaults to undefined, with no header element)
14881      */
14882
14883      /**
14884      * @cfg {String/Roo.Template} tpl The template to use to render the output default is  '<a class="dropdown-item" href="#">{' + this.displayField + '}</a>' 
14885      */
14886      
14887      /**
14888      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
14889      */
14890     listWidth: undefined,
14891     /**
14892      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
14893      * mode = 'remote' or 'text' if mode = 'local')
14894      */
14895     displayField: undefined,
14896     
14897     /**
14898      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
14899      * mode = 'remote' or 'value' if mode = 'local'). 
14900      * Note: use of a valueField requires the user make a selection
14901      * in order for a value to be mapped.
14902      */
14903     valueField: undefined,
14904     /**
14905      * @cfg {String} modalTitle The title of the dialog that pops up on mobile views.
14906      */
14907     modalTitle : '',
14908     
14909     /**
14910      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
14911      * field's data value (defaults to the underlying DOM element's name)
14912      */
14913     hiddenName: undefined,
14914     /**
14915      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
14916      */
14917     listClass: '',
14918     /**
14919      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
14920      */
14921     selectedClass: 'active',
14922     
14923     /**
14924      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
14925      */
14926     shadow:'sides',
14927     /**
14928      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
14929      * anchor positions (defaults to 'tl-bl')
14930      */
14931     listAlign: 'tl-bl?',
14932     /**
14933      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
14934      */
14935     maxHeight: 300,
14936     /**
14937      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
14938      * query specified by the allQuery config option (defaults to 'query')
14939      */
14940     triggerAction: 'query',
14941     /**
14942      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
14943      * (defaults to 4, does not apply if editable = false)
14944      */
14945     minChars : 4,
14946     /**
14947      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
14948      * delay (typeAheadDelay) if it matches a known value (defaults to false)
14949      */
14950     typeAhead: false,
14951     /**
14952      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
14953      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
14954      */
14955     queryDelay: 500,
14956     /**
14957      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
14958      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
14959      */
14960     pageSize: 0,
14961     /**
14962      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
14963      * when editable = true (defaults to false)
14964      */
14965     selectOnFocus:false,
14966     /**
14967      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
14968      */
14969     queryParam: 'query',
14970     /**
14971      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
14972      * when mode = 'remote' (defaults to 'Loading...')
14973      */
14974     loadingText: 'Loading...',
14975     /**
14976      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
14977      */
14978     resizable: false,
14979     /**
14980      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
14981      */
14982     handleHeight : 8,
14983     /**
14984      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
14985      * traditional select (defaults to true)
14986      */
14987     editable: true,
14988     /**
14989      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
14990      */
14991     allQuery: '',
14992     /**
14993      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
14994      */
14995     mode: 'remote',
14996     /**
14997      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
14998      * listWidth has a higher value)
14999      */
15000     minListWidth : 70,
15001     /**
15002      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
15003      * allow the user to set arbitrary text into the field (defaults to false)
15004      */
15005     forceSelection:false,
15006     /**
15007      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
15008      * if typeAhead = true (defaults to 250)
15009      */
15010     typeAheadDelay : 250,
15011     /**
15012      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
15013      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
15014      */
15015     valueNotFoundText : undefined,
15016     /**
15017      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
15018      */
15019     blockFocus : false,
15020     
15021     /**
15022      * @cfg {Boolean} disableClear Disable showing of clear button.
15023      */
15024     disableClear : false,
15025     /**
15026      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
15027      */
15028     alwaysQuery : false,
15029     
15030     /**
15031      * @cfg {Boolean} multiple  (true|false) ComboBobArray, default false
15032      */
15033     multiple : false,
15034     
15035     /**
15036      * @cfg {String} invalidClass DEPRICATED - uses BS4 is-valid now
15037      */
15038     invalidClass : "has-warning",
15039     
15040     /**
15041      * @cfg {String} validClass DEPRICATED - uses BS4 is-valid now
15042      */
15043     validClass : "has-success",
15044     
15045     /**
15046      * @cfg {Boolean} specialFilter (true|false) special filter default false
15047      */
15048     specialFilter : false,
15049     
15050     /**
15051      * @cfg {Boolean} mobileTouchView (true|false) show mobile touch view when using a mobile default true
15052      */
15053     mobileTouchView : true,
15054     
15055     /**
15056      * @cfg {Boolean} useNativeIOS (true|false) render it as classic select for ios, not support dynamic load data (default false)
15057      */
15058     useNativeIOS : false,
15059     
15060     /**
15061      * @cfg {Boolean} mobile_restrict_height (true|false) restrict height for touch view
15062      */
15063     mobile_restrict_height : false,
15064     
15065     ios_options : false,
15066     
15067     //private
15068     addicon : false,
15069     editicon: false,
15070     
15071     page: 0,
15072     hasQuery: false,
15073     append: false,
15074     loadNext: false,
15075     autoFocus : true,
15076     tickable : false,
15077     btnPosition : 'right',
15078     triggerList : true,
15079     showToggleBtn : true,
15080     animate : true,
15081     emptyResultText: 'Empty',
15082     triggerText : 'Select',
15083     emptyTitle : '',
15084     width : false,
15085     
15086     // element that contains real text value.. (when hidden is used..)
15087     
15088     getAutoCreate : function()
15089     {   
15090         var cfg = false;
15091         //render
15092         /*
15093          * Render classic select for iso
15094          */
15095         
15096         if(Roo.isIOS && this.useNativeIOS){
15097             cfg = this.getAutoCreateNativeIOS();
15098             return cfg;
15099         }
15100         
15101         /*
15102          * Touch Devices
15103          */
15104         
15105         if(Roo.isTouch && this.mobileTouchView){
15106             cfg = this.getAutoCreateTouchView();
15107             return cfg;;
15108         }
15109         
15110         /*
15111          *  Normal ComboBox
15112          */
15113         if(!this.tickable){
15114             cfg = Roo.bootstrap.ComboBox.superclass.getAutoCreate.call(this);
15115             return cfg;
15116         }
15117         
15118         /*
15119          *  ComboBox with tickable selections
15120          */
15121              
15122         var align = this.labelAlign || this.parentLabelAlign();
15123         
15124         cfg = {
15125             cls : 'form-group roo-combobox-tickable' //input-group
15126         };
15127         
15128         var btn_text_select = '';
15129         var btn_text_done = '';
15130         var btn_text_cancel = '';
15131         
15132         if (this.btn_text_show) {
15133             btn_text_select = 'Select';
15134             btn_text_done = 'Done';
15135             btn_text_cancel = 'Cancel'; 
15136         }
15137         
15138         var buttons = {
15139             tag : 'div',
15140             cls : 'tickable-buttons',
15141             cn : [
15142                 {
15143                     tag : 'button',
15144                     type : 'button',
15145                     cls : 'btn btn-link btn-edit pull-' + this.btnPosition,
15146                     //html : this.triggerText
15147                     html: btn_text_select
15148                 },
15149                 {
15150                     tag : 'button',
15151                     type : 'button',
15152                     name : 'ok',
15153                     cls : 'btn btn-link btn-ok pull-' + this.btnPosition,
15154                     //html : 'Done'
15155                     html: btn_text_done
15156                 },
15157                 {
15158                     tag : 'button',
15159                     type : 'button',
15160                     name : 'cancel',
15161                     cls : 'btn btn-link btn-cancel pull-' + this.btnPosition,
15162                     //html : 'Cancel'
15163                     html: btn_text_cancel
15164                 }
15165             ]
15166         };
15167         
15168         if(this.editable){
15169             buttons.cn.unshift({
15170                 tag: 'input',
15171                 cls: 'roo-select2-search-field-input'
15172             });
15173         }
15174         
15175         var _this = this;
15176         
15177         Roo.each(buttons.cn, function(c){
15178             if (_this.size) {
15179                 c.cls += ' btn-' + _this.size;
15180             }
15181
15182             if (_this.disabled) {
15183                 c.disabled = true;
15184             }
15185         });
15186         
15187         var box = {
15188             tag: 'div',
15189             style : 'display: contents',
15190             cn: [
15191                 {
15192                     tag: 'input',
15193                     type : 'hidden',
15194                     cls: 'form-hidden-field'
15195                 },
15196                 {
15197                     tag: 'ul',
15198                     cls: 'roo-select2-choices',
15199                     cn:[
15200                         {
15201                             tag: 'li',
15202                             cls: 'roo-select2-search-field',
15203                             cn: [
15204                                 buttons
15205                             ]
15206                         }
15207                     ]
15208                 }
15209             ]
15210         };
15211         
15212         var combobox = {
15213             cls: 'roo-select2-container input-group roo-select2-container-multi',
15214             cn: [
15215                 
15216                 box
15217 //                {
15218 //                    tag: 'ul',
15219 //                    cls: 'typeahead typeahead-long dropdown-menu',
15220 //                    style: 'display:none; max-height:' + this.maxHeight + 'px;'
15221 //                }
15222             ]
15223         };
15224         
15225         if(this.hasFeedback && !this.allowBlank){
15226             
15227             var feedback = {
15228                 tag: 'span',
15229                 cls: 'glyphicon form-control-feedback'
15230             };
15231
15232             combobox.cn.push(feedback);
15233         }
15234         
15235         
15236         
15237         var indicator = {
15238             tag : 'i',
15239             cls : 'roo-required-indicator ' + (this.indicatorpos == 'right'  ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
15240             tooltip : 'This field is required'
15241         };
15242         if (Roo.bootstrap.version == 4) {
15243             indicator = {
15244                 tag : 'i',
15245                 style : 'display:none'
15246             };
15247         }
15248         if (align ==='left' && this.fieldLabel.length) {
15249             
15250             cfg.cls += ' roo-form-group-label-left'  + (Roo.bootstrap.version == 4 ? ' row' : '');
15251             
15252             cfg.cn = [
15253                 indicator,
15254                 {
15255                     tag: 'label',
15256                     'for' :  id,
15257                     cls : 'control-label col-form-label',
15258                     html : this.fieldLabel
15259
15260                 },
15261                 {
15262                     cls : "", 
15263                     cn: [
15264                         combobox
15265                     ]
15266                 }
15267
15268             ];
15269             
15270             var labelCfg = cfg.cn[1];
15271             var contentCfg = cfg.cn[2];
15272             
15273
15274             if(this.indicatorpos == 'right'){
15275                 
15276                 cfg.cn = [
15277                     {
15278                         tag: 'label',
15279                         'for' :  id,
15280                         cls : 'control-label col-form-label',
15281                         cn : [
15282                             {
15283                                 tag : 'span',
15284                                 html : this.fieldLabel
15285                             },
15286                             indicator
15287                         ]
15288                     },
15289                     {
15290                         cls : "",
15291                         cn: [
15292                             combobox
15293                         ]
15294                     }
15295
15296                 ];
15297                 
15298                 
15299                 
15300                 labelCfg = cfg.cn[0];
15301                 contentCfg = cfg.cn[1];
15302             
15303             }
15304             
15305             if(this.labelWidth > 12){
15306                 labelCfg.style = "width: " + this.labelWidth + 'px';
15307             }
15308             if(this.width * 1 > 0){
15309                 contentCfg.style = "width: " + this.width + 'px';
15310             }
15311             if(this.labelWidth < 13 && this.labelmd == 0){
15312                 this.labelmd = this.labelWidth;
15313             }
15314             
15315             if(this.labellg > 0){
15316                 labelCfg.cls += ' col-lg-' + this.labellg;
15317                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
15318             }
15319             
15320             if(this.labelmd > 0){
15321                 labelCfg.cls += ' col-md-' + this.labelmd;
15322                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
15323             }
15324             
15325             if(this.labelsm > 0){
15326                 labelCfg.cls += ' col-sm-' + this.labelsm;
15327                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
15328             }
15329             
15330             if(this.labelxs > 0){
15331                 labelCfg.cls += ' col-xs-' + this.labelxs;
15332                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
15333             }
15334                 
15335                 
15336         } else if ( this.fieldLabel.length) {
15337 //                Roo.log(" label");
15338                  cfg.cn = [
15339                    indicator,
15340                     {
15341                         tag: 'label',
15342                         //cls : 'input-group-addon',
15343                         html : this.fieldLabel
15344                     },
15345                     combobox
15346                 ];
15347                 
15348                 if(this.indicatorpos == 'right'){
15349                     cfg.cn = [
15350                         {
15351                             tag: 'label',
15352                             //cls : 'input-group-addon',
15353                             html : this.fieldLabel
15354                         },
15355                         indicator,
15356                         combobox
15357                     ];
15358                     
15359                 }
15360
15361         } else {
15362             
15363 //                Roo.log(" no label && no align");
15364                 cfg = combobox
15365                      
15366                 
15367         }
15368          
15369         var settings=this;
15370         ['xs','sm','md','lg'].map(function(size){
15371             if (settings[size]) {
15372                 cfg.cls += ' col-' + size + '-' + settings[size];
15373             }
15374         });
15375         
15376         return cfg;
15377         
15378     },
15379     
15380     _initEventsCalled : false,
15381     
15382     // private
15383     initEvents: function()
15384     {   
15385         if (this._initEventsCalled) { // as we call render... prevent looping...
15386             return;
15387         }
15388         this._initEventsCalled = true;
15389         
15390         if (!this.store) {
15391             throw "can not find store for combo";
15392         }
15393         
15394         this.indicator = this.indicatorEl();
15395         
15396         this.store = Roo.factory(this.store, Roo.data);
15397         this.store.parent = this;
15398         
15399         // if we are building from html. then this element is so complex, that we can not really
15400         // use the rendered HTML.
15401         // so we have to trash and replace the previous code.
15402         if (Roo.XComponent.build_from_html) {
15403             // remove this element....
15404             var e = this.el.dom, k=0;
15405             while (e ) { e = e.previousSibling;  ++k;}
15406
15407             this.el.remove();
15408             
15409             this.el=false;
15410             this.rendered = false;
15411             
15412             this.render(this.parent().getChildContainer(true), k);
15413         }
15414         
15415         if(Roo.isIOS && this.useNativeIOS){
15416             this.initIOSView();
15417             return;
15418         }
15419         
15420         /*
15421          * Touch Devices
15422          */
15423         
15424         if(Roo.isTouch && this.mobileTouchView){
15425             this.initTouchView();
15426             return;
15427         }
15428         
15429         if(this.tickable){
15430             this.initTickableEvents();
15431             return;
15432         }
15433         
15434         Roo.bootstrap.ComboBox.superclass.initEvents.call(this);
15435         
15436         if(this.hiddenName){
15437             
15438             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
15439             
15440             this.hiddenField.dom.value =
15441                 this.hiddenValue !== undefined ? this.hiddenValue :
15442                 this.value !== undefined ? this.value : '';
15443
15444             // prevent input submission
15445             this.el.dom.removeAttribute('name');
15446             this.hiddenField.dom.setAttribute('name', this.hiddenName);
15447              
15448              
15449         }
15450         //if(Roo.isGecko){
15451         //    this.el.dom.setAttribute('autocomplete', 'off');
15452         //}
15453         
15454         var cls = 'x-combo-list';
15455         
15456         //this.list = new Roo.Layer({
15457         //    shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
15458         //});
15459         
15460         var _this = this;
15461         
15462         (function(){
15463             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
15464             _this.list.setWidth(lw);
15465         }).defer(100);
15466         
15467         this.list.on('mouseover', this.onViewOver, this);
15468         this.list.on('mousemove', this.onViewMove, this);
15469         this.list.on('scroll', this.onViewScroll, this);
15470         
15471         /*
15472         this.list.swallowEvent('mousewheel');
15473         this.assetHeight = 0;
15474
15475         if(this.title){
15476             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
15477             this.assetHeight += this.header.getHeight();
15478         }
15479
15480         this.innerList = this.list.createChild({cls:cls+'-inner'});
15481         this.innerList.on('mouseover', this.onViewOver, this);
15482         this.innerList.on('mousemove', this.onViewMove, this);
15483         this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
15484         
15485         if(this.allowBlank && !this.pageSize && !this.disableClear){
15486             this.footer = this.list.createChild({cls:cls+'-ft'});
15487             this.pageTb = new Roo.Toolbar(this.footer);
15488            
15489         }
15490         if(this.pageSize){
15491             this.footer = this.list.createChild({cls:cls+'-ft'});
15492             this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
15493                     {pageSize: this.pageSize});
15494             
15495         }
15496         
15497         if (this.pageTb && this.allowBlank && !this.disableClear) {
15498             var _this = this;
15499             this.pageTb.add(new Roo.Toolbar.Fill(), {
15500                 cls: 'x-btn-icon x-btn-clear',
15501                 text: '&#160;',
15502                 handler: function()
15503                 {
15504                     _this.collapse();
15505                     _this.clearValue();
15506                     _this.onSelect(false, -1);
15507                 }
15508             });
15509         }
15510         if (this.footer) {
15511             this.assetHeight += this.footer.getHeight();
15512         }
15513         */
15514             
15515         if(!this.tpl){
15516             this.tpl = Roo.bootstrap.version == 4 ?
15517                 '<a class="dropdown-item" href="#">{' + this.displayField + '}</a>' :  // 4 does not need <li> and it get's really confisued.
15518                 '<li><a class="dropdown-item" href="#">{' + this.displayField + '}</a></li>';
15519         }
15520
15521         this.view = new Roo.View(this.list, this.tpl, {
15522             singleSelect:true, store: this.store, selectedClass: this.selectedClass
15523         });
15524         //this.view.wrapEl.setDisplayed(false);
15525         this.view.on('click', this.onViewClick, this);
15526         
15527         
15528         this.store.on('beforeload', this.onBeforeLoad, this);
15529         this.store.on('load', this.onLoad, this);
15530         this.store.on('loadexception', this.onLoadException, this);
15531         /*
15532         if(this.resizable){
15533             this.resizer = new Roo.Resizable(this.list,  {
15534                pinned:true, handles:'se'
15535             });
15536             this.resizer.on('resize', function(r, w, h){
15537                 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
15538                 this.listWidth = w;
15539                 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
15540                 this.restrictHeight();
15541             }, this);
15542             this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
15543         }
15544         */
15545         if(!this.editable){
15546             this.editable = true;
15547             this.setEditable(false);
15548         }
15549         
15550         /*
15551         
15552         if (typeof(this.events.add.listeners) != 'undefined') {
15553             
15554             this.addicon = this.wrap.createChild(
15555                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });  
15556        
15557             this.addicon.on('click', function(e) {
15558                 this.fireEvent('add', this);
15559             }, this);
15560         }
15561         if (typeof(this.events.edit.listeners) != 'undefined') {
15562             
15563             this.editicon = this.wrap.createChild(
15564                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });  
15565             if (this.addicon) {
15566                 this.editicon.setStyle('margin-left', '40px');
15567             }
15568             this.editicon.on('click', function(e) {
15569                 
15570                 // we fire even  if inothing is selected..
15571                 this.fireEvent('edit', this, this.lastData );
15572                 
15573             }, this);
15574         }
15575         */
15576         
15577         this.keyNav = new Roo.KeyNav(this.inputEl(), {
15578             "up" : function(e){
15579                 this.inKeyMode = true;
15580                 this.selectPrev();
15581             },
15582
15583             "down" : function(e){
15584                 if(!this.isExpanded()){
15585                     this.onTriggerClick();
15586                 }else{
15587                     this.inKeyMode = true;
15588                     this.selectNext();
15589                 }
15590             },
15591
15592             "enter" : function(e){
15593 //                this.onViewClick();
15594                 //return true;
15595                 this.collapse();
15596                 
15597                 if(this.fireEvent("specialkey", this, e)){
15598                     this.onViewClick(false);
15599                 }
15600                 
15601                 return true;
15602             },
15603
15604             "esc" : function(e){
15605                 this.collapse();
15606             },
15607
15608             "tab" : function(e){
15609                 this.collapse();
15610                 
15611                 if(this.fireEvent("specialkey", this, e)){
15612                     this.onViewClick(false);
15613                 }
15614                 
15615                 return true;
15616             },
15617
15618             scope : this,
15619
15620             doRelay : function(foo, bar, hname){
15621                 if(hname == 'down' || this.scope.isExpanded()){
15622                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
15623                 }
15624                 return true;
15625             },
15626
15627             forceKeyDown: true
15628         });
15629         
15630         
15631         this.queryDelay = Math.max(this.queryDelay || 10,
15632                 this.mode == 'local' ? 10 : 250);
15633         
15634         
15635         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
15636         
15637         if(this.typeAhead){
15638             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
15639         }
15640         if(this.editable !== false){
15641             this.inputEl().on("keyup", this.onKeyUp, this);
15642         }
15643         if(this.forceSelection){
15644             this.inputEl().on('blur', this.doForce, this);
15645         }
15646         
15647         if(this.multiple){
15648             this.choices = this.el.select('ul.roo-select2-choices', true).first();
15649             this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
15650         }
15651     },
15652     
15653     initTickableEvents: function()
15654     {   
15655         this.createList();
15656         
15657         if(this.hiddenName){
15658             
15659             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
15660             
15661             this.hiddenField.dom.value =
15662                 this.hiddenValue !== undefined ? this.hiddenValue :
15663                 this.value !== undefined ? this.value : '';
15664
15665             // prevent input submission
15666             this.el.dom.removeAttribute('name');
15667             this.hiddenField.dom.setAttribute('name', this.hiddenName);
15668              
15669              
15670         }
15671         
15672 //        this.list = this.el.select('ul.dropdown-menu',true).first();
15673         
15674         this.choices = this.el.select('ul.roo-select2-choices', true).first();
15675         this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
15676         if(this.triggerList){
15677             this.searchField.on("click", this.onSearchFieldClick, this, {preventDefault:true});
15678         }
15679          
15680         this.trigger = this.el.select('.tickable-buttons > .btn-edit', true).first();
15681         this.trigger.on("click", this.onTickableTriggerClick, this, {preventDefault:true});
15682         
15683         this.okBtn = this.el.select('.tickable-buttons > .btn-ok', true).first();
15684         this.cancelBtn = this.el.select('.tickable-buttons > .btn-cancel', true).first();
15685         
15686         this.okBtn.on('click', this.onTickableFooterButtonClick, this, this.okBtn);
15687         this.cancelBtn.on('click', this.onTickableFooterButtonClick, this, this.cancelBtn);
15688         
15689         this.trigger.setVisibilityMode(Roo.Element.DISPLAY);
15690         this.okBtn.setVisibilityMode(Roo.Element.DISPLAY);
15691         this.cancelBtn.setVisibilityMode(Roo.Element.DISPLAY);
15692         
15693         this.okBtn.hide();
15694         this.cancelBtn.hide();
15695         
15696         var _this = this;
15697         
15698         (function(){
15699             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
15700             _this.list.setWidth(lw);
15701         }).defer(100);
15702         
15703         this.list.on('mouseover', this.onViewOver, this);
15704         this.list.on('mousemove', this.onViewMove, this);
15705         
15706         this.list.on('scroll', this.onViewScroll, this);
15707         
15708         if(!this.tpl){
15709             this.tpl = '<li class="roo-select2-result"><div class="checkbox"><input id="{roo-id}"' + 
15710                 'type="checkbox" {roo-data-checked}><label for="{roo-id}"><b>{' + this.displayField + '}</b></label></div></li>';
15711         }
15712
15713         this.view = new Roo.View(this.list, this.tpl, {
15714             singleSelect:true,
15715             tickable:true,
15716             parent:this,
15717             store: this.store,
15718             selectedClass: this.selectedClass
15719         });
15720         
15721         //this.view.wrapEl.setDisplayed(false);
15722         this.view.on('click', this.onViewClick, this);
15723         
15724         
15725         
15726         this.store.on('beforeload', this.onBeforeLoad, this);
15727         this.store.on('load', this.onLoad, this);
15728         this.store.on('loadexception', this.onLoadException, this);
15729         
15730         if(this.editable){
15731             this.keyNav = new Roo.KeyNav(this.tickableInputEl(), {
15732                 "up" : function(e){
15733                     this.inKeyMode = true;
15734                     this.selectPrev();
15735                 },
15736
15737                 "down" : function(e){
15738                     this.inKeyMode = true;
15739                     this.selectNext();
15740                 },
15741
15742                 "enter" : function(e){
15743                     if(this.fireEvent("specialkey", this, e)){
15744                         this.onViewClick(false);
15745                     }
15746                     
15747                     return true;
15748                 },
15749
15750                 "esc" : function(e){
15751                     this.onTickableFooterButtonClick(e, false, false);
15752                 },
15753
15754                 "tab" : function(e){
15755                     this.fireEvent("specialkey", this, e);
15756                     
15757                     this.onTickableFooterButtonClick(e, false, false);
15758                     
15759                     return true;
15760                 },
15761
15762                 scope : this,
15763
15764                 doRelay : function(e, fn, key){
15765                     if(this.scope.isExpanded()){
15766                        return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
15767                     }
15768                     return true;
15769                 },
15770
15771                 forceKeyDown: true
15772             });
15773         }
15774         
15775         this.queryDelay = Math.max(this.queryDelay || 10,
15776                 this.mode == 'local' ? 10 : 250);
15777         
15778         
15779         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
15780         
15781         if(this.typeAhead){
15782             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
15783         }
15784         
15785         if(this.editable !== false){
15786             this.tickableInputEl().on("keyup", this.onKeyUp, this);
15787         }
15788         
15789         this.indicator = this.indicatorEl();
15790         
15791         if(this.indicator){
15792             this.indicator.setVisibilityMode(Roo.Element.DISPLAY);
15793             this.indicator.hide();
15794         }
15795         
15796     },
15797
15798     onDestroy : function(){
15799         if(this.view){
15800             this.view.setStore(null);
15801             this.view.el.removeAllListeners();
15802             this.view.el.remove();
15803             this.view.purgeListeners();
15804         }
15805         if(this.list){
15806             this.list.dom.innerHTML  = '';
15807         }
15808         
15809         if(this.store){
15810             this.store.un('beforeload', this.onBeforeLoad, this);
15811             this.store.un('load', this.onLoad, this);
15812             this.store.un('loadexception', this.onLoadException, this);
15813         }
15814         Roo.bootstrap.ComboBox.superclass.onDestroy.call(this);
15815     },
15816
15817     // private
15818     fireKey : function(e){
15819         if(e.isNavKeyPress() && !this.list.isVisible()){
15820             this.fireEvent("specialkey", this, e);
15821         }
15822     },
15823
15824     // private
15825     onResize: function(w, h)
15826     {
15827         
15828         
15829 //        Roo.bootstrap.ComboBox.superclass.onResize.apply(this, arguments);
15830 //        
15831 //        if(typeof w != 'number'){
15832 //            // we do not handle it!?!?
15833 //            return;
15834 //        }
15835 //        var tw = this.trigger.getWidth();
15836 //       // tw += this.addicon ? this.addicon.getWidth() : 0;
15837 //       // tw += this.editicon ? this.editicon.getWidth() : 0;
15838 //        var x = w - tw;
15839 //        this.inputEl().setWidth( this.adjustWidth('input', x));
15840 //            
15841 //        //this.trigger.setStyle('left', x+'px');
15842 //        
15843 //        if(this.list && this.listWidth === undefined){
15844 //            var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
15845 //            this.list.setWidth(lw);
15846 //            this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
15847 //        }
15848         
15849     
15850         
15851     },
15852
15853     /**
15854      * Allow or prevent the user from directly editing the field text.  If false is passed,
15855      * the user will only be able to select from the items defined in the dropdown list.  This method
15856      * is the runtime equivalent of setting the 'editable' config option at config time.
15857      * @param {Boolean} value True to allow the user to directly edit the field text
15858      */
15859     setEditable : function(value){
15860         if(value == this.editable){
15861             return;
15862         }
15863         this.editable = value;
15864         if(!value){
15865             this.inputEl().dom.setAttribute('readOnly', true);
15866             this.inputEl().on('mousedown', this.onTriggerClick,  this);
15867             this.inputEl().addClass('x-combo-noedit');
15868         }else{
15869             this.inputEl().dom.setAttribute('readOnly', false);
15870             this.inputEl().un('mousedown', this.onTriggerClick,  this);
15871             this.inputEl().removeClass('x-combo-noedit');
15872         }
15873     },
15874
15875     // private
15876     
15877     onBeforeLoad : function(combo,opts){
15878         if(!this.hasFocus){
15879             return;
15880         }
15881          if (!opts.add) {
15882             this.list.dom.innerHTML = '<li class="loading-indicator">'+(this.loadingText||'loading')+'</li>' ;
15883          }
15884         this.restrictHeight();
15885         this.selectedIndex = -1;
15886     },
15887
15888     // private
15889     onLoad : function(){
15890         
15891         this.hasQuery = false;
15892         
15893         if(!this.hasFocus){
15894             return;
15895         }
15896         
15897         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
15898             this.loading.hide();
15899         }
15900         
15901         if(this.store.getCount() > 0){
15902             
15903             this.expand();
15904             this.restrictHeight();
15905             if(this.lastQuery == this.allQuery){
15906                 if(this.editable && !this.tickable){
15907                     this.inputEl().dom.select();
15908                 }
15909                 
15910                 if(
15911                     !this.selectByValue(this.value, true) &&
15912                     this.autoFocus && 
15913                     (
15914                         !this.store.lastOptions ||
15915                         typeof(this.store.lastOptions.add) == 'undefined' || 
15916                         this.store.lastOptions.add != true
15917                     )
15918                 ){
15919                     this.select(0, true);
15920                 }
15921             }else{
15922                 if(this.autoFocus){
15923                     this.selectNext();
15924                 }
15925                 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
15926                     this.taTask.delay(this.typeAheadDelay);
15927                 }
15928             }
15929         }else{
15930             this.onEmptyResults();
15931         }
15932         
15933         //this.el.focus();
15934     },
15935     // private
15936     onLoadException : function()
15937     {
15938         this.hasQuery = false;
15939         
15940         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
15941             this.loading.hide();
15942         }
15943         
15944         if(this.tickable && this.editable){
15945             return;
15946         }
15947         
15948         this.collapse();
15949         // only causes errors at present
15950         //Roo.log(this.store.reader.jsonData);
15951         //if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
15952             // fixme
15953             //Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
15954         //}
15955         
15956         
15957     },
15958     // private
15959     onTypeAhead : function(){
15960         if(this.store.getCount() > 0){
15961             var r = this.store.getAt(0);
15962             var newValue = r.data[this.displayField];
15963             var len = newValue.length;
15964             var selStart = this.getRawValue().length;
15965             
15966             if(selStart != len){
15967                 this.setRawValue(newValue);
15968                 this.selectText(selStart, newValue.length);
15969             }
15970         }
15971     },
15972
15973     // private
15974     onSelect : function(record, index){
15975         
15976         if(this.fireEvent('beforeselect', this, record, index) !== false){
15977         
15978             this.setFromData(index > -1 ? record.data : false);
15979             
15980             this.collapse();
15981             this.fireEvent('select', this, record, index);
15982         }
15983     },
15984
15985     /**
15986      * Returns the currently selected field value or empty string if no value is set.
15987      * @return {String} value The selected value
15988      */
15989     getValue : function()
15990     {
15991         if(Roo.isIOS && this.useNativeIOS){
15992             return this.ios_options[this.inputEl().dom.selectedIndex].data[this.valueField];
15993         }
15994         
15995         if(this.multiple){
15996             return (this.hiddenField) ? this.hiddenField.dom.value : this.value;
15997         }
15998         
15999         if(this.valueField){
16000             return typeof this.value != 'undefined' ? this.value : '';
16001         }else{
16002             return Roo.bootstrap.ComboBox.superclass.getValue.call(this);
16003         }
16004     },
16005     
16006     getRawValue : function()
16007     {
16008         if(Roo.isIOS && this.useNativeIOS){
16009             return this.ios_options[this.inputEl().dom.selectedIndex].data[this.displayField];
16010         }
16011         
16012         var v = this.inputEl().getValue();
16013         
16014         return v;
16015     },
16016
16017     /**
16018      * Clears any text/value currently set in the field
16019      */
16020     clearValue : function(){
16021         
16022         if(this.hiddenField){
16023             this.hiddenField.dom.value = '';
16024         }
16025         this.value = '';
16026         this.setRawValue('');
16027         this.lastSelectionText = '';
16028         this.lastData = false;
16029         
16030         var close = this.closeTriggerEl();
16031         
16032         if(close){
16033             close.hide();
16034         }
16035         
16036         this.validate();
16037         
16038     },
16039
16040     /**
16041      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
16042      * will be displayed in the field.  If the value does not match the data value of an existing item,
16043      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
16044      * Otherwise the field will be blank (although the value will still be set).
16045      * @param {String} value The value to match
16046      */
16047     setValue : function(v)
16048     {
16049         if(Roo.isIOS && this.useNativeIOS){
16050             this.setIOSValue(v);
16051             return;
16052         }
16053         
16054         if(this.multiple){
16055             this.syncValue();
16056             return;
16057         }
16058         
16059         var text = v;
16060         if(this.valueField){
16061             var r = this.findRecord(this.valueField, v);
16062             if(r){
16063                 text = r.data[this.displayField];
16064             }else if(this.valueNotFoundText !== undefined){
16065                 text = this.valueNotFoundText;
16066             }
16067         }
16068         this.lastSelectionText = text;
16069         if(this.hiddenField){
16070             this.hiddenField.dom.value = v;
16071         }
16072         Roo.bootstrap.ComboBox.superclass.setValue.call(this, text);
16073         this.value = v;
16074         
16075         var close = this.closeTriggerEl();
16076         
16077         if(close){
16078             (v && (v.length || v * 1 > 0)) ? close.show() : close.hide();
16079         }
16080         
16081         this.validate();
16082     },
16083     /**
16084      * @property {Object} the last set data for the element
16085      */
16086     
16087     lastData : false,
16088     /**
16089      * Sets the value of the field based on a object which is related to the record format for the store.
16090      * @param {Object} value the value to set as. or false on reset?
16091      */
16092     setFromData : function(o){
16093         
16094         if(this.multiple){
16095             this.addItem(o);
16096             return;
16097         }
16098             
16099         var dv = ''; // display value
16100         var vv = ''; // value value..
16101         this.lastData = o;
16102         if (this.displayField) {
16103             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
16104         } else {
16105             // this is an error condition!!!
16106             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
16107         }
16108         
16109         if(this.valueField){
16110             vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
16111         }
16112         
16113         var close = this.closeTriggerEl();
16114         
16115         if(close){
16116             if(dv.length || vv * 1 > 0){
16117                 close.show() ;
16118                 this.blockFocus=true;
16119             } else {
16120                 close.hide();
16121             }             
16122         }
16123         
16124         if(this.hiddenField){
16125             this.hiddenField.dom.value = vv;
16126             
16127             this.lastSelectionText = dv;
16128             Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
16129             this.value = vv;
16130             return;
16131         }
16132         // no hidden field.. - we store the value in 'value', but still display
16133         // display field!!!!
16134         this.lastSelectionText = dv;
16135         Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
16136         this.value = vv;
16137         
16138         
16139         
16140     },
16141     // private
16142     reset : function(){
16143         // overridden so that last data is reset..
16144         
16145         if(this.multiple){
16146             this.clearItem();
16147             return;
16148         }
16149         
16150         this.setValue(this.originalValue);
16151         //this.clearInvalid();
16152         this.lastData = false;
16153         if (this.view) {
16154             this.view.clearSelections();
16155         }
16156         
16157         this.validate();
16158     },
16159     // private
16160     findRecord : function(prop, value){
16161         var record;
16162         if(this.store.getCount() > 0){
16163             this.store.each(function(r){
16164                 if(r.data[prop] == value){
16165                     record = r;
16166                     return false;
16167                 }
16168                 return true;
16169             });
16170         }
16171         return record;
16172     },
16173     
16174     getName: function()
16175     {
16176         // returns hidden if it's set..
16177         if (!this.rendered) {return ''};
16178         return !this.hiddenName && this.inputEl().dom.name  ? this.inputEl().dom.name : (this.hiddenName || '');
16179         
16180     },
16181     // private
16182     onViewMove : function(e, t){
16183         this.inKeyMode = false;
16184     },
16185
16186     // private
16187     onViewOver : function(e, t){
16188         if(this.inKeyMode){ // prevent key nav and mouse over conflicts
16189             return;
16190         }
16191         var item = this.view.findItemFromChild(t);
16192         
16193         if(item){
16194             var index = this.view.indexOf(item);
16195             this.select(index, false);
16196         }
16197     },
16198
16199     // private
16200     onViewClick : function(view, doFocus, el, e)
16201     {
16202         var index = this.view.getSelectedIndexes()[0];
16203         
16204         var r = this.store.getAt(index);
16205         
16206         if(this.tickable){
16207             
16208             if(typeof(e) != 'undefined' && e.getTarget().nodeName.toLowerCase() != 'input'){
16209                 return;
16210             }
16211             
16212             var rm = false;
16213             var _this = this;
16214             
16215             Roo.each(this.tickItems, function(v,k){
16216                 
16217                 if(typeof(v) != 'undefined' && v[_this.valueField] == r.data[_this.valueField]){
16218                     Roo.log(v);
16219                     _this.tickItems.splice(k, 1);
16220                     
16221                     if(typeof(e) == 'undefined' && view == false){
16222                         Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = false;
16223                     }
16224                     
16225                     rm = true;
16226                     return;
16227                 }
16228             });
16229             
16230             if(rm){
16231                 return;
16232             }
16233             
16234             if(this.fireEvent('tick', this, r, index, Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked) !== false){
16235                 this.tickItems.push(r.data);
16236             }
16237             
16238             if(typeof(e) == 'undefined' && view == false){
16239                 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = true;
16240             }
16241                     
16242             return;
16243         }
16244         
16245         if(r){
16246             this.onSelect(r, index);
16247         }
16248         if(doFocus !== false && !this.blockFocus){
16249             this.inputEl().focus();
16250         }
16251     },
16252
16253     // private
16254     restrictHeight : function(){
16255         //this.innerList.dom.style.height = '';
16256         //var inner = this.innerList.dom;
16257         //var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
16258         //this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
16259         //this.list.beginUpdate();
16260         //this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
16261         this.list.alignTo(this.inputEl(), this.listAlign);
16262         this.list.alignTo(this.inputEl(), this.listAlign);
16263         //this.list.endUpdate();
16264     },
16265
16266     // private
16267     onEmptyResults : function(){
16268         
16269         if(this.tickable && this.editable){
16270             this.hasFocus = false;
16271             this.restrictHeight();
16272             return;
16273         }
16274         
16275         this.collapse();
16276     },
16277
16278     /**
16279      * Returns true if the dropdown list is expanded, else false.
16280      */
16281     isExpanded : function(){
16282         return this.list.isVisible();
16283     },
16284
16285     /**
16286      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
16287      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
16288      * @param {String} value The data value of the item to select
16289      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
16290      * selected item if it is not currently in view (defaults to true)
16291      * @return {Boolean} True if the value matched an item in the list, else false
16292      */
16293     selectByValue : function(v, scrollIntoView){
16294         if(v !== undefined && v !== null){
16295             var r = this.findRecord(this.valueField || this.displayField, v);
16296             if(r){
16297                 this.select(this.store.indexOf(r), scrollIntoView);
16298                 return true;
16299             }
16300         }
16301         return false;
16302     },
16303
16304     /**
16305      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
16306      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
16307      * @param {Number} index The zero-based index of the list item to select
16308      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
16309      * selected item if it is not currently in view (defaults to true)
16310      */
16311     select : function(index, scrollIntoView){
16312         this.selectedIndex = index;
16313         this.view.select(index);
16314         if(scrollIntoView !== false){
16315             var el = this.view.getNode(index);
16316             /*
16317              * el && !this.multiple && !this.tickable // not sure why we disable multiple before..
16318              */
16319             if(el){
16320                 this.list.scrollChildIntoView(el, false);
16321             }
16322         }
16323     },
16324
16325     // private
16326     selectNext : function(){
16327         var ct = this.store.getCount();
16328         if(ct > 0){
16329             if(this.selectedIndex == -1){
16330                 this.select(0);
16331             }else if(this.selectedIndex < ct-1){
16332                 this.select(this.selectedIndex+1);
16333             }
16334         }
16335     },
16336
16337     // private
16338     selectPrev : function(){
16339         var ct = this.store.getCount();
16340         if(ct > 0){
16341             if(this.selectedIndex == -1){
16342                 this.select(0);
16343             }else if(this.selectedIndex != 0){
16344                 this.select(this.selectedIndex-1);
16345             }
16346         }
16347     },
16348
16349     // private
16350     onKeyUp : function(e){
16351         if(this.editable !== false && !e.isSpecialKey()){
16352             this.lastKey = e.getKey();
16353             this.dqTask.delay(this.queryDelay);
16354         }
16355     },
16356
16357     // private
16358     validateBlur : function(){
16359         return !this.list || !this.list.isVisible();   
16360     },
16361
16362     // private
16363     initQuery : function(){
16364         
16365         var v = this.getRawValue();
16366         
16367         if(this.tickable && this.editable){
16368             v = this.tickableInputEl().getValue();
16369         }
16370         
16371         this.doQuery(v);
16372     },
16373
16374     // private
16375     doForce : function(){
16376         if(this.inputEl().dom.value.length > 0){
16377             this.inputEl().dom.value =
16378                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
16379              
16380         }
16381     },
16382
16383     /**
16384      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
16385      * query allowing the query action to be canceled if needed.
16386      * @param {String} query The SQL query to execute
16387      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
16388      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
16389      * saved in the current store (defaults to false)
16390      */
16391     doQuery : function(q, forceAll){
16392         
16393         if(q === undefined || q === null){
16394             q = '';
16395         }
16396         var qe = {
16397             query: q,
16398             forceAll: forceAll,
16399             combo: this,
16400             cancel:false
16401         };
16402         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
16403             return false;
16404         }
16405         q = qe.query;
16406         
16407         forceAll = qe.forceAll;
16408         if(forceAll === true || (q.length >= this.minChars)){
16409             
16410             this.hasQuery = true;
16411             
16412             if(this.lastQuery != q || this.alwaysQuery){
16413                 this.lastQuery = q;
16414                 if(this.mode == 'local'){
16415                     this.selectedIndex = -1;
16416                     if(forceAll){
16417                         this.store.clearFilter();
16418                     }else{
16419                         
16420                         if(this.specialFilter){
16421                             this.fireEvent('specialfilter', this);
16422                             this.onLoad();
16423                             return;
16424                         }
16425                         
16426                         this.store.filter(this.displayField, q);
16427                     }
16428                     
16429                     this.store.fireEvent("datachanged", this.store);
16430                     
16431                     this.onLoad();
16432                     
16433                     
16434                 }else{
16435                     
16436                     this.store.baseParams[this.queryParam] = q;
16437                     
16438                     var options = {params : this.getParams(q)};
16439                     
16440                     if(this.loadNext){
16441                         options.add = true;
16442                         options.params.start = this.page * this.pageSize;
16443                     }
16444                     
16445                     this.store.load(options);
16446                     
16447                     /*
16448                      *  this code will make the page width larger, at the beginning, the list not align correctly, 
16449                      *  we should expand the list on onLoad
16450                      *  so command out it
16451                      */
16452 //                    this.expand();
16453                 }
16454             }else{
16455                 this.selectedIndex = -1;
16456                 this.onLoad();   
16457             }
16458         }
16459         
16460         this.loadNext = false;
16461     },
16462     
16463     // private
16464     getParams : function(q){
16465         var p = {};
16466         //p[this.queryParam] = q;
16467         
16468         if(this.pageSize){
16469             p.start = 0;
16470             p.limit = this.pageSize;
16471         }
16472         return p;
16473     },
16474
16475     /**
16476      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
16477      */
16478     collapse : function(){
16479         if(!this.isExpanded()){
16480             return;
16481         }
16482         
16483         this.list.hide();
16484         
16485         this.hasFocus = false;
16486         
16487         if(this.tickable){
16488             this.okBtn.hide();
16489             this.cancelBtn.hide();
16490             this.trigger.show();
16491             
16492             if(this.editable){
16493                 this.tickableInputEl().dom.value = '';
16494                 this.tickableInputEl().blur();
16495             }
16496             
16497         }
16498         
16499         Roo.get(document).un('mousedown', this.collapseIf, this);
16500         Roo.get(document).un('mousewheel', this.collapseIf, this);
16501         if (!this.editable) {
16502             Roo.get(document).un('keydown', this.listKeyPress, this);
16503         }
16504         this.fireEvent('collapse', this);
16505         
16506         this.validate();
16507     },
16508
16509     // private
16510     collapseIf : function(e){
16511         var in_combo  = e.within(this.el);
16512         var in_list =  e.within(this.list);
16513         var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
16514         
16515         if (in_combo || in_list || is_list) {
16516             //e.stopPropagation();
16517             return;
16518         }
16519         
16520         if(this.tickable){
16521             this.onTickableFooterButtonClick(e, false, false);
16522         }
16523
16524         this.collapse();
16525         
16526     },
16527
16528     /**
16529      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
16530      */
16531     expand : function(){
16532        
16533         if(this.isExpanded() || !this.hasFocus){
16534             return;
16535         }
16536         
16537         var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
16538         this.list.setWidth(lw);
16539         
16540         Roo.log('expand');
16541         
16542         this.list.show();
16543         
16544         this.restrictHeight();
16545         
16546         if(this.tickable){
16547             
16548             this.tickItems = Roo.apply([], this.item);
16549             
16550             this.okBtn.show();
16551             this.cancelBtn.show();
16552             this.trigger.hide();
16553             
16554             if(this.editable){
16555                 this.tickableInputEl().focus();
16556             }
16557             
16558         }
16559         
16560         Roo.get(document).on('mousedown', this.collapseIf, this);
16561         Roo.get(document).on('mousewheel', this.collapseIf, this);
16562         if (!this.editable) {
16563             Roo.get(document).on('keydown', this.listKeyPress, this);
16564         }
16565         
16566         this.fireEvent('expand', this);
16567     },
16568
16569     // private
16570     // Implements the default empty TriggerField.onTriggerClick function
16571     onTriggerClick : function(e)
16572     {
16573         Roo.log('trigger click');
16574         
16575         if(this.disabled || !this.triggerList){
16576             return;
16577         }
16578         
16579         this.page = 0;
16580         this.loadNext = false;
16581         
16582         if(this.isExpanded()){
16583             this.collapse();
16584             if (!this.blockFocus) {
16585                 this.inputEl().focus();
16586             }
16587             
16588         }else {
16589             this.hasFocus = true;
16590             if(this.triggerAction == 'all') {
16591                 this.doQuery(this.allQuery, true);
16592             } else {
16593                 this.doQuery(this.getRawValue());
16594             }
16595             if (!this.blockFocus) {
16596                 this.inputEl().focus();
16597             }
16598         }
16599     },
16600     
16601     onTickableTriggerClick : function(e)
16602     {
16603         if(this.disabled){
16604             return;
16605         }
16606         
16607         this.page = 0;
16608         this.loadNext = false;
16609         this.hasFocus = true;
16610         
16611         if(this.triggerAction == 'all') {
16612             this.doQuery(this.allQuery, true);
16613         } else {
16614             this.doQuery(this.getRawValue());
16615         }
16616     },
16617     
16618     onSearchFieldClick : function(e)
16619     {
16620         if(this.hasFocus && !this.disabled && e.getTarget().nodeName.toLowerCase() != 'button'){
16621             this.onTickableFooterButtonClick(e, false, false);
16622             return;
16623         }
16624         
16625         if(this.hasFocus || this.disabled || e.getTarget().nodeName.toLowerCase() == 'button'){
16626             return;
16627         }
16628         
16629         this.page = 0;
16630         this.loadNext = false;
16631         this.hasFocus = true;
16632         
16633         if(this.triggerAction == 'all') {
16634             this.doQuery(this.allQuery, true);
16635         } else {
16636             this.doQuery(this.getRawValue());
16637         }
16638     },
16639     
16640     listKeyPress : function(e)
16641     {
16642         //Roo.log('listkeypress');
16643         // scroll to first matching element based on key pres..
16644         if (e.isSpecialKey()) {
16645             return false;
16646         }
16647         var k = String.fromCharCode(e.getKey()).toUpperCase();
16648         //Roo.log(k);
16649         var match  = false;
16650         var csel = this.view.getSelectedNodes();
16651         var cselitem = false;
16652         if (csel.length) {
16653             var ix = this.view.indexOf(csel[0]);
16654             cselitem  = this.store.getAt(ix);
16655             if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
16656                 cselitem = false;
16657             }
16658             
16659         }
16660         
16661         this.store.each(function(v) { 
16662             if (cselitem) {
16663                 // start at existing selection.
16664                 if (cselitem.id == v.id) {
16665                     cselitem = false;
16666                 }
16667                 return true;
16668             }
16669                 
16670             if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
16671                 match = this.store.indexOf(v);
16672                 return false;
16673             }
16674             return true;
16675         }, this);
16676         
16677         if (match === false) {
16678             return true; // no more action?
16679         }
16680         // scroll to?
16681         this.view.select(match);
16682         var sn = Roo.get(this.view.getSelectedNodes()[0]);
16683         sn.scrollIntoView(sn.dom.parentNode, false);
16684     },
16685     
16686     onViewScroll : function(e, t){
16687         
16688         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){
16689             return;
16690         }
16691         
16692         this.hasQuery = true;
16693         
16694         this.loading = this.list.select('.loading', true).first();
16695         
16696         if(this.loading === null){
16697             this.list.createChild({
16698                 tag: 'div',
16699                 cls: 'loading roo-select2-more-results roo-select2-active',
16700                 html: 'Loading more results...'
16701             });
16702             
16703             this.loading = this.list.select('.loading', true).first();
16704             
16705             this.loading.setVisibilityMode(Roo.Element.DISPLAY);
16706             
16707             this.loading.hide();
16708         }
16709         
16710         this.loading.show();
16711         
16712         var _combo = this;
16713         
16714         this.page++;
16715         this.loadNext = true;
16716         
16717         (function() { _combo.doQuery(_combo.allQuery, true); }).defer(500);
16718         
16719         return;
16720     },
16721     
16722     addItem : function(o)
16723     {   
16724         var dv = ''; // display value
16725         
16726         if (this.displayField) {
16727             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
16728         } else {
16729             // this is an error condition!!!
16730             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
16731         }
16732         
16733         if(!dv.length){
16734             return;
16735         }
16736         
16737         var choice = this.choices.createChild({
16738             tag: 'li',
16739             cls: 'roo-select2-search-choice',
16740             cn: [
16741                 {
16742                     tag: 'div',
16743                     html: dv
16744                 },
16745                 {
16746                     tag: 'a',
16747                     href: '#',
16748                     cls: 'roo-select2-search-choice-close fa fa-times',
16749                     tabindex: '-1'
16750                 }
16751             ]
16752             
16753         }, this.searchField);
16754         
16755         var close = choice.select('a.roo-select2-search-choice-close', true).first();
16756         
16757         close.on('click', this.onRemoveItem, this, { item : choice, data : o} );
16758         
16759         this.item.push(o);
16760         
16761         this.lastData = o;
16762         
16763         this.syncValue();
16764         
16765         this.inputEl().dom.value = '';
16766         
16767         this.validate();
16768     },
16769     
16770     onRemoveItem : function(e, _self, o)
16771     {
16772         e.preventDefault();
16773         
16774         this.lastItem = Roo.apply([], this.item);
16775         
16776         var index = this.item.indexOf(o.data) * 1;
16777         
16778         if( index < 0){
16779             Roo.log('not this item?!');
16780             return;
16781         }
16782         
16783         this.item.splice(index, 1);
16784         o.item.remove();
16785         
16786         this.syncValue();
16787         
16788         this.fireEvent('remove', this, e);
16789         
16790         this.validate();
16791         
16792     },
16793     
16794     syncValue : function()
16795     {
16796         if(!this.item.length){
16797             this.clearValue();
16798             return;
16799         }
16800             
16801         var value = [];
16802         var _this = this;
16803         Roo.each(this.item, function(i){
16804             if(_this.valueField){
16805                 value.push(i[_this.valueField]);
16806                 return;
16807             }
16808
16809             value.push(i);
16810         });
16811
16812         this.value = value.join(',');
16813
16814         if(this.hiddenField){
16815             this.hiddenField.dom.value = this.value;
16816         }
16817         
16818         this.store.fireEvent("datachanged", this.store);
16819         
16820         this.validate();
16821     },
16822     
16823     clearItem : function()
16824     {
16825         if(!this.multiple){
16826             return;
16827         }
16828         
16829         this.item = [];
16830         
16831         Roo.each(this.choices.select('>li.roo-select2-search-choice', true).elements, function(c){
16832            c.remove();
16833         });
16834         
16835         this.syncValue();
16836         
16837         this.validate();
16838         
16839         if(this.tickable && !Roo.isTouch){
16840             this.view.refresh();
16841         }
16842     },
16843     
16844     inputEl: function ()
16845     {
16846         if(Roo.isIOS && this.useNativeIOS){
16847             return this.el.select('select.roo-ios-select', true).first();
16848         }
16849         
16850         if(Roo.isTouch && this.mobileTouchView){
16851             return this.el.select('input.form-control',true).first();
16852         }
16853         
16854         if(this.tickable){
16855             return this.searchField;
16856         }
16857         
16858         return this.el.select('input.form-control',true).first();
16859     },
16860     
16861     onTickableFooterButtonClick : function(e, btn, el)
16862     {
16863         e.preventDefault();
16864         
16865         this.lastItem = Roo.apply([], this.item);
16866         
16867         if(btn && btn.name == 'cancel'){
16868             this.tickItems = Roo.apply([], this.item);
16869             this.collapse();
16870             return;
16871         }
16872         
16873         this.clearItem();
16874         
16875         var _this = this;
16876         
16877         Roo.each(this.tickItems, function(o){
16878             _this.addItem(o);
16879         });
16880         
16881         this.collapse();
16882         
16883     },
16884     
16885     validate : function()
16886     {
16887         if(this.getVisibilityEl().hasClass('hidden')){
16888             return true;
16889         }
16890         
16891         var v = this.getRawValue();
16892         
16893         if(this.multiple){
16894             v = this.getValue();
16895         }
16896         
16897         if(this.disabled || this.allowBlank || v.length){
16898             this.markValid();
16899             return true;
16900         }
16901         
16902         this.markInvalid();
16903         return false;
16904     },
16905     
16906     tickableInputEl : function()
16907     {
16908         if(!this.tickable || !this.editable){
16909             return this.inputEl();
16910         }
16911         
16912         return this.inputEl().select('.roo-select2-search-field-input', true).first();
16913     },
16914     
16915     
16916     getAutoCreateTouchView : function()
16917     {
16918         var id = Roo.id();
16919         
16920         var cfg = {
16921             cls: 'form-group' //input-group
16922         };
16923         
16924         var input =  {
16925             tag: 'input',
16926             id : id,
16927             type : this.inputType,
16928             cls : 'form-control x-combo-noedit',
16929             autocomplete: 'new-password',
16930             placeholder : this.placeholder || '',
16931             readonly : true
16932         };
16933         
16934         if (this.name) {
16935             input.name = this.name;
16936         }
16937         
16938         if (this.size) {
16939             input.cls += ' input-' + this.size;
16940         }
16941         
16942         if (this.disabled) {
16943             input.disabled = true;
16944         }
16945         
16946         var inputblock = {
16947             cls : 'roo-combobox-wrap',
16948             cn : [
16949                 input
16950             ]
16951         };
16952         
16953         if(this.before){
16954             inputblock.cls += ' input-group';
16955             
16956             inputblock.cn.unshift({
16957                 tag :'span',
16958                 cls : 'input-group-addon input-group-prepend input-group-text',
16959                 html : this.before
16960             });
16961         }
16962         
16963         if(this.removable && !this.multiple){
16964             inputblock.cls += ' roo-removable';
16965             
16966             inputblock.cn.push({
16967                 tag: 'button',
16968                 html : 'x',
16969                 cls : 'roo-combo-removable-btn close'
16970             });
16971         }
16972
16973         if(this.hasFeedback && !this.allowBlank){
16974             
16975             inputblock.cls += ' has-feedback';
16976             
16977             inputblock.cn.push({
16978                 tag: 'span',
16979                 cls: 'glyphicon form-control-feedback'
16980             });
16981             
16982         }
16983         
16984         if (this.after) {
16985             
16986             inputblock.cls += (this.before) ? '' : ' input-group';
16987             
16988             inputblock.cn.push({
16989                 tag :'span',
16990                 cls : 'input-group-addon input-group-append input-group-text',
16991                 html : this.after
16992             });
16993         }
16994
16995         
16996         var ibwrap = inputblock;
16997         
16998         if(this.multiple){
16999             ibwrap = {
17000                 tag: 'ul',
17001                 cls: 'roo-select2-choices',
17002                 cn:[
17003                     {
17004                         tag: 'li',
17005                         cls: 'roo-select2-search-field',
17006                         cn: [
17007
17008                             inputblock
17009                         ]
17010                     }
17011                 ]
17012             };
17013         
17014             
17015         }
17016         
17017         var combobox = {
17018             cls: 'roo-select2-container input-group roo-touchview-combobox ',
17019             cn: [
17020                 {
17021                     tag: 'input',
17022                     type : 'hidden',
17023                     cls: 'form-hidden-field'
17024                 },
17025                 ibwrap
17026             ]
17027         };
17028         
17029         if(!this.multiple && this.showToggleBtn){
17030             
17031             var caret = {
17032                 cls: 'caret'
17033             };
17034             
17035             if (this.caret != false) {
17036                 caret = {
17037                      tag: 'i',
17038                      cls: 'fa fa-' + this.caret
17039                 };
17040                 
17041             }
17042             
17043             combobox.cn.push({
17044                 tag :'span',
17045                 cls : 'input-group-addon input-group-append input-group-text btn dropdown-toggle',
17046                 cn : [
17047                     Roo.bootstrap.version == 3 ? caret : '',
17048                     {
17049                         tag: 'span',
17050                         cls: 'combobox-clear',
17051                         cn  : [
17052                             {
17053                                 tag : 'i',
17054                                 cls: 'icon-remove'
17055                             }
17056                         ]
17057                     }
17058                 ]
17059
17060             })
17061         }
17062         
17063         if(this.multiple){
17064             combobox.cls += ' roo-select2-container-multi';
17065         }
17066         
17067         var align = this.labelAlign || this.parentLabelAlign();
17068         
17069         if (align ==='left' && this.fieldLabel.length) {
17070
17071             cfg.cn = [
17072                 {
17073                    tag : 'i',
17074                    cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
17075                    tooltip : 'This field is required'
17076                 },
17077                 {
17078                     tag: 'label',
17079                     cls : 'control-label col-form-label',
17080                     html : this.fieldLabel
17081
17082                 },
17083                 {
17084                     cls : 'roo-combobox-wrap ', 
17085                     cn: [
17086                         combobox
17087                     ]
17088                 }
17089             ];
17090             
17091             var labelCfg = cfg.cn[1];
17092             var contentCfg = cfg.cn[2];
17093             
17094
17095             if(this.indicatorpos == 'right'){
17096                 cfg.cn = [
17097                     {
17098                         tag: 'label',
17099                         'for' :  id,
17100                         cls : 'control-label col-form-label',
17101                         cn : [
17102                             {
17103                                 tag : 'span',
17104                                 html : this.fieldLabel
17105                             },
17106                             {
17107                                 tag : 'i',
17108                                 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
17109                                 tooltip : 'This field is required'
17110                             }
17111                         ]
17112                     },
17113                     {
17114                         cls : "roo-combobox-wrap ",
17115                         cn: [
17116                             combobox
17117                         ]
17118                     }
17119
17120                 ];
17121                 
17122                 labelCfg = cfg.cn[0];
17123                 contentCfg = cfg.cn[1];
17124             }
17125             
17126            
17127             
17128             if(this.labelWidth > 12){
17129                 labelCfg.style = "width: " + this.labelWidth + 'px';
17130             }
17131            
17132             if(this.labelWidth < 13 && this.labelmd == 0){
17133                 this.labelmd = this.labelWidth;
17134             }
17135             
17136             if(this.labellg > 0){
17137                 labelCfg.cls += ' col-lg-' + this.labellg;
17138                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
17139             }
17140             
17141             if(this.labelmd > 0){
17142                 labelCfg.cls += ' col-md-' + this.labelmd;
17143                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
17144             }
17145             
17146             if(this.labelsm > 0){
17147                 labelCfg.cls += ' col-sm-' + this.labelsm;
17148                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
17149             }
17150             
17151             if(this.labelxs > 0){
17152                 labelCfg.cls += ' col-xs-' + this.labelxs;
17153                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
17154             }
17155                 
17156                 
17157         } else if ( this.fieldLabel.length) {
17158             cfg.cn = [
17159                 {
17160                    tag : 'i',
17161                    cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
17162                    tooltip : 'This field is required'
17163                 },
17164                 {
17165                     tag: 'label',
17166                     cls : 'control-label',
17167                     html : this.fieldLabel
17168
17169                 },
17170                 {
17171                     cls : '', 
17172                     cn: [
17173                         combobox
17174                     ]
17175                 }
17176             ];
17177             
17178             if(this.indicatorpos == 'right'){
17179                 cfg.cn = [
17180                     {
17181                         tag: 'label',
17182                         cls : 'control-label',
17183                         html : this.fieldLabel,
17184                         cn : [
17185                             {
17186                                tag : 'i',
17187                                cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
17188                                tooltip : 'This field is required'
17189                             }
17190                         ]
17191                     },
17192                     {
17193                         cls : '', 
17194                         cn: [
17195                             combobox
17196                         ]
17197                     }
17198                 ];
17199             }
17200         } else {
17201             cfg.cn = combobox;    
17202         }
17203         
17204         
17205         var settings = this;
17206         
17207         ['xs','sm','md','lg'].map(function(size){
17208             if (settings[size]) {
17209                 cfg.cls += ' col-' + size + '-' + settings[size];
17210             }
17211         });
17212         
17213         return cfg;
17214     },
17215     
17216     initTouchView : function()
17217     {
17218         this.renderTouchView();
17219         
17220         this.touchViewEl.on('scroll', function(){
17221             this.el.dom.scrollTop = 0;
17222         }, this);
17223         
17224         this.originalValue = this.getValue();
17225         
17226         this.triggerEl = this.el.select('span.dropdown-toggle',true).first();
17227         
17228         this.inputEl().on("click", this.showTouchView, this);
17229         if (this.triggerEl) {
17230             this.triggerEl.on("click", this.showTouchView, this);
17231         }
17232         
17233         
17234         this.touchViewFooterEl.select('.roo-touch-view-cancel', true).first().on('click', this.hideTouchView, this);
17235         this.touchViewFooterEl.select('.roo-touch-view-ok', true).first().on('click', this.setTouchViewValue, this);
17236         
17237         this.maskEl = new Roo.LoadMask(this.touchViewEl, { store : this.store, msgCls: 'roo-el-mask-msg' });
17238         
17239         this.store.on('beforeload', this.onTouchViewBeforeLoad, this);
17240         this.store.on('load', this.onTouchViewLoad, this);
17241         this.store.on('loadexception', this.onTouchViewLoadException, this);
17242         
17243         if(this.hiddenName){
17244             
17245             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
17246             
17247             this.hiddenField.dom.value =
17248                 this.hiddenValue !== undefined ? this.hiddenValue :
17249                 this.value !== undefined ? this.value : '';
17250         
17251             this.el.dom.removeAttribute('name');
17252             this.hiddenField.dom.setAttribute('name', this.hiddenName);
17253         }
17254         
17255         if(this.multiple){
17256             this.choices = this.el.select('ul.roo-select2-choices', true).first();
17257             this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
17258         }
17259         
17260         if(this.removable && !this.multiple){
17261             var close = this.closeTriggerEl();
17262             if(close){
17263                 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
17264                 close.on('click', this.removeBtnClick, this, close);
17265             }
17266         }
17267         /*
17268          * fix the bug in Safari iOS8
17269          */
17270         this.inputEl().on("focus", function(e){
17271             document.activeElement.blur();
17272         }, this);
17273         
17274         this._touchViewMask = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
17275         
17276         return;
17277         
17278         
17279     },
17280     
17281     renderTouchView : function()
17282     {
17283         this.touchViewEl = Roo.get(document.body).createChild(Roo.bootstrap.ComboBox.touchViewTemplate);
17284         this.touchViewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17285         
17286         this.touchViewHeaderEl = this.touchViewEl.select('.modal-header', true).first();
17287         this.touchViewHeaderEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17288         
17289         this.touchViewBodyEl = this.touchViewEl.select('.modal-body', true).first();
17290         this.touchViewBodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17291         this.touchViewBodyEl.setStyle('overflow', 'auto');
17292         
17293         this.touchViewListGroup = this.touchViewBodyEl.select('.list-group', true).first();
17294         this.touchViewListGroup.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17295         
17296         this.touchViewFooterEl = this.touchViewEl.select('.modal-footer', true).first();
17297         this.touchViewFooterEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17298         
17299     },
17300     
17301     showTouchView : function()
17302     {
17303         if(this.disabled){
17304             return;
17305         }
17306         
17307         this.touchViewHeaderEl.hide();
17308
17309         if(this.modalTitle.length){
17310             this.touchViewHeaderEl.dom.innerHTML = this.modalTitle;
17311             this.touchViewHeaderEl.show();
17312         }
17313
17314         this.touchViewEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
17315         this.touchViewEl.show();
17316
17317         this.touchViewEl.select('.modal-dialog', true).first().setStyle({ margin : '0px', width : '100%'});
17318         
17319         //this.touchViewEl.select('.modal-dialog > .modal-content', true).first().setSize(
17320         //        Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
17321
17322         var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
17323
17324         if(this.modalTitle.length){
17325             bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
17326         }
17327         
17328         this.touchViewBodyEl.setHeight(bodyHeight);
17329
17330         if(this.animate){
17331             var _this = this;
17332             (function(){ _this.touchViewEl.addClass(['in','show']); }).defer(50);
17333         }else{
17334             this.touchViewEl.addClass(['in','show']);
17335         }
17336         
17337         if(this._touchViewMask){
17338             Roo.get(document.body).addClass("x-body-masked");
17339             this._touchViewMask.setSize(Roo.lib.Dom.getViewWidth(true),   Roo.lib.Dom.getViewHeight(true));
17340             this._touchViewMask.setStyle('z-index', 10000);
17341             this._touchViewMask.addClass('show');
17342         }
17343         
17344         this.doTouchViewQuery();
17345         
17346     },
17347     
17348     hideTouchView : function()
17349     {
17350         this.touchViewEl.removeClass(['in','show']);
17351
17352         if(this.animate){
17353             var _this = this;
17354             (function(){ _this.touchViewEl.setStyle('display', 'none'); }).defer(150);
17355         }else{
17356             this.touchViewEl.setStyle('display', 'none');
17357         }
17358         
17359         if(this._touchViewMask){
17360             this._touchViewMask.removeClass('show');
17361             Roo.get(document.body).removeClass("x-body-masked");
17362         }
17363     },
17364     
17365     setTouchViewValue : function()
17366     {
17367         if(this.multiple){
17368             this.clearItem();
17369         
17370             var _this = this;
17371
17372             Roo.each(this.tickItems, function(o){
17373                 this.addItem(o);
17374             }, this);
17375         }
17376         
17377         this.hideTouchView();
17378     },
17379     
17380     doTouchViewQuery : function()
17381     {
17382         var qe = {
17383             query: '',
17384             forceAll: true,
17385             combo: this,
17386             cancel:false
17387         };
17388         
17389         if(this.fireEvent('beforequery', qe) ===false || qe.cancel){
17390             return false;
17391         }
17392         
17393         if(!this.alwaysQuery || this.mode == 'local'){
17394             this.onTouchViewLoad();
17395             return;
17396         }
17397         
17398         this.store.load();
17399     },
17400     
17401     onTouchViewBeforeLoad : function(combo,opts)
17402     {
17403         return;
17404     },
17405
17406     // private
17407     onTouchViewLoad : function()
17408     {
17409         if(this.store.getCount() < 1){
17410             this.onTouchViewEmptyResults();
17411             return;
17412         }
17413         
17414         this.clearTouchView();
17415         
17416         var rawValue = this.getRawValue();
17417         
17418         var template = (this.multiple) ? Roo.bootstrap.ComboBox.listItemCheckbox : Roo.bootstrap.ComboBox.listItemRadio;
17419         
17420         this.tickItems = [];
17421         
17422         this.store.data.each(function(d, rowIndex){
17423             var row = this.touchViewListGroup.createChild(template);
17424             
17425             if(typeof(d.data.cls) != 'undefined' && d.data.cls.length){
17426                 row.addClass(d.data.cls);
17427             }
17428             
17429             if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
17430                 var cfg = {
17431                     data : d.data,
17432                     html : d.data[this.displayField]
17433                 };
17434                 
17435                 if(this.fireEvent('touchviewdisplay', this, cfg) !== false){
17436                     row.select('.roo-combobox-list-group-item-value', true).first().dom.innerHTML = cfg.html;
17437                 }
17438             }
17439             row.removeClass('selected');
17440             if(!this.multiple && this.valueField &&
17441                     typeof(d.data[this.valueField]) != 'undefined' && d.data[this.valueField] == this.getValue())
17442             {
17443                 // radio buttons..
17444                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
17445                 row.addClass('selected');
17446             }
17447             
17448             if(this.multiple && this.valueField &&
17449                     typeof(d.data[this.valueField]) != 'undefined' && this.getValue().indexOf(d.data[this.valueField]) != -1)
17450             {
17451                 
17452                 // checkboxes...
17453                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
17454                 this.tickItems.push(d.data);
17455             }
17456             
17457             row.on('click', this.onTouchViewClick, this, {row : row, rowIndex : rowIndex});
17458             
17459         }, this);
17460         
17461         var firstChecked = this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).first();
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         var listHeight = this.touchViewListGroup.getHeight() + this.touchViewBodyEl.getPadding('tb') * 2;
17470         
17471         if(this.mobile_restrict_height && listHeight < bodyHeight){
17472             this.touchViewBodyEl.setHeight(listHeight);
17473         }
17474         
17475         var _this = this;
17476         
17477         if(firstChecked && listHeight > bodyHeight){
17478             (function() { firstChecked.findParent('li').scrollIntoView(_this.touchViewListGroup.dom); }).defer(500);
17479         }
17480         
17481     },
17482     
17483     onTouchViewLoadException : function()
17484     {
17485         this.hideTouchView();
17486     },
17487     
17488     onTouchViewEmptyResults : function()
17489     {
17490         this.clearTouchView();
17491         
17492         this.touchViewListGroup.createChild(Roo.bootstrap.ComboBox.emptyResult);
17493         
17494         this.touchViewListGroup.select('.roo-combobox-touch-view-empty-result', true).first().dom.innerHTML = this.emptyResultText;
17495         
17496     },
17497     
17498     clearTouchView : function()
17499     {
17500         this.touchViewListGroup.dom.innerHTML = '';
17501     },
17502     
17503     onTouchViewClick : function(e, el, o)
17504     {
17505         e.preventDefault();
17506         
17507         var row = o.row;
17508         var rowIndex = o.rowIndex;
17509         
17510         var r = this.store.getAt(rowIndex);
17511         
17512         if(this.fireEvent('beforeselect', this, r, rowIndex) !== false){
17513             
17514             if(!this.multiple){
17515                 Roo.each(this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).elements, function(c){
17516                     c.dom.removeAttribute('checked');
17517                 }, this);
17518
17519                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
17520
17521                 this.setFromData(r.data);
17522
17523                 var close = this.closeTriggerEl();
17524
17525                 if(close){
17526                     close.show();
17527                 }
17528
17529                 this.hideTouchView();
17530
17531                 this.fireEvent('select', this, r, rowIndex);
17532
17533                 return;
17534             }
17535
17536             if(this.valueField && typeof(r.data[this.valueField]) != 'undefined' && this.getValue().indexOf(r.data[this.valueField]) != -1){
17537                 row.select('.roo-combobox-list-group-item-box > input', true).first().dom.removeAttribute('checked');
17538                 this.tickItems.splice(this.tickItems.indexOf(r.data), 1);
17539                 return;
17540             }
17541
17542             row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
17543             this.addItem(r.data);
17544             this.tickItems.push(r.data);
17545         }
17546     },
17547     
17548     getAutoCreateNativeIOS : function()
17549     {
17550         var cfg = {
17551             cls: 'form-group' //input-group,
17552         };
17553         
17554         var combobox =  {
17555             tag: 'select',
17556             cls : 'roo-ios-select'
17557         };
17558         
17559         if (this.name) {
17560             combobox.name = this.name;
17561         }
17562         
17563         if (this.disabled) {
17564             combobox.disabled = true;
17565         }
17566         
17567         var settings = this;
17568         
17569         ['xs','sm','md','lg'].map(function(size){
17570             if (settings[size]) {
17571                 cfg.cls += ' col-' + size + '-' + settings[size];
17572             }
17573         });
17574         
17575         cfg.cn = combobox;
17576         
17577         return cfg;
17578         
17579     },
17580     
17581     initIOSView : function()
17582     {
17583         this.store.on('load', this.onIOSViewLoad, this);
17584         
17585         return;
17586     },
17587     
17588     onIOSViewLoad : function()
17589     {
17590         if(this.store.getCount() < 1){
17591             return;
17592         }
17593         
17594         this.clearIOSView();
17595         
17596         if(this.allowBlank) {
17597             
17598             var default_text = '-- SELECT --';
17599             
17600             if(this.placeholder.length){
17601                 default_text = this.placeholder;
17602             }
17603             
17604             if(this.emptyTitle.length){
17605                 default_text += ' - ' + this.emptyTitle + ' -';
17606             }
17607             
17608             var opt = this.inputEl().createChild({
17609                 tag: 'option',
17610                 value : 0,
17611                 html : default_text
17612             });
17613             
17614             var o = {};
17615             o[this.valueField] = 0;
17616             o[this.displayField] = default_text;
17617             
17618             this.ios_options.push({
17619                 data : o,
17620                 el : opt
17621             });
17622             
17623         }
17624         
17625         this.store.data.each(function(d, rowIndex){
17626             
17627             var html = '';
17628             
17629             if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
17630                 html = d.data[this.displayField];
17631             }
17632             
17633             var value = '';
17634             
17635             if(this.valueField && typeof(d.data[this.valueField]) != 'undefined'){
17636                 value = d.data[this.valueField];
17637             }
17638             
17639             var option = {
17640                 tag: 'option',
17641                 value : value,
17642                 html : html
17643             };
17644             
17645             if(this.value == d.data[this.valueField]){
17646                 option['selected'] = true;
17647             }
17648             
17649             var opt = this.inputEl().createChild(option);
17650             
17651             this.ios_options.push({
17652                 data : d.data,
17653                 el : opt
17654             });
17655             
17656         }, this);
17657         
17658         this.inputEl().on('change', function(){
17659            this.fireEvent('select', this);
17660         }, this);
17661         
17662     },
17663     
17664     clearIOSView: function()
17665     {
17666         this.inputEl().dom.innerHTML = '';
17667         
17668         this.ios_options = [];
17669     },
17670     
17671     setIOSValue: function(v)
17672     {
17673         this.value = v;
17674         
17675         if(!this.ios_options){
17676             return;
17677         }
17678         
17679         Roo.each(this.ios_options, function(opts){
17680            
17681            opts.el.dom.removeAttribute('selected');
17682            
17683            if(opts.data[this.valueField] != v){
17684                return;
17685            }
17686            
17687            opts.el.dom.setAttribute('selected', true);
17688            
17689         }, this);
17690     }
17691
17692     /** 
17693     * @cfg {Boolean} grow 
17694     * @hide 
17695     */
17696     /** 
17697     * @cfg {Number} growMin 
17698     * @hide 
17699     */
17700     /** 
17701     * @cfg {Number} growMax 
17702     * @hide 
17703     */
17704     /**
17705      * @hide
17706      * @method autoSize
17707      */
17708 });
17709
17710 Roo.apply(Roo.bootstrap.ComboBox,  {
17711     
17712     header : {
17713         tag: 'div',
17714         cls: 'modal-header',
17715         cn: [
17716             {
17717                 tag: 'h4',
17718                 cls: 'modal-title'
17719             }
17720         ]
17721     },
17722     
17723     body : {
17724         tag: 'div',
17725         cls: 'modal-body',
17726         cn: [
17727             {
17728                 tag: 'ul',
17729                 cls: 'list-group'
17730             }
17731         ]
17732     },
17733     
17734     listItemRadio : {
17735         tag: 'li',
17736         cls: 'list-group-item',
17737         cn: [
17738             {
17739                 tag: 'span',
17740                 cls: 'roo-combobox-list-group-item-value'
17741             },
17742             {
17743                 tag: 'div',
17744                 cls: 'roo-combobox-list-group-item-box pull-xs-right radio-inline radio radio-info',
17745                 cn: [
17746                     {
17747                         tag: 'input',
17748                         type: 'radio'
17749                     },
17750                     {
17751                         tag: 'label'
17752                     }
17753                 ]
17754             }
17755         ]
17756     },
17757     
17758     listItemCheckbox : {
17759         tag: 'li',
17760         cls: 'list-group-item',
17761         cn: [
17762             {
17763                 tag: 'span',
17764                 cls: 'roo-combobox-list-group-item-value'
17765             },
17766             {
17767                 tag: 'div',
17768                 cls: 'roo-combobox-list-group-item-box pull-xs-right checkbox-inline checkbox checkbox-info',
17769                 cn: [
17770                     {
17771                         tag: 'input',
17772                         type: 'checkbox'
17773                     },
17774                     {
17775                         tag: 'label'
17776                     }
17777                 ]
17778             }
17779         ]
17780     },
17781     
17782     emptyResult : {
17783         tag: 'div',
17784         cls: 'alert alert-danger roo-combobox-touch-view-empty-result'
17785     },
17786     
17787     footer : {
17788         tag: 'div',
17789         cls: 'modal-footer',
17790         cn: [
17791             {
17792                 tag: 'div',
17793                 cls: 'row',
17794                 cn: [
17795                     {
17796                         tag: 'div',
17797                         cls: 'col-xs-6 text-left',
17798                         cn: {
17799                             tag: 'button',
17800                             cls: 'btn btn-danger roo-touch-view-cancel',
17801                             html: 'Cancel'
17802                         }
17803                     },
17804                     {
17805                         tag: 'div',
17806                         cls: 'col-xs-6 text-right',
17807                         cn: {
17808                             tag: 'button',
17809                             cls: 'btn btn-success roo-touch-view-ok',
17810                             html: 'OK'
17811                         }
17812                     }
17813                 ]
17814             }
17815         ]
17816         
17817     }
17818 });
17819
17820 Roo.apply(Roo.bootstrap.ComboBox,  {
17821     
17822     touchViewTemplate : {
17823         tag: 'div',
17824         cls: 'modal fade roo-combobox-touch-view',
17825         cn: [
17826             {
17827                 tag: 'div',
17828                 cls: 'modal-dialog',
17829                 style : 'position:fixed', // we have to fix position....
17830                 cn: [
17831                     {
17832                         tag: 'div',
17833                         cls: 'modal-content',
17834                         cn: [
17835                             Roo.bootstrap.ComboBox.header,
17836                             Roo.bootstrap.ComboBox.body,
17837                             Roo.bootstrap.ComboBox.footer
17838                         ]
17839                     }
17840                 ]
17841             }
17842         ]
17843     }
17844 });/*
17845  * Based on:
17846  * Ext JS Library 1.1.1
17847  * Copyright(c) 2006-2007, Ext JS, LLC.
17848  *
17849  * Originally Released Under LGPL - original licence link has changed is not relivant.
17850  *
17851  * Fork - LGPL
17852  * <script type="text/javascript">
17853  */
17854
17855 /**
17856  * @class Roo.View
17857  * @extends Roo.util.Observable
17858  * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template. 
17859  * This class also supports single and multi selection modes. <br>
17860  * Create a data model bound view:
17861  <pre><code>
17862  var store = new Roo.data.Store(...);
17863
17864  var view = new Roo.View({
17865     el : "my-element",
17866     tpl : '&lt;div id="{0}"&gt;{2} - {1}&lt;/div&gt;', // auto create template
17867  
17868     singleSelect: true,
17869     selectedClass: "ydataview-selected",
17870     store: store
17871  });
17872
17873  // listen for node click?
17874  view.on("click", function(vw, index, node, e){
17875  alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
17876  });
17877
17878  // load XML data
17879  dataModel.load("foobar.xml");
17880  </code></pre>
17881  For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
17882  * <br><br>
17883  * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
17884  * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
17885  * 
17886  * Note: old style constructor is still suported (container, template, config)
17887  * 
17888  * @constructor
17889  * Create a new View
17890  * @param {Object} config The config object
17891  * 
17892  */
17893 Roo.View = function(config, depreciated_tpl, depreciated_config){
17894     
17895     this.parent = false;
17896     
17897     if (typeof(depreciated_tpl) == 'undefined') {
17898         // new way.. - universal constructor.
17899         Roo.apply(this, config);
17900         this.el  = Roo.get(this.el);
17901     } else {
17902         // old format..
17903         this.el  = Roo.get(config);
17904         this.tpl = depreciated_tpl;
17905         Roo.apply(this, depreciated_config);
17906     }
17907     this.wrapEl  = this.el.wrap().wrap();
17908     ///this.el = this.wrapEla.appendChild(document.createElement("div"));
17909     
17910     
17911     if(typeof(this.tpl) == "string"){
17912         this.tpl = new Roo.Template(this.tpl);
17913     } else {
17914         // support xtype ctors..
17915         this.tpl = new Roo.factory(this.tpl, Roo);
17916     }
17917     
17918     
17919     this.tpl.compile();
17920     
17921     /** @private */
17922     this.addEvents({
17923         /**
17924          * @event beforeclick
17925          * Fires before a click is processed. Returns false to cancel the default action.
17926          * @param {Roo.View} this
17927          * @param {Number} index The index of the target node
17928          * @param {HTMLElement} node The target node
17929          * @param {Roo.EventObject} e The raw event object
17930          */
17931             "beforeclick" : true,
17932         /**
17933          * @event click
17934          * Fires when a template node is clicked.
17935          * @param {Roo.View} this
17936          * @param {Number} index The index of the target node
17937          * @param {HTMLElement} node The target node
17938          * @param {Roo.EventObject} e The raw event object
17939          */
17940             "click" : true,
17941         /**
17942          * @event dblclick
17943          * Fires when a template node is double clicked.
17944          * @param {Roo.View} this
17945          * @param {Number} index The index of the target node
17946          * @param {HTMLElement} node The target node
17947          * @param {Roo.EventObject} e The raw event object
17948          */
17949             "dblclick" : true,
17950         /**
17951          * @event contextmenu
17952          * Fires when a template node is right clicked.
17953          * @param {Roo.View} this
17954          * @param {Number} index The index of the target node
17955          * @param {HTMLElement} node The target node
17956          * @param {Roo.EventObject} e The raw event object
17957          */
17958             "contextmenu" : true,
17959         /**
17960          * @event selectionchange
17961          * Fires when the selected nodes change.
17962          * @param {Roo.View} this
17963          * @param {Array} selections Array of the selected nodes
17964          */
17965             "selectionchange" : true,
17966     
17967         /**
17968          * @event beforeselect
17969          * Fires before a selection is made. If any handlers return false, the selection is cancelled.
17970          * @param {Roo.View} this
17971          * @param {HTMLElement} node The node to be selected
17972          * @param {Array} selections Array of currently selected nodes
17973          */
17974             "beforeselect" : true,
17975         /**
17976          * @event preparedata
17977          * Fires on every row to render, to allow you to change the data.
17978          * @param {Roo.View} this
17979          * @param {Object} data to be rendered (change this)
17980          */
17981           "preparedata" : true
17982           
17983           
17984         });
17985
17986
17987
17988     this.el.on({
17989         "click": this.onClick,
17990         "dblclick": this.onDblClick,
17991         "contextmenu": this.onContextMenu,
17992         scope:this
17993     });
17994
17995     this.selections = [];
17996     this.nodes = [];
17997     this.cmp = new Roo.CompositeElementLite([]);
17998     if(this.store){
17999         this.store = Roo.factory(this.store, Roo.data);
18000         this.setStore(this.store, true);
18001     }
18002     
18003     if ( this.footer && this.footer.xtype) {
18004            
18005          var fctr = this.wrapEl.appendChild(document.createElement("div"));
18006         
18007         this.footer.dataSource = this.store;
18008         this.footer.container = fctr;
18009         this.footer = Roo.factory(this.footer, Roo);
18010         fctr.insertFirst(this.el);
18011         
18012         // this is a bit insane - as the paging toolbar seems to detach the el..
18013 //        dom.parentNode.parentNode.parentNode
18014          // they get detached?
18015     }
18016     
18017     
18018     Roo.View.superclass.constructor.call(this);
18019     
18020     
18021 };
18022
18023 Roo.extend(Roo.View, Roo.util.Observable, {
18024     
18025      /**
18026      * @cfg {Roo.data.Store} store Data store to load data from.
18027      */
18028     store : false,
18029     
18030     /**
18031      * @cfg {String|Roo.Element} el The container element.
18032      */
18033     el : '',
18034     
18035     /**
18036      * @cfg {String|Roo.Template} tpl The template used by this View 
18037      */
18038     tpl : false,
18039     /**
18040      * @cfg {String} dataName the named area of the template to use as the data area
18041      *                          Works with domtemplates roo-name="name"
18042      */
18043     dataName: false,
18044     /**
18045      * @cfg {String} selectedClass The css class to add to selected nodes
18046      */
18047     selectedClass : "x-view-selected",
18048      /**
18049      * @cfg {String} emptyText The empty text to show when nothing is loaded.
18050      */
18051     emptyText : "",
18052     
18053     /**
18054      * @cfg {String} text to display on mask (default Loading)
18055      */
18056     mask : false,
18057     /**
18058      * @cfg {Boolean} multiSelect Allow multiple selection
18059      */
18060     multiSelect : false,
18061     /**
18062      * @cfg {Boolean} singleSelect Allow single selection
18063      */
18064     singleSelect:  false,
18065     
18066     /**
18067      * @cfg {Boolean} toggleSelect - selecting 
18068      */
18069     toggleSelect : false,
18070     
18071     /**
18072      * @cfg {Boolean} tickable - selecting 
18073      */
18074     tickable : false,
18075     
18076     /**
18077      * Returns the element this view is bound to.
18078      * @return {Roo.Element}
18079      */
18080     getEl : function(){
18081         return this.wrapEl;
18082     },
18083     
18084     
18085
18086     /**
18087      * Refreshes the view. - called by datachanged on the store. - do not call directly.
18088      */
18089     refresh : function(){
18090         //Roo.log('refresh');
18091         var t = this.tpl;
18092         
18093         // if we are using something like 'domtemplate', then
18094         // the what gets used is:
18095         // t.applySubtemplate(NAME, data, wrapping data..)
18096         // the outer template then get' applied with
18097         //     the store 'extra data'
18098         // and the body get's added to the
18099         //      roo-name="data" node?
18100         //      <span class='roo-tpl-{name}'></span> ?????
18101         
18102         
18103         
18104         this.clearSelections();
18105         this.el.update("");
18106         var html = [];
18107         var records = this.store.getRange();
18108         if(records.length < 1) {
18109             
18110             // is this valid??  = should it render a template??
18111             
18112             this.el.update(this.emptyText);
18113             return;
18114         }
18115         var el = this.el;
18116         if (this.dataName) {
18117             this.el.update(t.apply(this.store.meta)); //????
18118             el = this.el.child('.roo-tpl-' + this.dataName);
18119         }
18120         
18121         for(var i = 0, len = records.length; i < len; i++){
18122             var data = this.prepareData(records[i].data, i, records[i]);
18123             this.fireEvent("preparedata", this, data, i, records[i]);
18124             
18125             var d = Roo.apply({}, data);
18126             
18127             if(this.tickable){
18128                 Roo.apply(d, {'roo-id' : Roo.id()});
18129                 
18130                 var _this = this;
18131             
18132                 Roo.each(this.parent.item, function(item){
18133                     if(item[_this.parent.valueField] != data[_this.parent.valueField]){
18134                         return;
18135                     }
18136                     Roo.apply(d, {'roo-data-checked' : 'checked'});
18137                 });
18138             }
18139             
18140             html[html.length] = Roo.util.Format.trim(
18141                 this.dataName ?
18142                     t.applySubtemplate(this.dataName, d, this.store.meta) :
18143                     t.apply(d)
18144             );
18145         }
18146         
18147         
18148         
18149         el.update(html.join(""));
18150         this.nodes = el.dom.childNodes;
18151         this.updateIndexes(0);
18152     },
18153     
18154
18155     /**
18156      * Function to override to reformat the data that is sent to
18157      * the template for each node.
18158      * DEPRICATED - use the preparedata event handler.
18159      * @param {Array/Object} data The raw data (array of colData for a data model bound view or
18160      * a JSON object for an UpdateManager bound view).
18161      */
18162     prepareData : function(data, index, record)
18163     {
18164         this.fireEvent("preparedata", this, data, index, record);
18165         return data;
18166     },
18167
18168     onUpdate : function(ds, record){
18169         // Roo.log('on update');   
18170         this.clearSelections();
18171         var index = this.store.indexOf(record);
18172         var n = this.nodes[index];
18173         this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
18174         n.parentNode.removeChild(n);
18175         this.updateIndexes(index, index);
18176     },
18177
18178     
18179     
18180 // --------- FIXME     
18181     onAdd : function(ds, records, index)
18182     {
18183         //Roo.log(['on Add', ds, records, index] );        
18184         this.clearSelections();
18185         if(this.nodes.length == 0){
18186             this.refresh();
18187             return;
18188         }
18189         var n = this.nodes[index];
18190         for(var i = 0, len = records.length; i < len; i++){
18191             var d = this.prepareData(records[i].data, i, records[i]);
18192             if(n){
18193                 this.tpl.insertBefore(n, d);
18194             }else{
18195                 
18196                 this.tpl.append(this.el, d);
18197             }
18198         }
18199         this.updateIndexes(index);
18200     },
18201
18202     onRemove : function(ds, record, index){
18203        // Roo.log('onRemove');
18204         this.clearSelections();
18205         var el = this.dataName  ?
18206             this.el.child('.roo-tpl-' + this.dataName) :
18207             this.el; 
18208         
18209         el.dom.removeChild(this.nodes[index]);
18210         this.updateIndexes(index);
18211     },
18212
18213     /**
18214      * Refresh an individual node.
18215      * @param {Number} index
18216      */
18217     refreshNode : function(index){
18218         this.onUpdate(this.store, this.store.getAt(index));
18219     },
18220
18221     updateIndexes : function(startIndex, endIndex){
18222         var ns = this.nodes;
18223         startIndex = startIndex || 0;
18224         endIndex = endIndex || ns.length - 1;
18225         for(var i = startIndex; i <= endIndex; i++){
18226             ns[i].nodeIndex = i;
18227         }
18228     },
18229
18230     /**
18231      * Changes the data store this view uses and refresh the view.
18232      * @param {Store} store
18233      */
18234     setStore : function(store, initial){
18235         if(!initial && this.store){
18236             this.store.un("datachanged", this.refresh);
18237             this.store.un("add", this.onAdd);
18238             this.store.un("remove", this.onRemove);
18239             this.store.un("update", this.onUpdate);
18240             this.store.un("clear", this.refresh);
18241             this.store.un("beforeload", this.onBeforeLoad);
18242             this.store.un("load", this.onLoad);
18243             this.store.un("loadexception", this.onLoad);
18244         }
18245         if(store){
18246           
18247             store.on("datachanged", this.refresh, this);
18248             store.on("add", this.onAdd, this);
18249             store.on("remove", this.onRemove, this);
18250             store.on("update", this.onUpdate, this);
18251             store.on("clear", this.refresh, this);
18252             store.on("beforeload", this.onBeforeLoad, this);
18253             store.on("load", this.onLoad, this);
18254             store.on("loadexception", this.onLoad, this);
18255         }
18256         
18257         if(store){
18258             this.refresh();
18259         }
18260     },
18261     /**
18262      * onbeforeLoad - masks the loading area.
18263      *
18264      */
18265     onBeforeLoad : function(store,opts)
18266     {
18267          //Roo.log('onBeforeLoad');   
18268         if (!opts.add) {
18269             this.el.update("");
18270         }
18271         this.el.mask(this.mask ? this.mask : "Loading" ); 
18272     },
18273     onLoad : function ()
18274     {
18275         this.el.unmask();
18276     },
18277     
18278
18279     /**
18280      * Returns the template node the passed child belongs to or null if it doesn't belong to one.
18281      * @param {HTMLElement} node
18282      * @return {HTMLElement} The template node
18283      */
18284     findItemFromChild : function(node){
18285         var el = this.dataName  ?
18286             this.el.child('.roo-tpl-' + this.dataName,true) :
18287             this.el.dom; 
18288         
18289         if(!node || node.parentNode == el){
18290                     return node;
18291             }
18292             var p = node.parentNode;
18293             while(p && p != el){
18294             if(p.parentNode == el){
18295                 return p;
18296             }
18297             p = p.parentNode;
18298         }
18299             return null;
18300     },
18301
18302     /** @ignore */
18303     onClick : function(e){
18304         var item = this.findItemFromChild(e.getTarget());
18305         if(item){
18306             var index = this.indexOf(item);
18307             if(this.onItemClick(item, index, e) !== false){
18308                 this.fireEvent("click", this, index, item, e);
18309             }
18310         }else{
18311             this.clearSelections();
18312         }
18313     },
18314
18315     /** @ignore */
18316     onContextMenu : function(e){
18317         var item = this.findItemFromChild(e.getTarget());
18318         if(item){
18319             this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
18320         }
18321     },
18322
18323     /** @ignore */
18324     onDblClick : function(e){
18325         var item = this.findItemFromChild(e.getTarget());
18326         if(item){
18327             this.fireEvent("dblclick", this, this.indexOf(item), item, e);
18328         }
18329     },
18330
18331     onItemClick : function(item, index, e)
18332     {
18333         if(this.fireEvent("beforeclick", this, index, item, e) === false){
18334             return false;
18335         }
18336         if (this.toggleSelect) {
18337             var m = this.isSelected(item) ? 'unselect' : 'select';
18338             //Roo.log(m);
18339             var _t = this;
18340             _t[m](item, true, false);
18341             return true;
18342         }
18343         if(this.multiSelect || this.singleSelect){
18344             if(this.multiSelect && e.shiftKey && this.lastSelection){
18345                 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
18346             }else{
18347                 this.select(item, this.multiSelect && e.ctrlKey);
18348                 this.lastSelection = item;
18349             }
18350             
18351             if(!this.tickable){
18352                 e.preventDefault();
18353             }
18354             
18355         }
18356         return true;
18357     },
18358
18359     /**
18360      * Get the number of selected nodes.
18361      * @return {Number}
18362      */
18363     getSelectionCount : function(){
18364         return this.selections.length;
18365     },
18366
18367     /**
18368      * Get the currently selected nodes.
18369      * @return {Array} An array of HTMLElements
18370      */
18371     getSelectedNodes : function(){
18372         return this.selections;
18373     },
18374
18375     /**
18376      * Get the indexes of the selected nodes.
18377      * @return {Array}
18378      */
18379     getSelectedIndexes : function(){
18380         var indexes = [], s = this.selections;
18381         for(var i = 0, len = s.length; i < len; i++){
18382             indexes.push(s[i].nodeIndex);
18383         }
18384         return indexes;
18385     },
18386
18387     /**
18388      * Clear all selections
18389      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
18390      */
18391     clearSelections : function(suppressEvent){
18392         if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
18393             this.cmp.elements = this.selections;
18394             this.cmp.removeClass(this.selectedClass);
18395             this.selections = [];
18396             if(!suppressEvent){
18397                 this.fireEvent("selectionchange", this, this.selections);
18398             }
18399         }
18400     },
18401
18402     /**
18403      * Returns true if the passed node is selected
18404      * @param {HTMLElement/Number} node The node or node index
18405      * @return {Boolean}
18406      */
18407     isSelected : function(node){
18408         var s = this.selections;
18409         if(s.length < 1){
18410             return false;
18411         }
18412         node = this.getNode(node);
18413         return s.indexOf(node) !== -1;
18414     },
18415
18416     /**
18417      * Selects nodes.
18418      * @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
18419      * @param {Boolean} keepExisting (optional) true to keep existing selections
18420      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
18421      */
18422     select : function(nodeInfo, keepExisting, suppressEvent){
18423         if(nodeInfo instanceof Array){
18424             if(!keepExisting){
18425                 this.clearSelections(true);
18426             }
18427             for(var i = 0, len = nodeInfo.length; i < len; i++){
18428                 this.select(nodeInfo[i], true, true);
18429             }
18430             return;
18431         } 
18432         var node = this.getNode(nodeInfo);
18433         if(!node || this.isSelected(node)){
18434             return; // already selected.
18435         }
18436         if(!keepExisting){
18437             this.clearSelections(true);
18438         }
18439         
18440         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
18441             Roo.fly(node).addClass(this.selectedClass);
18442             this.selections.push(node);
18443             if(!suppressEvent){
18444                 this.fireEvent("selectionchange", this, this.selections);
18445             }
18446         }
18447         
18448         
18449     },
18450       /**
18451      * Unselects nodes.
18452      * @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
18453      * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
18454      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
18455      */
18456     unselect : function(nodeInfo, keepExisting, suppressEvent)
18457     {
18458         if(nodeInfo instanceof Array){
18459             Roo.each(this.selections, function(s) {
18460                 this.unselect(s, nodeInfo);
18461             }, this);
18462             return;
18463         }
18464         var node = this.getNode(nodeInfo);
18465         if(!node || !this.isSelected(node)){
18466             //Roo.log("not selected");
18467             return; // not selected.
18468         }
18469         // fireevent???
18470         var ns = [];
18471         Roo.each(this.selections, function(s) {
18472             if (s == node ) {
18473                 Roo.fly(node).removeClass(this.selectedClass);
18474
18475                 return;
18476             }
18477             ns.push(s);
18478         },this);
18479         
18480         this.selections= ns;
18481         this.fireEvent("selectionchange", this, this.selections);
18482     },
18483
18484     /**
18485      * Gets a template node.
18486      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
18487      * @return {HTMLElement} The node or null if it wasn't found
18488      */
18489     getNode : function(nodeInfo){
18490         if(typeof nodeInfo == "string"){
18491             return document.getElementById(nodeInfo);
18492         }else if(typeof nodeInfo == "number"){
18493             return this.nodes[nodeInfo];
18494         }
18495         return nodeInfo;
18496     },
18497
18498     /**
18499      * Gets a range template nodes.
18500      * @param {Number} startIndex
18501      * @param {Number} endIndex
18502      * @return {Array} An array of nodes
18503      */
18504     getNodes : function(start, end){
18505         var ns = this.nodes;
18506         start = start || 0;
18507         end = typeof end == "undefined" ? ns.length - 1 : end;
18508         var nodes = [];
18509         if(start <= end){
18510             for(var i = start; i <= end; i++){
18511                 nodes.push(ns[i]);
18512             }
18513         } else{
18514             for(var i = start; i >= end; i--){
18515                 nodes.push(ns[i]);
18516             }
18517         }
18518         return nodes;
18519     },
18520
18521     /**
18522      * Finds the index of the passed node
18523      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
18524      * @return {Number} The index of the node or -1
18525      */
18526     indexOf : function(node){
18527         node = this.getNode(node);
18528         if(typeof node.nodeIndex == "number"){
18529             return node.nodeIndex;
18530         }
18531         var ns = this.nodes;
18532         for(var i = 0, len = ns.length; i < len; i++){
18533             if(ns[i] == node){
18534                 return i;
18535             }
18536         }
18537         return -1;
18538     }
18539 });
18540 /*
18541  * - LGPL
18542  *
18543  * based on jquery fullcalendar
18544  * 
18545  */
18546
18547 Roo.bootstrap = Roo.bootstrap || {};
18548 /**
18549  * @class Roo.bootstrap.Calendar
18550  * @extends Roo.bootstrap.Component
18551  * Bootstrap Calendar class
18552  * @cfg {Boolean} loadMask (true|false) default false
18553  * @cfg {Object} header generate the user specific header of the calendar, default false
18554
18555  * @constructor
18556  * Create a new Container
18557  * @param {Object} config The config object
18558  */
18559
18560
18561
18562 Roo.bootstrap.Calendar = function(config){
18563     Roo.bootstrap.Calendar.superclass.constructor.call(this, config);
18564      this.addEvents({
18565         /**
18566              * @event select
18567              * Fires when a date is selected
18568              * @param {DatePicker} this
18569              * @param {Date} date The selected date
18570              */
18571         'select': true,
18572         /**
18573              * @event monthchange
18574              * Fires when the displayed month changes 
18575              * @param {DatePicker} this
18576              * @param {Date} date The selected month
18577              */
18578         'monthchange': true,
18579         /**
18580              * @event evententer
18581              * Fires when mouse over an event
18582              * @param {Calendar} this
18583              * @param {event} Event
18584              */
18585         'evententer': true,
18586         /**
18587              * @event eventleave
18588              * Fires when the mouse leaves an
18589              * @param {Calendar} this
18590              * @param {event}
18591              */
18592         'eventleave': true,
18593         /**
18594              * @event eventclick
18595              * Fires when the mouse click an
18596              * @param {Calendar} this
18597              * @param {event}
18598              */
18599         'eventclick': true
18600         
18601     });
18602
18603 };
18604
18605 Roo.extend(Roo.bootstrap.Calendar, Roo.bootstrap.Component,  {
18606     
18607      /**
18608      * @cfg {Number} startDay
18609      * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
18610      */
18611     startDay : 0,
18612     
18613     loadMask : false,
18614     
18615     header : false,
18616       
18617     getAutoCreate : function(){
18618         
18619         
18620         var fc_button = function(name, corner, style, content ) {
18621             return Roo.apply({},{
18622                 tag : 'span',
18623                 cls : 'fc-button fc-button-'+name+' fc-state-default ' + 
18624                          (corner.length ?
18625                             'fc-corner-' + corner.split(' ').join(' fc-corner-') :
18626                             ''
18627                         ),
18628                 html : '<SPAN class="fc-text-'+style+ '">'+content +'</SPAN>',
18629                 unselectable: 'on'
18630             });
18631         };
18632         
18633         var header = {};
18634         
18635         if(!this.header){
18636             header = {
18637                 tag : 'table',
18638                 cls : 'fc-header',
18639                 style : 'width:100%',
18640                 cn : [
18641                     {
18642                         tag: 'tr',
18643                         cn : [
18644                             {
18645                                 tag : 'td',
18646                                 cls : 'fc-header-left',
18647                                 cn : [
18648                                     fc_button('prev', 'left', 'arrow', '&#8249;' ),
18649                                     fc_button('next', 'right', 'arrow', '&#8250;' ),
18650                                     { tag: 'span', cls: 'fc-header-space' },
18651                                     fc_button('today', 'left right', '', 'today' )  // neds state disabled..
18652
18653
18654                                 ]
18655                             },
18656
18657                             {
18658                                 tag : 'td',
18659                                 cls : 'fc-header-center',
18660                                 cn : [
18661                                     {
18662                                         tag: 'span',
18663                                         cls: 'fc-header-title',
18664                                         cn : {
18665                                             tag: 'H2',
18666                                             html : 'month / year'
18667                                         }
18668                                     }
18669
18670                                 ]
18671                             },
18672                             {
18673                                 tag : 'td',
18674                                 cls : 'fc-header-right',
18675                                 cn : [
18676                               /*      fc_button('month', 'left', '', 'month' ),
18677                                     fc_button('week', '', '', 'week' ),
18678                                     fc_button('day', 'right', '', 'day' )
18679                                 */    
18680
18681                                 ]
18682                             }
18683
18684                         ]
18685                     }
18686                 ]
18687             };
18688         }
18689         
18690         header = this.header;
18691         
18692        
18693         var cal_heads = function() {
18694             var ret = [];
18695             // fixme - handle this.
18696             
18697             for (var i =0; i < Date.dayNames.length; i++) {
18698                 var d = Date.dayNames[i];
18699                 ret.push({
18700                     tag: 'th',
18701                     cls : 'fc-day-header fc-' + d.substring(0,3).toLowerCase() + ' fc-widget-header',
18702                     html : d.substring(0,3)
18703                 });
18704                 
18705             }
18706             ret[0].cls += ' fc-first';
18707             ret[6].cls += ' fc-last';
18708             return ret;
18709         };
18710         var cal_cell = function(n) {
18711             return  {
18712                 tag: 'td',
18713                 cls : 'fc-day fc-'+n + ' fc-widget-content', ///fc-other-month fc-past
18714                 cn : [
18715                     {
18716                         cn : [
18717                             {
18718                                 cls: 'fc-day-number',
18719                                 html: 'D'
18720                             },
18721                             {
18722                                 cls: 'fc-day-content',
18723                              
18724                                 cn : [
18725                                      {
18726                                         style: 'position: relative;' // height: 17px;
18727                                     }
18728                                 ]
18729                             }
18730                             
18731                             
18732                         ]
18733                     }
18734                 ]
18735                 
18736             }
18737         };
18738         var cal_rows = function() {
18739             
18740             var ret = [];
18741             for (var r = 0; r < 6; r++) {
18742                 var row= {
18743                     tag : 'tr',
18744                     cls : 'fc-week',
18745                     cn : []
18746                 };
18747                 
18748                 for (var i =0; i < Date.dayNames.length; i++) {
18749                     var d = Date.dayNames[i];
18750                     row.cn.push(cal_cell(d.substring(0,3).toLowerCase()));
18751
18752                 }
18753                 row.cn[0].cls+=' fc-first';
18754                 row.cn[0].cn[0].style = 'min-height:90px';
18755                 row.cn[6].cls+=' fc-last';
18756                 ret.push(row);
18757                 
18758             }
18759             ret[0].cls += ' fc-first';
18760             ret[4].cls += ' fc-prev-last';
18761             ret[5].cls += ' fc-last';
18762             return ret;
18763             
18764         };
18765         
18766         var cal_table = {
18767             tag: 'table',
18768             cls: 'fc-border-separate',
18769             style : 'width:100%',
18770             cellspacing  : 0,
18771             cn : [
18772                 { 
18773                     tag: 'thead',
18774                     cn : [
18775                         { 
18776                             tag: 'tr',
18777                             cls : 'fc-first fc-last',
18778                             cn : cal_heads()
18779                         }
18780                     ]
18781                 },
18782                 { 
18783                     tag: 'tbody',
18784                     cn : cal_rows()
18785                 }
18786                   
18787             ]
18788         };
18789          
18790          var cfg = {
18791             cls : 'fc fc-ltr',
18792             cn : [
18793                 header,
18794                 {
18795                     cls : 'fc-content',
18796                     style : "position: relative;",
18797                     cn : [
18798                         {
18799                             cls : 'fc-view fc-view-month fc-grid',
18800                             style : 'position: relative',
18801                             unselectable : 'on',
18802                             cn : [
18803                                 {
18804                                     cls : 'fc-event-container',
18805                                     style : 'position:absolute;z-index:8;top:0;left:0;'
18806                                 },
18807                                 cal_table
18808                             ]
18809                         }
18810                     ]
18811     
18812                 }
18813            ] 
18814             
18815         };
18816         
18817          
18818         
18819         return cfg;
18820     },
18821     
18822     
18823     initEvents : function()
18824     {
18825         if(!this.store){
18826             throw "can not find store for calendar";
18827         }
18828         
18829         var mark = {
18830             tag: "div",
18831             cls:"x-dlg-mask",
18832             style: "text-align:center",
18833             cn: [
18834                 {
18835                     tag: "div",
18836                     style: "background-color:white;width:50%;margin:250 auto",
18837                     cn: [
18838                         {
18839                             tag: "img",
18840                             src: Roo.rootURL + '/images/ux/lightbox/loading.gif' 
18841                         },
18842                         {
18843                             tag: "span",
18844                             html: "Loading"
18845                         }
18846                         
18847                     ]
18848                 }
18849             ]
18850         };
18851         this.maskEl = Roo.DomHelper.append(this.el.select('.fc-content', true).first(), mark, true);
18852         
18853         var size = this.el.select('.fc-content', true).first().getSize();
18854         this.maskEl.setSize(size.width, size.height);
18855         this.maskEl.enableDisplayMode("block");
18856         if(!this.loadMask){
18857             this.maskEl.hide();
18858         }
18859         
18860         this.store = Roo.factory(this.store, Roo.data);
18861         this.store.on('load', this.onLoad, this);
18862         this.store.on('beforeload', this.onBeforeLoad, this);
18863         
18864         this.resize();
18865         
18866         this.cells = this.el.select('.fc-day',true);
18867         //Roo.log(this.cells);
18868         this.textNodes = this.el.query('.fc-day-number');
18869         this.cells.addClassOnOver('fc-state-hover');
18870         
18871         this.el.select('.fc-button-prev',true).on('click', this.showPrevMonth, this);
18872         this.el.select('.fc-button-next',true).on('click', this.showNextMonth, this);
18873         this.el.select('.fc-button-today',true).on('click', this.showToday, this);
18874         this.el.select('.fc-button',true).addClassOnOver('fc-state-hover');
18875         
18876         this.on('monthchange', this.onMonthChange, this);
18877         
18878         this.update(new Date().clearTime());
18879     },
18880     
18881     resize : function() {
18882         var sz  = this.el.getSize();
18883         
18884         this.el.select('.fc-day-header',true).setWidth(sz.width / 7);
18885         this.el.select('.fc-day-content div',true).setHeight(34);
18886     },
18887     
18888     
18889     // private
18890     showPrevMonth : function(e){
18891         this.update(this.activeDate.add("mo", -1));
18892     },
18893     showToday : function(e){
18894         this.update(new Date().clearTime());
18895     },
18896     // private
18897     showNextMonth : function(e){
18898         this.update(this.activeDate.add("mo", 1));
18899     },
18900
18901     // private
18902     showPrevYear : function(){
18903         this.update(this.activeDate.add("y", -1));
18904     },
18905
18906     // private
18907     showNextYear : function(){
18908         this.update(this.activeDate.add("y", 1));
18909     },
18910
18911     
18912    // private
18913     update : function(date)
18914     {
18915         var vd = this.activeDate;
18916         this.activeDate = date;
18917 //        if(vd && this.el){
18918 //            var t = date.getTime();
18919 //            if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
18920 //                Roo.log('using add remove');
18921 //                
18922 //                this.fireEvent('monthchange', this, date);
18923 //                
18924 //                this.cells.removeClass("fc-state-highlight");
18925 //                this.cells.each(function(c){
18926 //                   if(c.dateValue == t){
18927 //                       c.addClass("fc-state-highlight");
18928 //                       setTimeout(function(){
18929 //                            try{c.dom.firstChild.focus();}catch(e){}
18930 //                       }, 50);
18931 //                       return false;
18932 //                   }
18933 //                   return true;
18934 //                });
18935 //                return;
18936 //            }
18937 //        }
18938         
18939         var days = date.getDaysInMonth();
18940         
18941         var firstOfMonth = date.getFirstDateOfMonth();
18942         var startingPos = firstOfMonth.getDay()-this.startDay;
18943         
18944         if(startingPos < this.startDay){
18945             startingPos += 7;
18946         }
18947         
18948         var pm = date.add(Date.MONTH, -1);
18949         var prevStart = pm.getDaysInMonth()-startingPos;
18950 //        
18951         this.cells = this.el.select('.fc-day',true);
18952         this.textNodes = this.el.query('.fc-day-number');
18953         this.cells.addClassOnOver('fc-state-hover');
18954         
18955         var cells = this.cells.elements;
18956         var textEls = this.textNodes;
18957         
18958         Roo.each(cells, function(cell){
18959             cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
18960         });
18961         
18962         days += startingPos;
18963
18964         // convert everything to numbers so it's fast
18965         var day = 86400000;
18966         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
18967         //Roo.log(d);
18968         //Roo.log(pm);
18969         //Roo.log(prevStart);
18970         
18971         var today = new Date().clearTime().getTime();
18972         var sel = date.clearTime().getTime();
18973         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
18974         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
18975         var ddMatch = this.disabledDatesRE;
18976         var ddText = this.disabledDatesText;
18977         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
18978         var ddaysText = this.disabledDaysText;
18979         var format = this.format;
18980         
18981         var setCellClass = function(cal, cell){
18982             cell.row = 0;
18983             cell.events = [];
18984             cell.more = [];
18985             //Roo.log('set Cell Class');
18986             cell.title = "";
18987             var t = d.getTime();
18988             
18989             //Roo.log(d);
18990             
18991             cell.dateValue = t;
18992             if(t == today){
18993                 cell.className += " fc-today";
18994                 cell.className += " fc-state-highlight";
18995                 cell.title = cal.todayText;
18996             }
18997             if(t == sel){
18998                 // disable highlight in other month..
18999                 //cell.className += " fc-state-highlight";
19000                 
19001             }
19002             // disabling
19003             if(t < min) {
19004                 cell.className = " fc-state-disabled";
19005                 cell.title = cal.minText;
19006                 return;
19007             }
19008             if(t > max) {
19009                 cell.className = " fc-state-disabled";
19010                 cell.title = cal.maxText;
19011                 return;
19012             }
19013             if(ddays){
19014                 if(ddays.indexOf(d.getDay()) != -1){
19015                     cell.title = ddaysText;
19016                     cell.className = " fc-state-disabled";
19017                 }
19018             }
19019             if(ddMatch && format){
19020                 var fvalue = d.dateFormat(format);
19021                 if(ddMatch.test(fvalue)){
19022                     cell.title = ddText.replace("%0", fvalue);
19023                     cell.className = " fc-state-disabled";
19024                 }
19025             }
19026             
19027             if (!cell.initialClassName) {
19028                 cell.initialClassName = cell.dom.className;
19029             }
19030             
19031             cell.dom.className = cell.initialClassName  + ' ' +  cell.className;
19032         };
19033
19034         var i = 0;
19035         
19036         for(; i < startingPos; i++) {
19037             textEls[i].innerHTML = (++prevStart);
19038             d.setDate(d.getDate()+1);
19039             
19040             cells[i].className = "fc-past fc-other-month";
19041             setCellClass(this, cells[i]);
19042         }
19043         
19044         var intDay = 0;
19045         
19046         for(; i < days; i++){
19047             intDay = i - startingPos + 1;
19048             textEls[i].innerHTML = (intDay);
19049             d.setDate(d.getDate()+1);
19050             
19051             cells[i].className = ''; // "x-date-active";
19052             setCellClass(this, cells[i]);
19053         }
19054         var extraDays = 0;
19055         
19056         for(; i < 42; i++) {
19057             textEls[i].innerHTML = (++extraDays);
19058             d.setDate(d.getDate()+1);
19059             
19060             cells[i].className = "fc-future fc-other-month";
19061             setCellClass(this, cells[i]);
19062         }
19063         
19064         this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
19065         
19066         var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
19067         
19068         this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
19069         this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
19070         
19071         if(totalRows != 6){
19072             this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
19073             this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
19074         }
19075         
19076         this.fireEvent('monthchange', this, date);
19077         
19078         
19079         /*
19080         if(!this.internalRender){
19081             var main = this.el.dom.firstChild;
19082             var w = main.offsetWidth;
19083             this.el.setWidth(w + this.el.getBorderWidth("lr"));
19084             Roo.fly(main).setWidth(w);
19085             this.internalRender = true;
19086             // opera does not respect the auto grow header center column
19087             // then, after it gets a width opera refuses to recalculate
19088             // without a second pass
19089             if(Roo.isOpera && !this.secondPass){
19090                 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
19091                 this.secondPass = true;
19092                 this.update.defer(10, this, [date]);
19093             }
19094         }
19095         */
19096         
19097     },
19098     
19099     findCell : function(dt) {
19100         dt = dt.clearTime().getTime();
19101         var ret = false;
19102         this.cells.each(function(c){
19103             //Roo.log("check " +c.dateValue + '?=' + dt);
19104             if(c.dateValue == dt){
19105                 ret = c;
19106                 return false;
19107             }
19108             return true;
19109         });
19110         
19111         return ret;
19112     },
19113     
19114     findCells : function(ev) {
19115         var s = ev.start.clone().clearTime().getTime();
19116        // Roo.log(s);
19117         var e= ev.end.clone().clearTime().getTime();
19118        // Roo.log(e);
19119         var ret = [];
19120         this.cells.each(function(c){
19121              ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
19122             
19123             if(c.dateValue > e){
19124                 return ;
19125             }
19126             if(c.dateValue < s){
19127                 return ;
19128             }
19129             ret.push(c);
19130         });
19131         
19132         return ret;    
19133     },
19134     
19135 //    findBestRow: function(cells)
19136 //    {
19137 //        var ret = 0;
19138 //        
19139 //        for (var i =0 ; i < cells.length;i++) {
19140 //            ret  = Math.max(cells[i].rows || 0,ret);
19141 //        }
19142 //        return ret;
19143 //        
19144 //    },
19145     
19146     
19147     addItem : function(ev)
19148     {
19149         // look for vertical location slot in
19150         var cells = this.findCells(ev);
19151         
19152 //        ev.row = this.findBestRow(cells);
19153         
19154         // work out the location.
19155         
19156         var crow = false;
19157         var rows = [];
19158         for(var i =0; i < cells.length; i++) {
19159             
19160             cells[i].row = cells[0].row;
19161             
19162             if(i == 0){
19163                 cells[i].row = cells[i].row + 1;
19164             }
19165             
19166             if (!crow) {
19167                 crow = {
19168                     start : cells[i],
19169                     end :  cells[i]
19170                 };
19171                 continue;
19172             }
19173             if (crow.start.getY() == cells[i].getY()) {
19174                 // on same row.
19175                 crow.end = cells[i];
19176                 continue;
19177             }
19178             // different row.
19179             rows.push(crow);
19180             crow = {
19181                 start: cells[i],
19182                 end : cells[i]
19183             };
19184             
19185         }
19186         
19187         rows.push(crow);
19188         ev.els = [];
19189         ev.rows = rows;
19190         ev.cells = cells;
19191         
19192         cells[0].events.push(ev);
19193         
19194         this.calevents.push(ev);
19195     },
19196     
19197     clearEvents: function() {
19198         
19199         if(!this.calevents){
19200             return;
19201         }
19202         
19203         Roo.each(this.cells.elements, function(c){
19204             c.row = 0;
19205             c.events = [];
19206             c.more = [];
19207         });
19208         
19209         Roo.each(this.calevents, function(e) {
19210             Roo.each(e.els, function(el) {
19211                 el.un('mouseenter' ,this.onEventEnter, this);
19212                 el.un('mouseleave' ,this.onEventLeave, this);
19213                 el.remove();
19214             },this);
19215         },this);
19216         
19217         Roo.each(Roo.select('.fc-more-event', true).elements, function(e){
19218             e.remove();
19219         });
19220         
19221     },
19222     
19223     renderEvents: function()
19224     {   
19225         var _this = this;
19226         
19227         this.cells.each(function(c) {
19228             
19229             if(c.row < 5){
19230                 return;
19231             }
19232             
19233             var ev = c.events;
19234             
19235             var r = 4;
19236             if(c.row != c.events.length){
19237                 r = 4 - (4 - (c.row - c.events.length));
19238             }
19239             
19240             c.events = ev.slice(0, r);
19241             c.more = ev.slice(r);
19242             
19243             if(c.more.length && c.more.length == 1){
19244                 c.events.push(c.more.pop());
19245             }
19246             
19247             c.row = (c.row - ev.length) + c.events.length + ((c.more.length) ? 1 : 0);
19248             
19249         });
19250             
19251         this.cells.each(function(c) {
19252             
19253             c.select('.fc-day-content div',true).first().setHeight(Math.max(34, c.row * 20));
19254             
19255             
19256             for (var e = 0; e < c.events.length; e++){
19257                 var ev = c.events[e];
19258                 var rows = ev.rows;
19259                 
19260                 for(var i = 0; i < rows.length; i++) {
19261                 
19262                     // how many rows should it span..
19263
19264                     var  cfg = {
19265                         cls : 'roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable',
19266                         style : 'position: absolute', // left: 387px; width: 121px; top: 359px;
19267
19268                         unselectable : "on",
19269                         cn : [
19270                             {
19271                                 cls: 'fc-event-inner',
19272                                 cn : [
19273     //                                {
19274     //                                  tag:'span',
19275     //                                  cls: 'fc-event-time',
19276     //                                  html : cells.length > 1 ? '' : ev.time
19277     //                                },
19278                                     {
19279                                       tag:'span',
19280                                       cls: 'fc-event-title',
19281                                       html : String.format('{0}', ev.title)
19282                                     }
19283
19284
19285                                 ]
19286                             },
19287                             {
19288                                 cls: 'ui-resizable-handle ui-resizable-e',
19289                                 html : '&nbsp;&nbsp;&nbsp'
19290                             }
19291
19292                         ]
19293                     };
19294
19295                     if (i == 0) {
19296                         cfg.cls += ' fc-event-start';
19297                     }
19298                     if ((i+1) == rows.length) {
19299                         cfg.cls += ' fc-event-end';
19300                     }
19301
19302                     var ctr = _this.el.select('.fc-event-container',true).first();
19303                     var cg = ctr.createChild(cfg);
19304
19305                     var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
19306                     var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
19307
19308                     var r = (c.more.length) ? 1 : 0;
19309                     cg.setXY([sbox.x +2, sbox.y + ((c.row - c.events.length - r + e) * 20)]);    
19310                     cg.setWidth(ebox.right - sbox.x -2);
19311
19312                     cg.on('mouseenter' ,_this.onEventEnter, _this, ev);
19313                     cg.on('mouseleave' ,_this.onEventLeave, _this, ev);
19314                     cg.on('click', _this.onEventClick, _this, ev);
19315
19316                     ev.els.push(cg);
19317                     
19318                 }
19319                 
19320             }
19321             
19322             
19323             if(c.more.length){
19324                 var  cfg = {
19325                     cls : 'fc-more-event roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable fc-event-start fc-event-end',
19326                     style : 'position: absolute',
19327                     unselectable : "on",
19328                     cn : [
19329                         {
19330                             cls: 'fc-event-inner',
19331                             cn : [
19332                                 {
19333                                   tag:'span',
19334                                   cls: 'fc-event-title',
19335                                   html : 'More'
19336                                 }
19337
19338
19339                             ]
19340                         },
19341                         {
19342                             cls: 'ui-resizable-handle ui-resizable-e',
19343                             html : '&nbsp;&nbsp;&nbsp'
19344                         }
19345
19346                     ]
19347                 };
19348
19349                 var ctr = _this.el.select('.fc-event-container',true).first();
19350                 var cg = ctr.createChild(cfg);
19351
19352                 var sbox = c.select('.fc-day-content',true).first().getBox();
19353                 var ebox = c.select('.fc-day-content',true).first().getBox();
19354                 //Roo.log(cg);
19355                 cg.setXY([sbox.x +2, sbox.y +((c.row - 1) * 20)]);    
19356                 cg.setWidth(ebox.right - sbox.x -2);
19357
19358                 cg.on('click', _this.onMoreEventClick, _this, c.more);
19359                 
19360             }
19361             
19362         });
19363         
19364         
19365         
19366     },
19367     
19368     onEventEnter: function (e, el,event,d) {
19369         this.fireEvent('evententer', this, el, event);
19370     },
19371     
19372     onEventLeave: function (e, el,event,d) {
19373         this.fireEvent('eventleave', this, el, event);
19374     },
19375     
19376     onEventClick: function (e, el,event,d) {
19377         this.fireEvent('eventclick', this, el, event);
19378     },
19379     
19380     onMonthChange: function () {
19381         this.store.load();
19382     },
19383     
19384     onMoreEventClick: function(e, el, more)
19385     {
19386         var _this = this;
19387         
19388         this.calpopover.placement = 'right';
19389         this.calpopover.setTitle('More');
19390         
19391         this.calpopover.setContent('');
19392         
19393         var ctr = this.calpopover.el.select('.popover-content', true).first();
19394         
19395         Roo.each(more, function(m){
19396             var cfg = {
19397                 cls : 'fc-event-hori fc-event-draggable',
19398                 html : m.title
19399             };
19400             var cg = ctr.createChild(cfg);
19401             
19402             cg.on('click', _this.onEventClick, _this, m);
19403         });
19404         
19405         this.calpopover.show(el);
19406         
19407         
19408     },
19409     
19410     onLoad: function () 
19411     {   
19412         this.calevents = [];
19413         var cal = this;
19414         
19415         if(this.store.getCount() > 0){
19416             this.store.data.each(function(d){
19417                cal.addItem({
19418                     id : d.data.id,
19419                     start: (typeof(d.data.start_dt) === 'string') ? new Date.parseDate(d.data.start_dt, 'Y-m-d H:i:s') : d.data.start_dt,
19420                     end : (typeof(d.data.end_dt) === 'string') ? new Date.parseDate(d.data.end_dt, 'Y-m-d H:i:s') : d.data.end_dt,
19421                     time : d.data.start_time,
19422                     title : d.data.title,
19423                     description : d.data.description,
19424                     venue : d.data.venue
19425                 });
19426             });
19427         }
19428         
19429         this.renderEvents();
19430         
19431         if(this.calevents.length && this.loadMask){
19432             this.maskEl.hide();
19433         }
19434     },
19435     
19436     onBeforeLoad: function()
19437     {
19438         this.clearEvents();
19439         if(this.loadMask){
19440             this.maskEl.show();
19441         }
19442     }
19443 });
19444
19445  
19446  /*
19447  * - LGPL
19448  *
19449  * element
19450  * 
19451  */
19452
19453 /**
19454  * @class Roo.bootstrap.Popover
19455  * @extends Roo.bootstrap.Component
19456  * Bootstrap Popover class
19457  * @cfg {String} html contents of the popover   (or false to use children..)
19458  * @cfg {String} title of popover (or false to hide)
19459  * @cfg {String} placement how it is placed
19460  * @cfg {String} trigger click || hover (or false to trigger manually)
19461  * @cfg {String} over what (parent or false to trigger manually.)
19462  * @cfg {Number} delay - delay before showing
19463  
19464  * @constructor
19465  * Create a new Popover
19466  * @param {Object} config The config object
19467  */
19468
19469 Roo.bootstrap.Popover = function(config){
19470     Roo.bootstrap.Popover.superclass.constructor.call(this, config);
19471     
19472     this.addEvents({
19473         // raw events
19474          /**
19475          * @event show
19476          * After the popover show
19477          * 
19478          * @param {Roo.bootstrap.Popover} this
19479          */
19480         "show" : true,
19481         /**
19482          * @event hide
19483          * After the popover hide
19484          * 
19485          * @param {Roo.bootstrap.Popover} this
19486          */
19487         "hide" : true
19488     });
19489 };
19490
19491 Roo.extend(Roo.bootstrap.Popover, Roo.bootstrap.Component,  {
19492     
19493     title: 'Fill in a title',
19494     html: false,
19495     
19496     placement : 'right',
19497     trigger : 'hover', // hover
19498     
19499     delay : 0,
19500     
19501     over: 'parent',
19502     
19503     can_build_overlaid : false,
19504     
19505     getChildContainer : function()
19506     {
19507         return this.el.select('.popover-content',true).first();
19508     },
19509     
19510     getAutoCreate : function(){
19511          
19512         var cfg = {
19513            cls : 'popover roo-dynamic',
19514            style: 'display:block',
19515            cn : [
19516                 {
19517                     cls : 'arrow'
19518                 },
19519                 {
19520                     cls : 'popover-inner',
19521                     cn : [
19522                         {
19523                             tag: 'h3',
19524                             cls: 'popover-title popover-header',
19525                             html : this.title
19526                         },
19527                         {
19528                             cls : 'popover-content popover-body',
19529                             html : this.html
19530                         }
19531                     ]
19532                     
19533                 }
19534            ]
19535         };
19536         
19537         return cfg;
19538     },
19539     setTitle: function(str)
19540     {
19541         this.title = str;
19542         this.el.select('.popover-title',true).first().dom.innerHTML = str;
19543     },
19544     setContent: function(str)
19545     {
19546         this.html = str;
19547         this.el.select('.popover-content',true).first().dom.innerHTML = str;
19548     },
19549     // as it get's added to the bottom of the page.
19550     onRender : function(ct, position)
19551     {
19552         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
19553         if(!this.el){
19554             var cfg = Roo.apply({},  this.getAutoCreate());
19555             cfg.id = Roo.id();
19556             
19557             if (this.cls) {
19558                 cfg.cls += ' ' + this.cls;
19559             }
19560             if (this.style) {
19561                 cfg.style = this.style;
19562             }
19563             //Roo.log("adding to ");
19564             this.el = Roo.get(document.body).createChild(cfg, position);
19565 //            Roo.log(this.el);
19566         }
19567         this.initEvents();
19568     },
19569     
19570     initEvents : function()
19571     {
19572         this.el.select('.popover-title',true).setVisibilityMode(Roo.Element.DISPLAY);
19573         this.el.enableDisplayMode('block');
19574         this.el.hide();
19575         if (this.over === false) {
19576             return; 
19577         }
19578         if (this.triggers === false) {
19579             return;
19580         }
19581         var on_el = (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
19582         var triggers = this.trigger ? this.trigger.split(' ') : [];
19583         Roo.each(triggers, function(trigger) {
19584         
19585             if (trigger == 'click') {
19586                 on_el.on('click', this.toggle, this);
19587             } else if (trigger != 'manual') {
19588                 var eventIn  = trigger == 'hover' ? 'mouseenter' : 'focusin';
19589                 var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout';
19590       
19591                 on_el.on(eventIn  ,this.enter, this);
19592                 on_el.on(eventOut, this.leave, this);
19593             }
19594         }, this);
19595         
19596     },
19597     
19598     
19599     // private
19600     timeout : null,
19601     hoverState : null,
19602     
19603     toggle : function () {
19604         this.hoverState == 'in' ? this.leave() : this.enter();
19605     },
19606     
19607     enter : function () {
19608         
19609         clearTimeout(this.timeout);
19610     
19611         this.hoverState = 'in';
19612     
19613         if (!this.delay || !this.delay.show) {
19614             this.show();
19615             return;
19616         }
19617         var _t = this;
19618         this.timeout = setTimeout(function () {
19619             if (_t.hoverState == 'in') {
19620                 _t.show();
19621             }
19622         }, this.delay.show)
19623     },
19624     
19625     leave : function() {
19626         clearTimeout(this.timeout);
19627     
19628         this.hoverState = 'out';
19629     
19630         if (!this.delay || !this.delay.hide) {
19631             this.hide();
19632             return;
19633         }
19634         var _t = this;
19635         this.timeout = setTimeout(function () {
19636             if (_t.hoverState == 'out') {
19637                 _t.hide();
19638             }
19639         }, this.delay.hide)
19640     },
19641     
19642     show : function (on_el)
19643     {
19644         if (!on_el) {
19645             on_el= (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
19646         }
19647         
19648         // set content.
19649         this.el.select('.popover-title',true).first().dom.innerHtml = this.title;
19650         if (this.html !== false) {
19651             this.el.select('.popover-content',true).first().dom.innerHtml = this.html;
19652         }
19653         this.el.removeClass([
19654             'fade','top','bottom', 'left', 'right','in',
19655             'bs-popover-top','bs-popover-bottom', 'bs-popover-left', 'bs-popover-right'
19656         ]);
19657         if (!this.title.length) {
19658             this.el.select('.popover-title',true).hide();
19659         }
19660         
19661         var placement = typeof this.placement == 'function' ?
19662             this.placement.call(this, this.el, on_el) :
19663             this.placement;
19664             
19665         var autoToken = /\s?auto?\s?/i;
19666         var autoPlace = autoToken.test(placement);
19667         if (autoPlace) {
19668             placement = placement.replace(autoToken, '') || 'top';
19669         }
19670         
19671         //this.el.detach()
19672         //this.el.setXY([0,0]);
19673         this.el.show();
19674         this.el.dom.style.display='block';
19675         this.el.addClass(placement);
19676         
19677         //this.el.appendTo(on_el);
19678         
19679         var p = this.getPosition();
19680         var box = this.el.getBox();
19681         
19682         if (autoPlace) {
19683             // fixme..
19684         }
19685         var align = Roo.bootstrap.Popover.alignment[placement];
19686         
19687 //        Roo.log(align);
19688         this.el.alignTo(on_el, align[0],align[1]);
19689         //var arrow = this.el.select('.arrow',true).first();
19690         //arrow.set(align[2], 
19691         
19692         this.el.addClass('in');
19693         
19694         
19695         if (this.el.hasClass('fade')) {
19696             // fade it?
19697         }
19698         
19699         this.hoverState = 'in';
19700         
19701         this.fireEvent('show', this);
19702         
19703     },
19704     hide : function()
19705     {
19706         this.el.setXY([0,0]);
19707         this.el.removeClass('in');
19708         this.el.hide();
19709         this.hoverState = null;
19710         
19711         this.fireEvent('hide', this);
19712     }
19713     
19714 });
19715
19716 Roo.bootstrap.Popover.alignment = {
19717     'left' : ['r-l', [-10,0], 'right bs-popover-right'],
19718     'right' : ['l-r', [10,0], 'left bs-popover-left'],
19719     'bottom' : ['t-b', [0,10], 'top bs-popover-top'],
19720     'top' : [ 'b-t', [0,-10], 'bottom bs-popover-bottom']
19721 };
19722
19723  /*
19724  * - LGPL
19725  *
19726  * Progress
19727  * 
19728  */
19729
19730 /**
19731  * @class Roo.bootstrap.Progress
19732  * @extends Roo.bootstrap.Component
19733  * Bootstrap Progress class
19734  * @cfg {Boolean} striped striped of the progress bar
19735  * @cfg {Boolean} active animated of the progress bar
19736  * 
19737  * 
19738  * @constructor
19739  * Create a new Progress
19740  * @param {Object} config The config object
19741  */
19742
19743 Roo.bootstrap.Progress = function(config){
19744     Roo.bootstrap.Progress.superclass.constructor.call(this, config);
19745 };
19746
19747 Roo.extend(Roo.bootstrap.Progress, Roo.bootstrap.Component,  {
19748     
19749     striped : false,
19750     active: false,
19751     
19752     getAutoCreate : function(){
19753         var cfg = {
19754             tag: 'div',
19755             cls: 'progress'
19756         };
19757         
19758         
19759         if(this.striped){
19760             cfg.cls += ' progress-striped';
19761         }
19762       
19763         if(this.active){
19764             cfg.cls += ' active';
19765         }
19766         
19767         
19768         return cfg;
19769     }
19770    
19771 });
19772
19773  
19774
19775  /*
19776  * - LGPL
19777  *
19778  * ProgressBar
19779  * 
19780  */
19781
19782 /**
19783  * @class Roo.bootstrap.ProgressBar
19784  * @extends Roo.bootstrap.Component
19785  * Bootstrap ProgressBar class
19786  * @cfg {Number} aria_valuenow aria-value now
19787  * @cfg {Number} aria_valuemin aria-value min
19788  * @cfg {Number} aria_valuemax aria-value max
19789  * @cfg {String} label label for the progress bar
19790  * @cfg {String} panel (success | info | warning | danger )
19791  * @cfg {String} role role of the progress bar
19792  * @cfg {String} sr_only text
19793  * 
19794  * 
19795  * @constructor
19796  * Create a new ProgressBar
19797  * @param {Object} config The config object
19798  */
19799
19800 Roo.bootstrap.ProgressBar = function(config){
19801     Roo.bootstrap.ProgressBar.superclass.constructor.call(this, config);
19802 };
19803
19804 Roo.extend(Roo.bootstrap.ProgressBar, Roo.bootstrap.Component,  {
19805     
19806     aria_valuenow : 0,
19807     aria_valuemin : 0,
19808     aria_valuemax : 100,
19809     label : false,
19810     panel : false,
19811     role : false,
19812     sr_only: false,
19813     
19814     getAutoCreate : function()
19815     {
19816         
19817         var cfg = {
19818             tag: 'div',
19819             cls: 'progress-bar',
19820             style: 'width:' + Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%'
19821         };
19822         
19823         if(this.sr_only){
19824             cfg.cn = {
19825                 tag: 'span',
19826                 cls: 'sr-only',
19827                 html: this.sr_only
19828             }
19829         }
19830         
19831         if(this.role){
19832             cfg.role = this.role;
19833         }
19834         
19835         if(this.aria_valuenow){
19836             cfg['aria-valuenow'] = this.aria_valuenow;
19837         }
19838         
19839         if(this.aria_valuemin){
19840             cfg['aria-valuemin'] = this.aria_valuemin;
19841         }
19842         
19843         if(this.aria_valuemax){
19844             cfg['aria-valuemax'] = this.aria_valuemax;
19845         }
19846         
19847         if(this.label && !this.sr_only){
19848             cfg.html = this.label;
19849         }
19850         
19851         if(this.panel){
19852             cfg.cls += ' progress-bar-' + this.panel;
19853         }
19854         
19855         return cfg;
19856     },
19857     
19858     update : function(aria_valuenow)
19859     {
19860         this.aria_valuenow = aria_valuenow;
19861         
19862         this.el.setStyle('width', Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%');
19863     }
19864    
19865 });
19866
19867  
19868
19869  /*
19870  * - LGPL
19871  *
19872  * column
19873  * 
19874  */
19875
19876 /**
19877  * @class Roo.bootstrap.TabGroup
19878  * @extends Roo.bootstrap.Column
19879  * Bootstrap Column class
19880  * @cfg {String} navId the navigation id (for use with navbars) - will be auto generated if it does not exist..
19881  * @cfg {Boolean} carousel true to make the group behave like a carousel
19882  * @cfg {Boolean} bullets show bullets for the panels
19883  * @cfg {Boolean} autoslide (true|false) auto slide .. default false
19884  * @cfg {Number} timer auto slide timer .. default 0 millisecond
19885  * @cfg {Boolean} showarrow (true|false) show arrow default true
19886  * 
19887  * @constructor
19888  * Create a new TabGroup
19889  * @param {Object} config The config object
19890  */
19891
19892 Roo.bootstrap.TabGroup = function(config){
19893     Roo.bootstrap.TabGroup.superclass.constructor.call(this, config);
19894     if (!this.navId) {
19895         this.navId = Roo.id();
19896     }
19897     this.tabs = [];
19898     Roo.bootstrap.TabGroup.register(this);
19899     
19900 };
19901
19902 Roo.extend(Roo.bootstrap.TabGroup, Roo.bootstrap.Column,  {
19903     
19904     carousel : false,
19905     transition : false,
19906     bullets : 0,
19907     timer : 0,
19908     autoslide : false,
19909     slideFn : false,
19910     slideOnTouch : false,
19911     showarrow : true,
19912     
19913     getAutoCreate : function()
19914     {
19915         var cfg = Roo.apply({}, Roo.bootstrap.TabGroup.superclass.getAutoCreate.call(this));
19916         
19917         cfg.cls += ' tab-content';
19918         
19919         if (this.carousel) {
19920             cfg.cls += ' carousel slide';
19921             
19922             cfg.cn = [{
19923                cls : 'carousel-inner',
19924                cn : []
19925             }];
19926         
19927             if(this.bullets  && !Roo.isTouch){
19928                 
19929                 var bullets = {
19930                     cls : 'carousel-bullets',
19931                     cn : []
19932                 };
19933                
19934                 if(this.bullets_cls){
19935                     bullets.cls = bullets.cls + ' ' + this.bullets_cls;
19936                 }
19937                 
19938                 bullets.cn.push({
19939                     cls : 'clear'
19940                 });
19941                 
19942                 cfg.cn[0].cn.push(bullets);
19943             }
19944             
19945             if(this.showarrow){
19946                 cfg.cn[0].cn.push({
19947                     tag : 'div',
19948                     class : 'carousel-arrow',
19949                     cn : [
19950                         {
19951                             tag : 'div',
19952                             class : 'carousel-prev',
19953                             cn : [
19954                                 {
19955                                     tag : 'i',
19956                                     class : 'fa fa-chevron-left'
19957                                 }
19958                             ]
19959                         },
19960                         {
19961                             tag : 'div',
19962                             class : 'carousel-next',
19963                             cn : [
19964                                 {
19965                                     tag : 'i',
19966                                     class : 'fa fa-chevron-right'
19967                                 }
19968                             ]
19969                         }
19970                     ]
19971                 });
19972             }
19973             
19974         }
19975         
19976         return cfg;
19977     },
19978     
19979     initEvents:  function()
19980     {
19981 //        if(Roo.isTouch && this.slideOnTouch && !this.showarrow){
19982 //            this.el.on("touchstart", this.onTouchStart, this);
19983 //        }
19984         
19985         if(this.autoslide){
19986             var _this = this;
19987             
19988             this.slideFn = window.setInterval(function() {
19989                 _this.showPanelNext();
19990             }, this.timer);
19991         }
19992         
19993         if(this.showarrow){
19994             this.el.select('.carousel-prev', true).first().on('click', this.showPanelPrev, this);
19995             this.el.select('.carousel-next', true).first().on('click', this.showPanelNext, this);
19996         }
19997         
19998         
19999     },
20000     
20001 //    onTouchStart : function(e, el, o)
20002 //    {
20003 //        if(!this.slideOnTouch || !Roo.isTouch || Roo.get(e.getTarget()).hasClass('roo-button-text')){
20004 //            return;
20005 //        }
20006 //        
20007 //        this.showPanelNext();
20008 //    },
20009     
20010     
20011     getChildContainer : function()
20012     {
20013         return this.carousel ? this.el.select('.carousel-inner', true).first() : this.el;
20014     },
20015     
20016     /**
20017     * register a Navigation item
20018     * @param {Roo.bootstrap.NavItem} the navitem to add
20019     */
20020     register : function(item)
20021     {
20022         this.tabs.push( item);
20023         item.navId = this.navId; // not really needed..
20024         this.addBullet();
20025     
20026     },
20027     
20028     getActivePanel : function()
20029     {
20030         var r = false;
20031         Roo.each(this.tabs, function(t) {
20032             if (t.active) {
20033                 r = t;
20034                 return false;
20035             }
20036             return null;
20037         });
20038         return r;
20039         
20040     },
20041     getPanelByName : function(n)
20042     {
20043         var r = false;
20044         Roo.each(this.tabs, function(t) {
20045             if (t.tabId == n) {
20046                 r = t;
20047                 return false;
20048             }
20049             return null;
20050         });
20051         return r;
20052     },
20053     indexOfPanel : function(p)
20054     {
20055         var r = false;
20056         Roo.each(this.tabs, function(t,i) {
20057             if (t.tabId == p.tabId) {
20058                 r = i;
20059                 return false;
20060             }
20061             return null;
20062         });
20063         return r;
20064     },
20065     /**
20066      * show a specific panel
20067      * @param {Roo.bootstrap.TabPanel|number|string} panel to change to (use the tabId to specify a specific one)
20068      * @return {boolean} false if panel was not shown (invalid entry or beforedeactivate fails.)
20069      */
20070     showPanel : function (pan)
20071     {
20072         if(this.transition || typeof(pan) == 'undefined'){
20073             Roo.log("waiting for the transitionend");
20074             return false;
20075         }
20076         
20077         if (typeof(pan) == 'number') {
20078             pan = this.tabs[pan];
20079         }
20080         
20081         if (typeof(pan) == 'string') {
20082             pan = this.getPanelByName(pan);
20083         }
20084         
20085         var cur = this.getActivePanel();
20086         
20087         if(!pan || !cur){
20088             Roo.log('pan or acitve pan is undefined');
20089             return false;
20090         }
20091         
20092         if (pan.tabId == this.getActivePanel().tabId) {
20093             return true;
20094         }
20095         
20096         if (false === cur.fireEvent('beforedeactivate')) {
20097             return false;
20098         }
20099         
20100         if(this.bullets > 0 && !Roo.isTouch){
20101             this.setActiveBullet(this.indexOfPanel(pan));
20102         }
20103         
20104         if (this.carousel && typeof(Roo.get(document.body).dom.style.transition) != 'undefined') {
20105             
20106             //class="carousel-item carousel-item-next carousel-item-left"
20107             
20108             this.transition = true;
20109             var dir = this.indexOfPanel(pan) > this.indexOfPanel(cur)  ? 'next' : 'prev';
20110             var lr = dir == 'next' ? 'left' : 'right';
20111             pan.el.addClass(dir); // or prev
20112             pan.el.addClass('carousel-item-' + dir); // or prev
20113             pan.el.dom.offsetWidth; // find the offset with - causing a reflow?
20114             cur.el.addClass(lr); // or right
20115             pan.el.addClass(lr);
20116             cur.el.addClass('carousel-item-' +lr); // or right
20117             pan.el.addClass('carousel-item-' +lr);
20118             
20119             
20120             var _this = this;
20121             cur.el.on('transitionend', function() {
20122                 Roo.log("trans end?");
20123                 
20124                 pan.el.removeClass([lr,dir, 'carousel-item-' + lr, 'carousel-item-' + dir]);
20125                 pan.setActive(true);
20126                 
20127                 cur.el.removeClass([lr, 'carousel-item-' + lr]);
20128                 cur.setActive(false);
20129                 
20130                 _this.transition = false;
20131                 
20132             }, this, { single:  true } );
20133             
20134             return true;
20135         }
20136         
20137         cur.setActive(false);
20138         pan.setActive(true);
20139         
20140         return true;
20141         
20142     },
20143     showPanelNext : function()
20144     {
20145         var i = this.indexOfPanel(this.getActivePanel());
20146         
20147         if (i >= this.tabs.length - 1 && !this.autoslide) {
20148             return;
20149         }
20150         
20151         if (i >= this.tabs.length - 1 && this.autoslide) {
20152             i = -1;
20153         }
20154         
20155         this.showPanel(this.tabs[i+1]);
20156     },
20157     
20158     showPanelPrev : function()
20159     {
20160         var i = this.indexOfPanel(this.getActivePanel());
20161         
20162         if (i  < 1 && !this.autoslide) {
20163             return;
20164         }
20165         
20166         if (i < 1 && this.autoslide) {
20167             i = this.tabs.length;
20168         }
20169         
20170         this.showPanel(this.tabs[i-1]);
20171     },
20172     
20173     
20174     addBullet: function()
20175     {
20176         if(!this.bullets || Roo.isTouch){
20177             return;
20178         }
20179         var ctr = this.el.select('.carousel-bullets',true).first();
20180         var i = this.el.select('.carousel-bullets .bullet',true).getCount() ;
20181         var bullet = ctr.createChild({
20182             cls : 'bullet bullet-' + i
20183         },ctr.dom.lastChild);
20184         
20185         
20186         var _this = this;
20187         
20188         bullet.on('click', (function(e, el, o, ii, t){
20189
20190             e.preventDefault();
20191
20192             this.showPanel(ii);
20193
20194             if(this.autoslide && this.slideFn){
20195                 clearInterval(this.slideFn);
20196                 this.slideFn = window.setInterval(function() {
20197                     _this.showPanelNext();
20198                 }, this.timer);
20199             }
20200
20201         }).createDelegate(this, [i, bullet], true));
20202                 
20203         
20204     },
20205      
20206     setActiveBullet : function(i)
20207     {
20208         if(Roo.isTouch){
20209             return;
20210         }
20211         
20212         Roo.each(this.el.select('.bullet', true).elements, function(el){
20213             el.removeClass('selected');
20214         });
20215
20216         var bullet = this.el.select('.bullet-' + i, true).first();
20217         
20218         if(!bullet){
20219             return;
20220         }
20221         
20222         bullet.addClass('selected');
20223     }
20224     
20225     
20226   
20227 });
20228
20229  
20230
20231  
20232  
20233 Roo.apply(Roo.bootstrap.TabGroup, {
20234     
20235     groups: {},
20236      /**
20237     * register a Navigation Group
20238     * @param {Roo.bootstrap.NavGroup} the navgroup to add
20239     */
20240     register : function(navgrp)
20241     {
20242         this.groups[navgrp.navId] = navgrp;
20243         
20244     },
20245     /**
20246     * fetch a Navigation Group based on the navigation ID
20247     * if one does not exist , it will get created.
20248     * @param {string} the navgroup to add
20249     * @returns {Roo.bootstrap.NavGroup} the navgroup 
20250     */
20251     get: function(navId) {
20252         if (typeof(this.groups[navId]) == 'undefined') {
20253             this.register(new Roo.bootstrap.TabGroup({ navId : navId }));
20254         }
20255         return this.groups[navId] ;
20256     }
20257     
20258     
20259     
20260 });
20261
20262  /*
20263  * - LGPL
20264  *
20265  * TabPanel
20266  * 
20267  */
20268
20269 /**
20270  * @class Roo.bootstrap.TabPanel
20271  * @extends Roo.bootstrap.Component
20272  * Bootstrap TabPanel class
20273  * @cfg {Boolean} active panel active
20274  * @cfg {String} html panel content
20275  * @cfg {String} tabId  unique tab ID (will be autogenerated if not set. - used to match TabItem to Panel)
20276  * @cfg {String} navId The Roo.bootstrap.NavGroup which triggers show hide ()
20277  * @cfg {String} href click to link..
20278  * @cfg {Boolean} touchSlide if swiping slides tab to next panel (default off)
20279  * 
20280  * 
20281  * @constructor
20282  * Create a new TabPanel
20283  * @param {Object} config The config object
20284  */
20285
20286 Roo.bootstrap.TabPanel = function(config){
20287     Roo.bootstrap.TabPanel.superclass.constructor.call(this, config);
20288     this.addEvents({
20289         /**
20290              * @event changed
20291              * Fires when the active status changes
20292              * @param {Roo.bootstrap.TabPanel} this
20293              * @param {Boolean} state the new state
20294             
20295          */
20296         'changed': true,
20297         /**
20298              * @event beforedeactivate
20299              * Fires before a tab is de-activated - can be used to do validation on a form.
20300              * @param {Roo.bootstrap.TabPanel} this
20301              * @return {Boolean} false if there is an error
20302             
20303          */
20304         'beforedeactivate': true
20305      });
20306     
20307     this.tabId = this.tabId || Roo.id();
20308   
20309 };
20310
20311 Roo.extend(Roo.bootstrap.TabPanel, Roo.bootstrap.Component,  {
20312     
20313     active: false,
20314     html: false,
20315     tabId: false,
20316     navId : false,
20317     href : '',
20318     touchSlide : false,
20319     getAutoCreate : function(){
20320         
20321         
20322         var cfg = {
20323             tag: 'div',
20324             // item is needed for carousel - not sure if it has any effect otherwise
20325             cls: 'carousel-item tab-pane item' + ((this.href.length) ? ' clickable ' : ''),
20326             html: this.html || ''
20327         };
20328         
20329         if(this.active){
20330             cfg.cls += ' active';
20331         }
20332         
20333         if(this.tabId){
20334             cfg.tabId = this.tabId;
20335         }
20336         
20337         
20338         
20339         return cfg;
20340     },
20341     
20342     initEvents:  function()
20343     {
20344         var p = this.parent();
20345         
20346         this.navId = this.navId || p.navId;
20347         
20348         if (typeof(this.navId) != 'undefined') {
20349             // not really needed.. but just in case.. parent should be a NavGroup.
20350             var tg = Roo.bootstrap.TabGroup.get(this.navId);
20351             
20352             tg.register(this);
20353             
20354             var i = tg.tabs.length - 1;
20355             
20356             if(this.active && tg.bullets > 0 && i < tg.bullets){
20357                 tg.setActiveBullet(i);
20358             }
20359         }
20360         
20361         this.el.on('click', this.onClick, this);
20362         
20363         if(Roo.isTouch && this.touchSlide){
20364             this.el.on("touchstart", this.onTouchStart, this);
20365             this.el.on("touchmove", this.onTouchMove, this);
20366             this.el.on("touchend", this.onTouchEnd, this);
20367         }
20368         
20369     },
20370     
20371     onRender : function(ct, position)
20372     {
20373         Roo.bootstrap.TabPanel.superclass.onRender.call(this, ct, position);
20374     },
20375     
20376     setActive : function(state)
20377     {
20378         Roo.log("panel - set active " + this.tabId + "=" + state);
20379         
20380         this.active = state;
20381         if (!state) {
20382             this.el.removeClass('active');
20383             
20384         } else  if (!this.el.hasClass('active')) {
20385             this.el.addClass('active');
20386         }
20387         
20388         this.fireEvent('changed', this, state);
20389     },
20390     
20391     onClick : function(e)
20392     {
20393         e.preventDefault();
20394         
20395         if(!this.href.length){
20396             return;
20397         }
20398         
20399         window.location.href = this.href;
20400     },
20401     
20402     startX : 0,
20403     startY : 0,
20404     endX : 0,
20405     endY : 0,
20406     swiping : false,
20407     
20408     onTouchStart : function(e)
20409     {
20410         this.swiping = false;
20411         
20412         this.startX = e.browserEvent.touches[0].clientX;
20413         this.startY = e.browserEvent.touches[0].clientY;
20414     },
20415     
20416     onTouchMove : function(e)
20417     {
20418         this.swiping = true;
20419         
20420         this.endX = e.browserEvent.touches[0].clientX;
20421         this.endY = e.browserEvent.touches[0].clientY;
20422     },
20423     
20424     onTouchEnd : function(e)
20425     {
20426         if(!this.swiping){
20427             this.onClick(e);
20428             return;
20429         }
20430         
20431         var tabGroup = this.parent();
20432         
20433         if(this.endX > this.startX){ // swiping right
20434             tabGroup.showPanelPrev();
20435             return;
20436         }
20437         
20438         if(this.startX > this.endX){ // swiping left
20439             tabGroup.showPanelNext();
20440             return;
20441         }
20442     }
20443     
20444     
20445 });
20446  
20447
20448  
20449
20450  /*
20451  * - LGPL
20452  *
20453  * DateField
20454  * 
20455  */
20456
20457 /**
20458  * @class Roo.bootstrap.DateField
20459  * @extends Roo.bootstrap.Input
20460  * Bootstrap DateField class
20461  * @cfg {Number} weekStart default 0
20462  * @cfg {String} viewMode default empty, (months|years)
20463  * @cfg {String} minViewMode default empty, (months|years)
20464  * @cfg {Number} startDate default -Infinity
20465  * @cfg {Number} endDate default Infinity
20466  * @cfg {Boolean} todayHighlight default false
20467  * @cfg {Boolean} todayBtn default false
20468  * @cfg {Boolean} calendarWeeks default false
20469  * @cfg {Object} daysOfWeekDisabled default empty
20470  * @cfg {Boolean} singleMode default false (true | false)
20471  * 
20472  * @cfg {Boolean} keyboardNavigation default true
20473  * @cfg {String} language default en
20474  * 
20475  * @constructor
20476  * Create a new DateField
20477  * @param {Object} config The config object
20478  */
20479
20480 Roo.bootstrap.DateField = function(config){
20481     Roo.bootstrap.DateField.superclass.constructor.call(this, config);
20482      this.addEvents({
20483             /**
20484              * @event show
20485              * Fires when this field show.
20486              * @param {Roo.bootstrap.DateField} this
20487              * @param {Mixed} date The date value
20488              */
20489             show : true,
20490             /**
20491              * @event show
20492              * Fires when this field hide.
20493              * @param {Roo.bootstrap.DateField} this
20494              * @param {Mixed} date The date value
20495              */
20496             hide : true,
20497             /**
20498              * @event select
20499              * Fires when select a date.
20500              * @param {Roo.bootstrap.DateField} this
20501              * @param {Mixed} date The date value
20502              */
20503             select : true,
20504             /**
20505              * @event beforeselect
20506              * Fires when before select a date.
20507              * @param {Roo.bootstrap.DateField} this
20508              * @param {Mixed} date The date value
20509              */
20510             beforeselect : true
20511         });
20512 };
20513
20514 Roo.extend(Roo.bootstrap.DateField, Roo.bootstrap.Input,  {
20515     
20516     /**
20517      * @cfg {String} format
20518      * The default date format string which can be overriden for localization support.  The format must be
20519      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
20520      */
20521     format : "m/d/y",
20522     /**
20523      * @cfg {String} altFormats
20524      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
20525      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
20526      */
20527     altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
20528     
20529     weekStart : 0,
20530     
20531     viewMode : '',
20532     
20533     minViewMode : '',
20534     
20535     todayHighlight : false,
20536     
20537     todayBtn: false,
20538     
20539     language: 'en',
20540     
20541     keyboardNavigation: true,
20542     
20543     calendarWeeks: false,
20544     
20545     startDate: -Infinity,
20546     
20547     endDate: Infinity,
20548     
20549     daysOfWeekDisabled: [],
20550     
20551     _events: [],
20552     
20553     singleMode : false,
20554     
20555     UTCDate: function()
20556     {
20557         return new Date(Date.UTC.apply(Date, arguments));
20558     },
20559     
20560     UTCToday: function()
20561     {
20562         var today = new Date();
20563         return this.UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
20564     },
20565     
20566     getDate: function() {
20567             var d = this.getUTCDate();
20568             return new Date(d.getTime() + (d.getTimezoneOffset()*60000));
20569     },
20570     
20571     getUTCDate: function() {
20572             return this.date;
20573     },
20574     
20575     setDate: function(d) {
20576             this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000)));
20577     },
20578     
20579     setUTCDate: function(d) {
20580             this.date = d;
20581             this.setValue(this.formatDate(this.date));
20582     },
20583         
20584     onRender: function(ct, position)
20585     {
20586         
20587         Roo.bootstrap.DateField.superclass.onRender.call(this, ct, position);
20588         
20589         this.language = this.language || 'en';
20590         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : this.language.split('-')[0];
20591         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : "en";
20592         
20593         this.isRTL = Roo.bootstrap.DateField.dates[this.language].rtl || false;
20594         this.format = this.format || 'm/d/y';
20595         this.isInline = false;
20596         this.isInput = true;
20597         this.component = this.el.select('.add-on', true).first() || false;
20598         this.component = (this.component && this.component.length === 0) ? false : this.component;
20599         this.hasInput = this.component && this.inputEl().length;
20600         
20601         if (typeof(this.minViewMode === 'string')) {
20602             switch (this.minViewMode) {
20603                 case 'months':
20604                     this.minViewMode = 1;
20605                     break;
20606                 case 'years':
20607                     this.minViewMode = 2;
20608                     break;
20609                 default:
20610                     this.minViewMode = 0;
20611                     break;
20612             }
20613         }
20614         
20615         if (typeof(this.viewMode === 'string')) {
20616             switch (this.viewMode) {
20617                 case 'months':
20618                     this.viewMode = 1;
20619                     break;
20620                 case 'years':
20621                     this.viewMode = 2;
20622                     break;
20623                 default:
20624                     this.viewMode = 0;
20625                     break;
20626             }
20627         }
20628                 
20629         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.DateField.template);
20630         
20631 //        this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.DateField.template);
20632         
20633         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
20634         
20635         this.picker().on('mousedown', this.onMousedown, this);
20636         this.picker().on('click', this.onClick, this);
20637         
20638         this.picker().addClass('datepicker-dropdown');
20639         
20640         this.startViewMode = this.viewMode;
20641         
20642         if(this.singleMode){
20643             Roo.each(this.picker().select('thead > tr > th', true).elements, function(v){
20644                 v.setVisibilityMode(Roo.Element.DISPLAY);
20645                 v.hide();
20646             });
20647             
20648             Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
20649                 v.setStyle('width', '189px');
20650             });
20651         }
20652         
20653         Roo.each(this.picker().select('tfoot th.today', true).elements, function(v){
20654             if(!this.calendarWeeks){
20655                 v.remove();
20656                 return;
20657             }
20658             
20659             v.dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
20660             v.attr('colspan', function(i, val){
20661                 return parseInt(val) + 1;
20662             });
20663         });
20664                         
20665         
20666         this.weekEnd = this.weekStart === 0 ? 6 : this.weekStart - 1;
20667         
20668         this.setStartDate(this.startDate);
20669         this.setEndDate(this.endDate);
20670         
20671         this.setDaysOfWeekDisabled(this.daysOfWeekDisabled);
20672         
20673         this.fillDow();
20674         this.fillMonths();
20675         this.update();
20676         this.showMode();
20677         
20678         if(this.isInline) {
20679             this.showPopup();
20680         }
20681     },
20682     
20683     picker : function()
20684     {
20685         return this.pickerEl;
20686 //        return this.el.select('.datepicker', true).first();
20687     },
20688     
20689     fillDow: function()
20690     {
20691         var dowCnt = this.weekStart;
20692         
20693         var dow = {
20694             tag: 'tr',
20695             cn: [
20696                 
20697             ]
20698         };
20699         
20700         if(this.calendarWeeks){
20701             dow.cn.push({
20702                 tag: 'th',
20703                 cls: 'cw',
20704                 html: '&nbsp;'
20705             })
20706         }
20707         
20708         while (dowCnt < this.weekStart + 7) {
20709             dow.cn.push({
20710                 tag: 'th',
20711                 cls: 'dow',
20712                 html: Roo.bootstrap.DateField.dates[this.language].daysMin[(dowCnt++)%7]
20713             });
20714         }
20715         
20716         this.picker().select('>.datepicker-days thead', true).first().createChild(dow);
20717     },
20718     
20719     fillMonths: function()
20720     {    
20721         var i = 0;
20722         var months = this.picker().select('>.datepicker-months td', true).first();
20723         
20724         months.dom.innerHTML = '';
20725         
20726         while (i < 12) {
20727             var month = {
20728                 tag: 'span',
20729                 cls: 'month',
20730                 html: Roo.bootstrap.DateField.dates[this.language].monthsShort[i++]
20731             };
20732             
20733             months.createChild(month);
20734         }
20735         
20736     },
20737     
20738     update: function()
20739     {
20740         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;
20741         
20742         if (this.date < this.startDate) {
20743             this.viewDate = new Date(this.startDate);
20744         } else if (this.date > this.endDate) {
20745             this.viewDate = new Date(this.endDate);
20746         } else {
20747             this.viewDate = new Date(this.date);
20748         }
20749         
20750         this.fill();
20751     },
20752     
20753     fill: function() 
20754     {
20755         var d = new Date(this.viewDate),
20756                 year = d.getUTCFullYear(),
20757                 month = d.getUTCMonth(),
20758                 startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity,
20759                 startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
20760                 endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
20761                 endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
20762                 currentDate = this.date && this.date.valueOf(),
20763                 today = this.UTCToday();
20764         
20765         this.picker().select('>.datepicker-days thead th.switch', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].months[month]+' '+year;
20766         
20767 //        this.picker().select('>tfoot th.today', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
20768         
20769 //        this.picker.select('>tfoot th.today').
20770 //                                              .text(dates[this.language].today)
20771 //                                              .toggle(this.todayBtn !== false);
20772     
20773         this.updateNavArrows();
20774         this.fillMonths();
20775                                                 
20776         var prevMonth = this.UTCDate(year, month-1, 28,0,0,0,0),
20777         
20778         day = prevMonth.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
20779          
20780         prevMonth.setUTCDate(day);
20781         
20782         prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7);
20783         
20784         var nextMonth = new Date(prevMonth);
20785         
20786         nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
20787         
20788         nextMonth = nextMonth.valueOf();
20789         
20790         var fillMonths = false;
20791         
20792         this.picker().select('>.datepicker-days tbody',true).first().dom.innerHTML = '';
20793         
20794         while(prevMonth.valueOf() <= nextMonth) {
20795             var clsName = '';
20796             
20797             if (prevMonth.getUTCDay() === this.weekStart) {
20798                 if(fillMonths){
20799                     this.picker().select('>.datepicker-days tbody',true).first().createChild(fillMonths);
20800                 }
20801                     
20802                 fillMonths = {
20803                     tag: 'tr',
20804                     cn: []
20805                 };
20806                 
20807                 if(this.calendarWeeks){
20808                     // ISO 8601: First week contains first thursday.
20809                     // ISO also states week starts on Monday, but we can be more abstract here.
20810                     var
20811                     // Start of current week: based on weekstart/current date
20812                     ws = new Date(+prevMonth + (this.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5),
20813                     // Thursday of this week
20814                     th = new Date(+ws + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
20815                     // First Thursday of year, year from thursday
20816                     yth = new Date(+(yth = this.UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5),
20817                     // Calendar week: ms between thursdays, div ms per day, div 7 days
20818                     calWeek =  (th - yth) / 864e5 / 7 + 1;
20819                     
20820                     fillMonths.cn.push({
20821                         tag: 'td',
20822                         cls: 'cw',
20823                         html: calWeek
20824                     });
20825                 }
20826             }
20827             
20828             if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
20829                 clsName += ' old';
20830             } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
20831                 clsName += ' new';
20832             }
20833             if (this.todayHighlight &&
20834                 prevMonth.getUTCFullYear() == today.getFullYear() &&
20835                 prevMonth.getUTCMonth() == today.getMonth() &&
20836                 prevMonth.getUTCDate() == today.getDate()) {
20837                 clsName += ' today';
20838             }
20839             
20840             if (currentDate && prevMonth.valueOf() === currentDate) {
20841                 clsName += ' active';
20842             }
20843             
20844             if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate ||
20845                     this.daysOfWeekDisabled.indexOf(prevMonth.getUTCDay()) !== -1) {
20846                     clsName += ' disabled';
20847             }
20848             
20849             fillMonths.cn.push({
20850                 tag: 'td',
20851                 cls: 'day ' + clsName,
20852                 html: prevMonth.getDate()
20853             });
20854             
20855             prevMonth.setDate(prevMonth.getDate()+1);
20856         }
20857           
20858         var currentYear = this.date && this.date.getUTCFullYear();
20859         var currentMonth = this.date && this.date.getUTCMonth();
20860         
20861         this.picker().select('>.datepicker-months th.switch',true).first().dom.innerHTML = year;
20862         
20863         Roo.each(this.picker().select('>.datepicker-months tbody span',true).elements, function(v,k){
20864             v.removeClass('active');
20865             
20866             if(currentYear === year && k === currentMonth){
20867                 v.addClass('active');
20868             }
20869             
20870             if (year < startYear || year > endYear || (year == startYear && k < startMonth) || (year == endYear && k > endMonth)) {
20871                 v.addClass('disabled');
20872             }
20873             
20874         });
20875         
20876         
20877         year = parseInt(year/10, 10) * 10;
20878         
20879         this.picker().select('>.datepicker-years th.switch', true).first().dom.innerHTML = year + '-' + (year + 9);
20880         
20881         this.picker().select('>.datepicker-years tbody td',true).first().dom.innerHTML = '';
20882         
20883         year -= 1;
20884         for (var i = -1; i < 11; i++) {
20885             this.picker().select('>.datepicker-years tbody td',true).first().createChild({
20886                 tag: 'span',
20887                 cls: 'year' + (i === -1 || i === 10 ? ' old' : '') + (currentYear === year ? ' active' : '') + (year < startYear || year > endYear ? ' disabled' : ''),
20888                 html: year
20889             });
20890             
20891             year += 1;
20892         }
20893     },
20894     
20895     showMode: function(dir) 
20896     {
20897         if (dir) {
20898             this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir));
20899         }
20900         
20901         Roo.each(this.picker().select('>div',true).elements, function(v){
20902             v.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
20903             v.hide();
20904         });
20905         this.picker().select('>.datepicker-'+Roo.bootstrap.DateField.modes[this.viewMode].clsName, true).first().show();
20906     },
20907     
20908     place: function()
20909     {
20910         if(this.isInline) {
20911             return;
20912         }
20913         
20914         this.picker().removeClass(['bottom', 'top']);
20915         
20916         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
20917             /*
20918              * place to the top of element!
20919              *
20920              */
20921             
20922             this.picker().addClass('top');
20923             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
20924             
20925             return;
20926         }
20927         
20928         this.picker().addClass('bottom');
20929         
20930         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
20931     },
20932     
20933     parseDate : function(value)
20934     {
20935         if(!value || value instanceof Date){
20936             return value;
20937         }
20938         var v = Date.parseDate(value, this.format);
20939         if (!v && (this.useIso || value.match(/^(\d{4})-0?(\d+)-0?(\d+)/))) {
20940             v = Date.parseDate(value, 'Y-m-d');
20941         }
20942         if(!v && this.altFormats){
20943             if(!this.altFormatsArray){
20944                 this.altFormatsArray = this.altFormats.split("|");
20945             }
20946             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
20947                 v = Date.parseDate(value, this.altFormatsArray[i]);
20948             }
20949         }
20950         return v;
20951     },
20952     
20953     formatDate : function(date, fmt)
20954     {   
20955         return (!date || !(date instanceof Date)) ?
20956         date : date.dateFormat(fmt || this.format);
20957     },
20958     
20959     onFocus : function()
20960     {
20961         Roo.bootstrap.DateField.superclass.onFocus.call(this);
20962         this.showPopup();
20963     },
20964     
20965     onBlur : function()
20966     {
20967         Roo.bootstrap.DateField.superclass.onBlur.call(this);
20968         
20969         var d = this.inputEl().getValue();
20970         
20971         this.setValue(d);
20972                 
20973         this.hidePopup();
20974     },
20975     
20976     showPopup : function()
20977     {
20978         this.picker().show();
20979         this.update();
20980         this.place();
20981         
20982         this.fireEvent('showpopup', this, this.date);
20983     },
20984     
20985     hidePopup : function()
20986     {
20987         if(this.isInline) {
20988             return;
20989         }
20990         this.picker().hide();
20991         this.viewMode = this.startViewMode;
20992         this.showMode();
20993         
20994         this.fireEvent('hidepopup', this, this.date);
20995         
20996     },
20997     
20998     onMousedown: function(e)
20999     {
21000         e.stopPropagation();
21001         e.preventDefault();
21002     },
21003     
21004     keyup: function(e)
21005     {
21006         Roo.bootstrap.DateField.superclass.keyup.call(this);
21007         this.update();
21008     },
21009
21010     setValue: function(v)
21011     {
21012         if(this.fireEvent('beforeselect', this, v) !== false){
21013             var d = new Date(this.parseDate(v) ).clearTime();
21014         
21015             if(isNaN(d.getTime())){
21016                 this.date = this.viewDate = '';
21017                 Roo.bootstrap.DateField.superclass.setValue.call(this, '');
21018                 return;
21019             }
21020
21021             v = this.formatDate(d);
21022
21023             Roo.bootstrap.DateField.superclass.setValue.call(this, v);
21024
21025             this.date = new Date(d.getTime() - d.getTimezoneOffset()*60000);
21026
21027             this.update();
21028
21029             this.fireEvent('select', this, this.date);
21030         }
21031     },
21032     
21033     getValue: function()
21034     {
21035         return this.formatDate(this.date);
21036     },
21037     
21038     fireKey: function(e)
21039     {
21040         if (!this.picker().isVisible()){
21041             if (e.keyCode == 27) { // allow escape to hide and re-show picker
21042                 this.showPopup();
21043             }
21044             return;
21045         }
21046         
21047         var dateChanged = false,
21048         dir, day, month,
21049         newDate, newViewDate;
21050         
21051         switch(e.keyCode){
21052             case 27: // escape
21053                 this.hidePopup();
21054                 e.preventDefault();
21055                 break;
21056             case 37: // left
21057             case 39: // right
21058                 if (!this.keyboardNavigation) {
21059                     break;
21060                 }
21061                 dir = e.keyCode == 37 ? -1 : 1;
21062                 
21063                 if (e.ctrlKey){
21064                     newDate = this.moveYear(this.date, dir);
21065                     newViewDate = this.moveYear(this.viewDate, dir);
21066                 } else if (e.shiftKey){
21067                     newDate = this.moveMonth(this.date, dir);
21068                     newViewDate = this.moveMonth(this.viewDate, dir);
21069                 } else {
21070                     newDate = new Date(this.date);
21071                     newDate.setUTCDate(this.date.getUTCDate() + dir);
21072                     newViewDate = new Date(this.viewDate);
21073                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
21074                 }
21075                 if (this.dateWithinRange(newDate)){
21076                     this.date = newDate;
21077                     this.viewDate = newViewDate;
21078                     this.setValue(this.formatDate(this.date));
21079 //                    this.update();
21080                     e.preventDefault();
21081                     dateChanged = true;
21082                 }
21083                 break;
21084             case 38: // up
21085             case 40: // down
21086                 if (!this.keyboardNavigation) {
21087                     break;
21088                 }
21089                 dir = e.keyCode == 38 ? -1 : 1;
21090                 if (e.ctrlKey){
21091                     newDate = this.moveYear(this.date, dir);
21092                     newViewDate = this.moveYear(this.viewDate, dir);
21093                 } else if (e.shiftKey){
21094                     newDate = this.moveMonth(this.date, dir);
21095                     newViewDate = this.moveMonth(this.viewDate, dir);
21096                 } else {
21097                     newDate = new Date(this.date);
21098                     newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
21099                     newViewDate = new Date(this.viewDate);
21100                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
21101                 }
21102                 if (this.dateWithinRange(newDate)){
21103                     this.date = newDate;
21104                     this.viewDate = newViewDate;
21105                     this.setValue(this.formatDate(this.date));
21106 //                    this.update();
21107                     e.preventDefault();
21108                     dateChanged = true;
21109                 }
21110                 break;
21111             case 13: // enter
21112                 this.setValue(this.formatDate(this.date));
21113                 this.hidePopup();
21114                 e.preventDefault();
21115                 break;
21116             case 9: // tab
21117                 this.setValue(this.formatDate(this.date));
21118                 this.hidePopup();
21119                 break;
21120             case 16: // shift
21121             case 17: // ctrl
21122             case 18: // alt
21123                 break;
21124             default :
21125                 this.hidePopup();
21126                 
21127         }
21128     },
21129     
21130     
21131     onClick: function(e) 
21132     {
21133         e.stopPropagation();
21134         e.preventDefault();
21135         
21136         var target = e.getTarget();
21137         
21138         if(target.nodeName.toLowerCase() === 'i'){
21139             target = Roo.get(target).dom.parentNode;
21140         }
21141         
21142         var nodeName = target.nodeName;
21143         var className = target.className;
21144         var html = target.innerHTML;
21145         //Roo.log(nodeName);
21146         
21147         switch(nodeName.toLowerCase()) {
21148             case 'th':
21149                 switch(className) {
21150                     case 'switch':
21151                         this.showMode(1);
21152                         break;
21153                     case 'prev':
21154                     case 'next':
21155                         var dir = Roo.bootstrap.DateField.modes[this.viewMode].navStep * (className == 'prev' ? -1 : 1);
21156                         switch(this.viewMode){
21157                                 case 0:
21158                                         this.viewDate = this.moveMonth(this.viewDate, dir);
21159                                         break;
21160                                 case 1:
21161                                 case 2:
21162                                         this.viewDate = this.moveYear(this.viewDate, dir);
21163                                         break;
21164                         }
21165                         this.fill();
21166                         break;
21167                     case 'today':
21168                         var date = new Date();
21169                         this.date = this.UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
21170 //                        this.fill()
21171                         this.setValue(this.formatDate(this.date));
21172                         
21173                         this.hidePopup();
21174                         break;
21175                 }
21176                 break;
21177             case 'span':
21178                 if (className.indexOf('disabled') < 0) {
21179                     this.viewDate.setUTCDate(1);
21180                     if (className.indexOf('month') > -1) {
21181                         this.viewDate.setUTCMonth(Roo.bootstrap.DateField.dates[this.language].monthsShort.indexOf(html));
21182                     } else {
21183                         var year = parseInt(html, 10) || 0;
21184                         this.viewDate.setUTCFullYear(year);
21185                         
21186                     }
21187                     
21188                     if(this.singleMode){
21189                         this.setValue(this.formatDate(this.viewDate));
21190                         this.hidePopup();
21191                         return;
21192                     }
21193                     
21194                     this.showMode(-1);
21195                     this.fill();
21196                 }
21197                 break;
21198                 
21199             case 'td':
21200                 //Roo.log(className);
21201                 if (className.indexOf('day') > -1 && className.indexOf('disabled') < 0 ){
21202                     var day = parseInt(html, 10) || 1;
21203                     var year = this.viewDate.getUTCFullYear(),
21204                         month = this.viewDate.getUTCMonth();
21205
21206                     if (className.indexOf('old') > -1) {
21207                         if(month === 0 ){
21208                             month = 11;
21209                             year -= 1;
21210                         }else{
21211                             month -= 1;
21212                         }
21213                     } else if (className.indexOf('new') > -1) {
21214                         if (month == 11) {
21215                             month = 0;
21216                             year += 1;
21217                         } else {
21218                             month += 1;
21219                         }
21220                     }
21221                     //Roo.log([year,month,day]);
21222                     this.date = this.UTCDate(year, month, day,0,0,0,0);
21223                     this.viewDate = this.UTCDate(year, month, Math.min(28, day),0,0,0,0);
21224 //                    this.fill();
21225                     //Roo.log(this.formatDate(this.date));
21226                     this.setValue(this.formatDate(this.date));
21227                     this.hidePopup();
21228                 }
21229                 break;
21230         }
21231     },
21232     
21233     setStartDate: function(startDate)
21234     {
21235         this.startDate = startDate || -Infinity;
21236         if (this.startDate !== -Infinity) {
21237             this.startDate = this.parseDate(this.startDate);
21238         }
21239         this.update();
21240         this.updateNavArrows();
21241     },
21242
21243     setEndDate: function(endDate)
21244     {
21245         this.endDate = endDate || Infinity;
21246         if (this.endDate !== Infinity) {
21247             this.endDate = this.parseDate(this.endDate);
21248         }
21249         this.update();
21250         this.updateNavArrows();
21251     },
21252     
21253     setDaysOfWeekDisabled: function(daysOfWeekDisabled)
21254     {
21255         this.daysOfWeekDisabled = daysOfWeekDisabled || [];
21256         if (typeof(this.daysOfWeekDisabled) !== 'object') {
21257             this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
21258         }
21259         this.daysOfWeekDisabled = this.daysOfWeekDisabled.map(function (d) {
21260             return parseInt(d, 10);
21261         });
21262         this.update();
21263         this.updateNavArrows();
21264     },
21265     
21266     updateNavArrows: function() 
21267     {
21268         if(this.singleMode){
21269             return;
21270         }
21271         
21272         var d = new Date(this.viewDate),
21273         year = d.getUTCFullYear(),
21274         month = d.getUTCMonth();
21275         
21276         Roo.each(this.picker().select('.prev', true).elements, function(v){
21277             v.show();
21278             switch (this.viewMode) {
21279                 case 0:
21280
21281                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
21282                         v.hide();
21283                     }
21284                     break;
21285                 case 1:
21286                 case 2:
21287                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
21288                         v.hide();
21289                     }
21290                     break;
21291             }
21292         });
21293         
21294         Roo.each(this.picker().select('.next', true).elements, function(v){
21295             v.show();
21296             switch (this.viewMode) {
21297                 case 0:
21298
21299                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
21300                         v.hide();
21301                     }
21302                     break;
21303                 case 1:
21304                 case 2:
21305                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
21306                         v.hide();
21307                     }
21308                     break;
21309             }
21310         })
21311     },
21312     
21313     moveMonth: function(date, dir)
21314     {
21315         if (!dir) {
21316             return date;
21317         }
21318         var new_date = new Date(date.valueOf()),
21319         day = new_date.getUTCDate(),
21320         month = new_date.getUTCMonth(),
21321         mag = Math.abs(dir),
21322         new_month, test;
21323         dir = dir > 0 ? 1 : -1;
21324         if (mag == 1){
21325             test = dir == -1
21326             // If going back one month, make sure month is not current month
21327             // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
21328             ? function(){
21329                 return new_date.getUTCMonth() == month;
21330             }
21331             // If going forward one month, make sure month is as expected
21332             // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
21333             : function(){
21334                 return new_date.getUTCMonth() != new_month;
21335             };
21336             new_month = month + dir;
21337             new_date.setUTCMonth(new_month);
21338             // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
21339             if (new_month < 0 || new_month > 11) {
21340                 new_month = (new_month + 12) % 12;
21341             }
21342         } else {
21343             // For magnitudes >1, move one month at a time...
21344             for (var i=0; i<mag; i++) {
21345                 // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
21346                 new_date = this.moveMonth(new_date, dir);
21347             }
21348             // ...then reset the day, keeping it in the new month
21349             new_month = new_date.getUTCMonth();
21350             new_date.setUTCDate(day);
21351             test = function(){
21352                 return new_month != new_date.getUTCMonth();
21353             };
21354         }
21355         // Common date-resetting loop -- if date is beyond end of month, make it
21356         // end of month
21357         while (test()){
21358             new_date.setUTCDate(--day);
21359             new_date.setUTCMonth(new_month);
21360         }
21361         return new_date;
21362     },
21363
21364     moveYear: function(date, dir)
21365     {
21366         return this.moveMonth(date, dir*12);
21367     },
21368
21369     dateWithinRange: function(date)
21370     {
21371         return date >= this.startDate && date <= this.endDate;
21372     },
21373
21374     
21375     remove: function() 
21376     {
21377         this.picker().remove();
21378     },
21379     
21380     validateValue : function(value)
21381     {
21382         if(this.getVisibilityEl().hasClass('hidden')){
21383             return true;
21384         }
21385         
21386         if(value.length < 1)  {
21387             if(this.allowBlank){
21388                 return true;
21389             }
21390             return false;
21391         }
21392         
21393         if(value.length < this.minLength){
21394             return false;
21395         }
21396         if(value.length > this.maxLength){
21397             return false;
21398         }
21399         if(this.vtype){
21400             var vt = Roo.form.VTypes;
21401             if(!vt[this.vtype](value, this)){
21402                 return false;
21403             }
21404         }
21405         if(typeof this.validator == "function"){
21406             var msg = this.validator(value);
21407             if(msg !== true){
21408                 return false;
21409             }
21410         }
21411         
21412         if(this.regex && !this.regex.test(value)){
21413             return false;
21414         }
21415         
21416         if(typeof(this.parseDate(value)) == 'undefined'){
21417             return false;
21418         }
21419         
21420         if (this.endDate !== Infinity && this.parseDate(value).getTime() > this.endDate.getTime()) {
21421             return false;
21422         }      
21423         
21424         if (this.startDate !== -Infinity && this.parseDate(value).getTime() < this.startDate.getTime()) {
21425             return false;
21426         } 
21427         
21428         
21429         return true;
21430     },
21431     
21432     reset : function()
21433     {
21434         this.date = this.viewDate = '';
21435         
21436         Roo.bootstrap.DateField.superclass.setValue.call(this, '');
21437     }
21438    
21439 });
21440
21441 Roo.apply(Roo.bootstrap.DateField,  {
21442     
21443     head : {
21444         tag: 'thead',
21445         cn: [
21446         {
21447             tag: 'tr',
21448             cn: [
21449             {
21450                 tag: 'th',
21451                 cls: 'prev',
21452                 html: '<i class="fa fa-arrow-left"/>'
21453             },
21454             {
21455                 tag: 'th',
21456                 cls: 'switch',
21457                 colspan: '5'
21458             },
21459             {
21460                 tag: 'th',
21461                 cls: 'next',
21462                 html: '<i class="fa fa-arrow-right"/>'
21463             }
21464
21465             ]
21466         }
21467         ]
21468     },
21469     
21470     content : {
21471         tag: 'tbody',
21472         cn: [
21473         {
21474             tag: 'tr',
21475             cn: [
21476             {
21477                 tag: 'td',
21478                 colspan: '7'
21479             }
21480             ]
21481         }
21482         ]
21483     },
21484     
21485     footer : {
21486         tag: 'tfoot',
21487         cn: [
21488         {
21489             tag: 'tr',
21490             cn: [
21491             {
21492                 tag: 'th',
21493                 colspan: '7',
21494                 cls: 'today'
21495             }
21496                     
21497             ]
21498         }
21499         ]
21500     },
21501     
21502     dates:{
21503         en: {
21504             days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
21505             daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
21506             daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
21507             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
21508             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
21509             today: "Today"
21510         }
21511     },
21512     
21513     modes: [
21514     {
21515         clsName: 'days',
21516         navFnc: 'Month',
21517         navStep: 1
21518     },
21519     {
21520         clsName: 'months',
21521         navFnc: 'FullYear',
21522         navStep: 1
21523     },
21524     {
21525         clsName: 'years',
21526         navFnc: 'FullYear',
21527         navStep: 10
21528     }]
21529 });
21530
21531 Roo.apply(Roo.bootstrap.DateField,  {
21532   
21533     template : {
21534         tag: 'div',
21535         cls: 'datepicker dropdown-menu roo-dynamic',
21536         cn: [
21537         {
21538             tag: 'div',
21539             cls: 'datepicker-days',
21540             cn: [
21541             {
21542                 tag: 'table',
21543                 cls: 'table-condensed',
21544                 cn:[
21545                 Roo.bootstrap.DateField.head,
21546                 {
21547                     tag: 'tbody'
21548                 },
21549                 Roo.bootstrap.DateField.footer
21550                 ]
21551             }
21552             ]
21553         },
21554         {
21555             tag: 'div',
21556             cls: 'datepicker-months',
21557             cn: [
21558             {
21559                 tag: 'table',
21560                 cls: 'table-condensed',
21561                 cn:[
21562                 Roo.bootstrap.DateField.head,
21563                 Roo.bootstrap.DateField.content,
21564                 Roo.bootstrap.DateField.footer
21565                 ]
21566             }
21567             ]
21568         },
21569         {
21570             tag: 'div',
21571             cls: 'datepicker-years',
21572             cn: [
21573             {
21574                 tag: 'table',
21575                 cls: 'table-condensed',
21576                 cn:[
21577                 Roo.bootstrap.DateField.head,
21578                 Roo.bootstrap.DateField.content,
21579                 Roo.bootstrap.DateField.footer
21580                 ]
21581             }
21582             ]
21583         }
21584         ]
21585     }
21586 });
21587
21588  
21589
21590  /*
21591  * - LGPL
21592  *
21593  * TimeField
21594  * 
21595  */
21596
21597 /**
21598  * @class Roo.bootstrap.TimeField
21599  * @extends Roo.bootstrap.Input
21600  * Bootstrap DateField class
21601  * 
21602  * 
21603  * @constructor
21604  * Create a new TimeField
21605  * @param {Object} config The config object
21606  */
21607
21608 Roo.bootstrap.TimeField = function(config){
21609     Roo.bootstrap.TimeField.superclass.constructor.call(this, config);
21610     this.addEvents({
21611             /**
21612              * @event show
21613              * Fires when this field show.
21614              * @param {Roo.bootstrap.DateField} thisthis
21615              * @param {Mixed} date The date value
21616              */
21617             show : true,
21618             /**
21619              * @event show
21620              * Fires when this field hide.
21621              * @param {Roo.bootstrap.DateField} this
21622              * @param {Mixed} date The date value
21623              */
21624             hide : true,
21625             /**
21626              * @event select
21627              * Fires when select a date.
21628              * @param {Roo.bootstrap.DateField} this
21629              * @param {Mixed} date The date value
21630              */
21631             select : true
21632         });
21633 };
21634
21635 Roo.extend(Roo.bootstrap.TimeField, Roo.bootstrap.Input,  {
21636     
21637     /**
21638      * @cfg {String} format
21639      * The default time format string which can be overriden for localization support.  The format must be
21640      * valid according to {@link Date#parseDate} (defaults to 'H:i').
21641      */
21642     format : "H:i",
21643        
21644     onRender: function(ct, position)
21645     {
21646         
21647         Roo.bootstrap.TimeField.superclass.onRender.call(this, ct, position);
21648                 
21649         this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.TimeField.template);
21650         
21651         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
21652         
21653         this.pop = this.picker().select('>.datepicker-time',true).first();
21654         this.pop.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
21655         
21656         this.picker().on('mousedown', this.onMousedown, this);
21657         this.picker().on('click', this.onClick, this);
21658         
21659         this.picker().addClass('datepicker-dropdown');
21660     
21661         this.fillTime();
21662         this.update();
21663             
21664         this.pop.select('span.hours-up', true).first().on('click', this.onIncrementHours, this);
21665         this.pop.select('span.hours-down', true).first().on('click', this.onDecrementHours, this);
21666         this.pop.select('span.minutes-up', true).first().on('click', this.onIncrementMinutes, this);
21667         this.pop.select('span.minutes-down', true).first().on('click', this.onDecrementMinutes, this);
21668         this.pop.select('button.period', true).first().on('click', this.onTogglePeriod, this);
21669         this.pop.select('button.ok', true).first().on('click', this.setTime, this);
21670
21671     },
21672     
21673     fireKey: function(e){
21674         if (!this.picker().isVisible()){
21675             if (e.keyCode == 27) { // allow escape to hide and re-show picker
21676                 this.show();
21677             }
21678             return;
21679         }
21680
21681         e.preventDefault();
21682         
21683         switch(e.keyCode){
21684             case 27: // escape
21685                 this.hide();
21686                 break;
21687             case 37: // left
21688             case 39: // right
21689                 this.onTogglePeriod();
21690                 break;
21691             case 38: // up
21692                 this.onIncrementMinutes();
21693                 break;
21694             case 40: // down
21695                 this.onDecrementMinutes();
21696                 break;
21697             case 13: // enter
21698             case 9: // tab
21699                 this.setTime();
21700                 break;
21701         }
21702     },
21703     
21704     onClick: function(e) {
21705         e.stopPropagation();
21706         e.preventDefault();
21707     },
21708     
21709     picker : function()
21710     {
21711         return this.el.select('.datepicker', true).first();
21712     },
21713     
21714     fillTime: function()
21715     {    
21716         var time = this.pop.select('tbody', true).first();
21717         
21718         time.dom.innerHTML = '';
21719         
21720         time.createChild({
21721             tag: 'tr',
21722             cn: [
21723                 {
21724                     tag: 'td',
21725                     cn: [
21726                         {
21727                             tag: 'a',
21728                             href: '#',
21729                             cls: 'btn',
21730                             cn: [
21731                                 {
21732                                     tag: 'span',
21733                                     cls: 'hours-up glyphicon glyphicon-chevron-up'
21734                                 }
21735                             ]
21736                         } 
21737                     ]
21738                 },
21739                 {
21740                     tag: 'td',
21741                     cls: 'separator'
21742                 },
21743                 {
21744                     tag: 'td',
21745                     cn: [
21746                         {
21747                             tag: 'a',
21748                             href: '#',
21749                             cls: 'btn',
21750                             cn: [
21751                                 {
21752                                     tag: 'span',
21753                                     cls: 'minutes-up glyphicon glyphicon-chevron-up'
21754                                 }
21755                             ]
21756                         }
21757                     ]
21758                 },
21759                 {
21760                     tag: 'td',
21761                     cls: 'separator'
21762                 }
21763             ]
21764         });
21765         
21766         time.createChild({
21767             tag: 'tr',
21768             cn: [
21769                 {
21770                     tag: 'td',
21771                     cn: [
21772                         {
21773                             tag: 'span',
21774                             cls: 'timepicker-hour',
21775                             html: '00'
21776                         }  
21777                     ]
21778                 },
21779                 {
21780                     tag: 'td',
21781                     cls: 'separator',
21782                     html: ':'
21783                 },
21784                 {
21785                     tag: 'td',
21786                     cn: [
21787                         {
21788                             tag: 'span',
21789                             cls: 'timepicker-minute',
21790                             html: '00'
21791                         }  
21792                     ]
21793                 },
21794                 {
21795                     tag: 'td',
21796                     cls: 'separator'
21797                 },
21798                 {
21799                     tag: 'td',
21800                     cn: [
21801                         {
21802                             tag: 'button',
21803                             type: 'button',
21804                             cls: 'btn btn-primary period',
21805                             html: 'AM'
21806                             
21807                         }
21808                     ]
21809                 }
21810             ]
21811         });
21812         
21813         time.createChild({
21814             tag: 'tr',
21815             cn: [
21816                 {
21817                     tag: 'td',
21818                     cn: [
21819                         {
21820                             tag: 'a',
21821                             href: '#',
21822                             cls: 'btn',
21823                             cn: [
21824                                 {
21825                                     tag: 'span',
21826                                     cls: 'hours-down glyphicon glyphicon-chevron-down'
21827                                 }
21828                             ]
21829                         }
21830                     ]
21831                 },
21832                 {
21833                     tag: 'td',
21834                     cls: 'separator'
21835                 },
21836                 {
21837                     tag: 'td',
21838                     cn: [
21839                         {
21840                             tag: 'a',
21841                             href: '#',
21842                             cls: 'btn',
21843                             cn: [
21844                                 {
21845                                     tag: 'span',
21846                                     cls: 'minutes-down glyphicon glyphicon-chevron-down'
21847                                 }
21848                             ]
21849                         }
21850                     ]
21851                 },
21852                 {
21853                     tag: 'td',
21854                     cls: 'separator'
21855                 }
21856             ]
21857         });
21858         
21859     },
21860     
21861     update: function()
21862     {
21863         
21864         this.time = (typeof(this.time) === 'undefined') ? new Date() : this.time;
21865         
21866         this.fill();
21867     },
21868     
21869     fill: function() 
21870     {
21871         var hours = this.time.getHours();
21872         var minutes = this.time.getMinutes();
21873         var period = 'AM';
21874         
21875         if(hours > 11){
21876             period = 'PM';
21877         }
21878         
21879         if(hours == 0){
21880             hours = 12;
21881         }
21882         
21883         
21884         if(hours > 12){
21885             hours = hours - 12;
21886         }
21887         
21888         if(hours < 10){
21889             hours = '0' + hours;
21890         }
21891         
21892         if(minutes < 10){
21893             minutes = '0' + minutes;
21894         }
21895         
21896         this.pop.select('.timepicker-hour', true).first().dom.innerHTML = hours;
21897         this.pop.select('.timepicker-minute', true).first().dom.innerHTML = minutes;
21898         this.pop.select('button', true).first().dom.innerHTML = period;
21899         
21900     },
21901     
21902     place: function()
21903     {   
21904         this.picker().removeClass(['bottom-left', 'bottom-right', 'top-left', 'top-right']);
21905         
21906         var cls = ['bottom'];
21907         
21908         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){ // top
21909             cls.pop();
21910             cls.push('top');
21911         }
21912         
21913         cls.push('right');
21914         
21915         if((Roo.lib.Dom.getViewWidth() + Roo.get(document.body).getScroll().left) - (this.inputEl().getLeft() + this.picker().getWidth()) < 0){ // left
21916             cls.pop();
21917             cls.push('left');
21918         }
21919         
21920         this.picker().addClass(cls.join('-'));
21921         
21922         var _this = this;
21923         
21924         Roo.each(cls, function(c){
21925             if(c == 'bottom'){
21926                 _this.picker().setTop(_this.inputEl().getHeight());
21927                 return;
21928             }
21929             if(c == 'top'){
21930                 _this.picker().setTop(0 - _this.picker().getHeight());
21931                 return;
21932             }
21933             
21934             if(c == 'left'){
21935                 _this.picker().setLeft(_this.inputEl().getLeft() + _this.inputEl().getWidth() - _this.el.getLeft() - _this.picker().getWidth());
21936                 return;
21937             }
21938             if(c == 'right'){
21939                 _this.picker().setLeft(_this.inputEl().getLeft() - _this.el.getLeft());
21940                 return;
21941             }
21942         });
21943         
21944     },
21945   
21946     onFocus : function()
21947     {
21948         Roo.bootstrap.TimeField.superclass.onFocus.call(this);
21949         this.show();
21950     },
21951     
21952     onBlur : function()
21953     {
21954         Roo.bootstrap.TimeField.superclass.onBlur.call(this);
21955         this.hide();
21956     },
21957     
21958     show : function()
21959     {
21960         this.picker().show();
21961         this.pop.show();
21962         this.update();
21963         this.place();
21964         
21965         this.fireEvent('show', this, this.date);
21966     },
21967     
21968     hide : function()
21969     {
21970         this.picker().hide();
21971         this.pop.hide();
21972         
21973         this.fireEvent('hide', this, this.date);
21974     },
21975     
21976     setTime : function()
21977     {
21978         this.hide();
21979         this.setValue(this.time.format(this.format));
21980         
21981         this.fireEvent('select', this, this.date);
21982         
21983         
21984     },
21985     
21986     onMousedown: function(e){
21987         e.stopPropagation();
21988         e.preventDefault();
21989     },
21990     
21991     onIncrementHours: function()
21992     {
21993         Roo.log('onIncrementHours');
21994         this.time = this.time.add(Date.HOUR, 1);
21995         this.update();
21996         
21997     },
21998     
21999     onDecrementHours: function()
22000     {
22001         Roo.log('onDecrementHours');
22002         this.time = this.time.add(Date.HOUR, -1);
22003         this.update();
22004     },
22005     
22006     onIncrementMinutes: function()
22007     {
22008         Roo.log('onIncrementMinutes');
22009         this.time = this.time.add(Date.MINUTE, 1);
22010         this.update();
22011     },
22012     
22013     onDecrementMinutes: function()
22014     {
22015         Roo.log('onDecrementMinutes');
22016         this.time = this.time.add(Date.MINUTE, -1);
22017         this.update();
22018     },
22019     
22020     onTogglePeriod: function()
22021     {
22022         Roo.log('onTogglePeriod');
22023         this.time = this.time.add(Date.HOUR, 12);
22024         this.update();
22025     }
22026     
22027    
22028 });
22029
22030 Roo.apply(Roo.bootstrap.TimeField,  {
22031     
22032     content : {
22033         tag: 'tbody',
22034         cn: [
22035             {
22036                 tag: 'tr',
22037                 cn: [
22038                 {
22039                     tag: 'td',
22040                     colspan: '7'
22041                 }
22042                 ]
22043             }
22044         ]
22045     },
22046     
22047     footer : {
22048         tag: 'tfoot',
22049         cn: [
22050             {
22051                 tag: 'tr',
22052                 cn: [
22053                 {
22054                     tag: 'th',
22055                     colspan: '7',
22056                     cls: '',
22057                     cn: [
22058                         {
22059                             tag: 'button',
22060                             cls: 'btn btn-info ok',
22061                             html: 'OK'
22062                         }
22063                     ]
22064                 }
22065
22066                 ]
22067             }
22068         ]
22069     }
22070 });
22071
22072 Roo.apply(Roo.bootstrap.TimeField,  {
22073   
22074     template : {
22075         tag: 'div',
22076         cls: 'datepicker dropdown-menu',
22077         cn: [
22078             {
22079                 tag: 'div',
22080                 cls: 'datepicker-time',
22081                 cn: [
22082                 {
22083                     tag: 'table',
22084                     cls: 'table-condensed',
22085                     cn:[
22086                     Roo.bootstrap.TimeField.content,
22087                     Roo.bootstrap.TimeField.footer
22088                     ]
22089                 }
22090                 ]
22091             }
22092         ]
22093     }
22094 });
22095
22096  
22097
22098  /*
22099  * - LGPL
22100  *
22101  * MonthField
22102  * 
22103  */
22104
22105 /**
22106  * @class Roo.bootstrap.MonthField
22107  * @extends Roo.bootstrap.Input
22108  * Bootstrap MonthField class
22109  * 
22110  * @cfg {String} language default en
22111  * 
22112  * @constructor
22113  * Create a new MonthField
22114  * @param {Object} config The config object
22115  */
22116
22117 Roo.bootstrap.MonthField = function(config){
22118     Roo.bootstrap.MonthField.superclass.constructor.call(this, config);
22119     
22120     this.addEvents({
22121         /**
22122          * @event show
22123          * Fires when this field show.
22124          * @param {Roo.bootstrap.MonthField} this
22125          * @param {Mixed} date The date value
22126          */
22127         show : true,
22128         /**
22129          * @event show
22130          * Fires when this field hide.
22131          * @param {Roo.bootstrap.MonthField} this
22132          * @param {Mixed} date The date value
22133          */
22134         hide : true,
22135         /**
22136          * @event select
22137          * Fires when select a date.
22138          * @param {Roo.bootstrap.MonthField} this
22139          * @param {String} oldvalue The old value
22140          * @param {String} newvalue The new value
22141          */
22142         select : true
22143     });
22144 };
22145
22146 Roo.extend(Roo.bootstrap.MonthField, Roo.bootstrap.Input,  {
22147     
22148     onRender: function(ct, position)
22149     {
22150         
22151         Roo.bootstrap.MonthField.superclass.onRender.call(this, ct, position);
22152         
22153         this.language = this.language || 'en';
22154         this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : this.language.split('-')[0];
22155         this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : "en";
22156         
22157         this.isRTL = Roo.bootstrap.MonthField.dates[this.language].rtl || false;
22158         this.isInline = false;
22159         this.isInput = true;
22160         this.component = this.el.select('.add-on', true).first() || false;
22161         this.component = (this.component && this.component.length === 0) ? false : this.component;
22162         this.hasInput = this.component && this.inputEL().length;
22163         
22164         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.MonthField.template);
22165         
22166         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
22167         
22168         this.picker().on('mousedown', this.onMousedown, this);
22169         this.picker().on('click', this.onClick, this);
22170         
22171         this.picker().addClass('datepicker-dropdown');
22172         
22173         Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
22174             v.setStyle('width', '189px');
22175         });
22176         
22177         this.fillMonths();
22178         
22179         this.update();
22180         
22181         if(this.isInline) {
22182             this.show();
22183         }
22184         
22185     },
22186     
22187     setValue: function(v, suppressEvent)
22188     {   
22189         var o = this.getValue();
22190         
22191         Roo.bootstrap.MonthField.superclass.setValue.call(this, v);
22192         
22193         this.update();
22194
22195         if(suppressEvent !== true){
22196             this.fireEvent('select', this, o, v);
22197         }
22198         
22199     },
22200     
22201     getValue: function()
22202     {
22203         return this.value;
22204     },
22205     
22206     onClick: function(e) 
22207     {
22208         e.stopPropagation();
22209         e.preventDefault();
22210         
22211         var target = e.getTarget();
22212         
22213         if(target.nodeName.toLowerCase() === 'i'){
22214             target = Roo.get(target).dom.parentNode;
22215         }
22216         
22217         var nodeName = target.nodeName;
22218         var className = target.className;
22219         var html = target.innerHTML;
22220         
22221         if(nodeName.toLowerCase() != 'span' || className.indexOf('disabled') > -1 || className.indexOf('month') == -1){
22222             return;
22223         }
22224         
22225         this.vIndex = Roo.bootstrap.MonthField.dates[this.language].monthsShort.indexOf(html);
22226         
22227         this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
22228         
22229         this.hide();
22230                         
22231     },
22232     
22233     picker : function()
22234     {
22235         return this.pickerEl;
22236     },
22237     
22238     fillMonths: function()
22239     {    
22240         var i = 0;
22241         var months = this.picker().select('>.datepicker-months td', true).first();
22242         
22243         months.dom.innerHTML = '';
22244         
22245         while (i < 12) {
22246             var month = {
22247                 tag: 'span',
22248                 cls: 'month',
22249                 html: Roo.bootstrap.MonthField.dates[this.language].monthsShort[i++]
22250             };
22251             
22252             months.createChild(month);
22253         }
22254         
22255     },
22256     
22257     update: function()
22258     {
22259         var _this = this;
22260         
22261         if(typeof(this.vIndex) == 'undefined' && this.value.length){
22262             this.vIndex = Roo.bootstrap.MonthField.dates[this.language].months.indexOf(this.value);
22263         }
22264         
22265         Roo.each(this.pickerEl.select('> .datepicker-months tbody > tr > td > span', true).elements, function(e, k){
22266             e.removeClass('active');
22267             
22268             if(typeof(_this.vIndex) != 'undefined' && k == _this.vIndex){
22269                 e.addClass('active');
22270             }
22271         })
22272     },
22273     
22274     place: function()
22275     {
22276         if(this.isInline) {
22277             return;
22278         }
22279         
22280         this.picker().removeClass(['bottom', 'top']);
22281         
22282         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
22283             /*
22284              * place to the top of element!
22285              *
22286              */
22287             
22288             this.picker().addClass('top');
22289             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
22290             
22291             return;
22292         }
22293         
22294         this.picker().addClass('bottom');
22295         
22296         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
22297     },
22298     
22299     onFocus : function()
22300     {
22301         Roo.bootstrap.MonthField.superclass.onFocus.call(this);
22302         this.show();
22303     },
22304     
22305     onBlur : function()
22306     {
22307         Roo.bootstrap.MonthField.superclass.onBlur.call(this);
22308         
22309         var d = this.inputEl().getValue();
22310         
22311         this.setValue(d);
22312                 
22313         this.hide();
22314     },
22315     
22316     show : function()
22317     {
22318         this.picker().show();
22319         this.picker().select('>.datepicker-months', true).first().show();
22320         this.update();
22321         this.place();
22322         
22323         this.fireEvent('show', this, this.date);
22324     },
22325     
22326     hide : function()
22327     {
22328         if(this.isInline) {
22329             return;
22330         }
22331         this.picker().hide();
22332         this.fireEvent('hide', this, this.date);
22333         
22334     },
22335     
22336     onMousedown: function(e)
22337     {
22338         e.stopPropagation();
22339         e.preventDefault();
22340     },
22341     
22342     keyup: function(e)
22343     {
22344         Roo.bootstrap.MonthField.superclass.keyup.call(this);
22345         this.update();
22346     },
22347
22348     fireKey: function(e)
22349     {
22350         if (!this.picker().isVisible()){
22351             if (e.keyCode == 27)   {// allow escape to hide and re-show picker
22352                 this.show();
22353             }
22354             return;
22355         }
22356         
22357         var dir;
22358         
22359         switch(e.keyCode){
22360             case 27: // escape
22361                 this.hide();
22362                 e.preventDefault();
22363                 break;
22364             case 37: // left
22365             case 39: // right
22366                 dir = e.keyCode == 37 ? -1 : 1;
22367                 
22368                 this.vIndex = this.vIndex + dir;
22369                 
22370                 if(this.vIndex < 0){
22371                     this.vIndex = 0;
22372                 }
22373                 
22374                 if(this.vIndex > 11){
22375                     this.vIndex = 11;
22376                 }
22377                 
22378                 if(isNaN(this.vIndex)){
22379                     this.vIndex = 0;
22380                 }
22381                 
22382                 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
22383                 
22384                 break;
22385             case 38: // up
22386             case 40: // down
22387                 
22388                 dir = e.keyCode == 38 ? -1 : 1;
22389                 
22390                 this.vIndex = this.vIndex + dir * 4;
22391                 
22392                 if(this.vIndex < 0){
22393                     this.vIndex = 0;
22394                 }
22395                 
22396                 if(this.vIndex > 11){
22397                     this.vIndex = 11;
22398                 }
22399                 
22400                 if(isNaN(this.vIndex)){
22401                     this.vIndex = 0;
22402                 }
22403                 
22404                 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
22405                 break;
22406                 
22407             case 13: // enter
22408                 
22409                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
22410                     this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
22411                 }
22412                 
22413                 this.hide();
22414                 e.preventDefault();
22415                 break;
22416             case 9: // tab
22417                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
22418                     this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
22419                 }
22420                 this.hide();
22421                 break;
22422             case 16: // shift
22423             case 17: // ctrl
22424             case 18: // alt
22425                 break;
22426             default :
22427                 this.hide();
22428                 
22429         }
22430     },
22431     
22432     remove: function() 
22433     {
22434         this.picker().remove();
22435     }
22436    
22437 });
22438
22439 Roo.apply(Roo.bootstrap.MonthField,  {
22440     
22441     content : {
22442         tag: 'tbody',
22443         cn: [
22444         {
22445             tag: 'tr',
22446             cn: [
22447             {
22448                 tag: 'td',
22449                 colspan: '7'
22450             }
22451             ]
22452         }
22453         ]
22454     },
22455     
22456     dates:{
22457         en: {
22458             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
22459             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
22460         }
22461     }
22462 });
22463
22464 Roo.apply(Roo.bootstrap.MonthField,  {
22465   
22466     template : {
22467         tag: 'div',
22468         cls: 'datepicker dropdown-menu roo-dynamic',
22469         cn: [
22470             {
22471                 tag: 'div',
22472                 cls: 'datepicker-months',
22473                 cn: [
22474                 {
22475                     tag: 'table',
22476                     cls: 'table-condensed',
22477                     cn:[
22478                         Roo.bootstrap.DateField.content
22479                     ]
22480                 }
22481                 ]
22482             }
22483         ]
22484     }
22485 });
22486
22487  
22488
22489  
22490  /*
22491  * - LGPL
22492  *
22493  * CheckBox
22494  * 
22495  */
22496
22497 /**
22498  * @class Roo.bootstrap.CheckBox
22499  * @extends Roo.bootstrap.Input
22500  * Bootstrap CheckBox class
22501  * 
22502  * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
22503  * @cfg {String} inputValue The value that should go into the generated input element's value when checked.
22504  * @cfg {String} boxLabel The text that appears beside the checkbox
22505  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the checkbox
22506  * @cfg {Boolean} checked initnal the element
22507  * @cfg {Boolean} inline inline the element (default false)
22508  * @cfg {String} groupId the checkbox group id // normal just use for checkbox
22509  * @cfg {String} tooltip label tooltip
22510  * 
22511  * @constructor
22512  * Create a new CheckBox
22513  * @param {Object} config The config object
22514  */
22515
22516 Roo.bootstrap.CheckBox = function(config){
22517     Roo.bootstrap.CheckBox.superclass.constructor.call(this, config);
22518    
22519     this.addEvents({
22520         /**
22521         * @event check
22522         * Fires when the element is checked or unchecked.
22523         * @param {Roo.bootstrap.CheckBox} this This input
22524         * @param {Boolean} checked The new checked value
22525         */
22526        check : true,
22527        /**
22528         * @event click
22529         * Fires when the element is click.
22530         * @param {Roo.bootstrap.CheckBox} this This input
22531         */
22532        click : true
22533     });
22534     
22535 };
22536
22537 Roo.extend(Roo.bootstrap.CheckBox, Roo.bootstrap.Input,  {
22538   
22539     inputType: 'checkbox',
22540     inputValue: 1,
22541     valueOff: 0,
22542     boxLabel: false,
22543     checked: false,
22544     weight : false,
22545     inline: false,
22546     tooltip : '',
22547     
22548     // checkbox success does not make any sense really.. 
22549     invalidClass : "",
22550     validClass : "",
22551     
22552     
22553     getAutoCreate : function()
22554     {
22555         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
22556         
22557         var id = Roo.id();
22558         
22559         var cfg = {};
22560         
22561         cfg.cls = 'form-group form-check ' + this.inputType; //input-group
22562         
22563         if(this.inline){
22564             cfg.cls += ' ' + this.inputType + '-inline  form-check-inline';
22565         }
22566         
22567         var input =  {
22568             tag: 'input',
22569             id : id,
22570             type : this.inputType,
22571             value : this.inputValue,
22572             cls : 'roo-' + this.inputType, //'form-box',
22573             placeholder : this.placeholder || ''
22574             
22575         };
22576         
22577         if(this.inputType != 'radio'){
22578             var hidden =  {
22579                 tag: 'input',
22580                 type : 'hidden',
22581                 cls : 'roo-hidden-value',
22582                 value : this.checked ? this.inputValue : this.valueOff
22583             };
22584         }
22585         
22586             
22587         if (this.weight) { // Validity check?
22588             cfg.cls += " " + this.inputType + "-" + this.weight;
22589         }
22590         
22591         if (this.disabled) {
22592             input.disabled=true;
22593         }
22594         
22595         if(this.checked){
22596             input.checked = this.checked;
22597         }
22598         
22599         if (this.name) {
22600             
22601             input.name = this.name;
22602             
22603             if(this.inputType != 'radio'){
22604                 hidden.name = this.name;
22605                 input.name = '_hidden_' + this.name;
22606             }
22607         }
22608         
22609         if (this.size) {
22610             input.cls += ' input-' + this.size;
22611         }
22612         
22613         var settings=this;
22614         
22615         ['xs','sm','md','lg'].map(function(size){
22616             if (settings[size]) {
22617                 cfg.cls += ' col-' + size + '-' + settings[size];
22618             }
22619         });
22620         
22621         var inputblock = input;
22622          
22623         if (this.before || this.after) {
22624             
22625             inputblock = {
22626                 cls : 'input-group',
22627                 cn :  [] 
22628             };
22629             
22630             if (this.before) {
22631                 inputblock.cn.push({
22632                     tag :'span',
22633                     cls : 'input-group-addon',
22634                     html : this.before
22635                 });
22636             }
22637             
22638             inputblock.cn.push(input);
22639             
22640             if(this.inputType != 'radio'){
22641                 inputblock.cn.push(hidden);
22642             }
22643             
22644             if (this.after) {
22645                 inputblock.cn.push({
22646                     tag :'span',
22647                     cls : 'input-group-addon',
22648                     html : this.after
22649                 });
22650             }
22651             
22652         }
22653         var boxLabelCfg = false;
22654         
22655         if(this.boxLabel){
22656            
22657             boxLabelCfg = {
22658                 tag: 'label',
22659                 //'for': id, // box label is handled by onclick - so no for...
22660                 cls: 'box-label',
22661                 html: this.boxLabel
22662             };
22663             if(this.tooltip){
22664                 boxLabelCfg.tooltip = this.tooltip;
22665             }
22666              
22667         }
22668         
22669         
22670         if (align ==='left' && this.fieldLabel.length) {
22671 //                Roo.log("left and has label");
22672             cfg.cn = [
22673                 {
22674                     tag: 'label',
22675                     'for' :  id,
22676                     cls : 'control-label',
22677                     html : this.fieldLabel
22678                 },
22679                 {
22680                     cls : "", 
22681                     cn: [
22682                         inputblock
22683                     ]
22684                 }
22685             ];
22686             
22687             if (boxLabelCfg) {
22688                 cfg.cn[1].cn.push(boxLabelCfg);
22689             }
22690             
22691             if(this.labelWidth > 12){
22692                 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
22693             }
22694             
22695             if(this.labelWidth < 13 && this.labelmd == 0){
22696                 this.labelmd = this.labelWidth;
22697             }
22698             
22699             if(this.labellg > 0){
22700                 cfg.cn[0].cls += ' col-lg-' + this.labellg;
22701                 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
22702             }
22703             
22704             if(this.labelmd > 0){
22705                 cfg.cn[0].cls += ' col-md-' + this.labelmd;
22706                 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
22707             }
22708             
22709             if(this.labelsm > 0){
22710                 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
22711                 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
22712             }
22713             
22714             if(this.labelxs > 0){
22715                 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
22716                 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
22717             }
22718             
22719         } else if ( this.fieldLabel.length) {
22720 //                Roo.log(" label");
22721                 cfg.cn = [
22722                    
22723                     {
22724                         tag: this.boxLabel ? 'span' : 'label',
22725                         'for': id,
22726                         cls: 'control-label box-input-label',
22727                         //cls : 'input-group-addon',
22728                         html : this.fieldLabel
22729                     },
22730                     
22731                     inputblock
22732                     
22733                 ];
22734                 if (boxLabelCfg) {
22735                     cfg.cn.push(boxLabelCfg);
22736                 }
22737
22738         } else {
22739             
22740 //                Roo.log(" no label && no align");
22741                 cfg.cn = [  inputblock ] ;
22742                 if (boxLabelCfg) {
22743                     cfg.cn.push(boxLabelCfg);
22744                 }
22745
22746                 
22747         }
22748         
22749        
22750         
22751         if(this.inputType != 'radio'){
22752             cfg.cn.push(hidden);
22753         }
22754         
22755         return cfg;
22756         
22757     },
22758     
22759     /**
22760      * return the real input element.
22761      */
22762     inputEl: function ()
22763     {
22764         return this.el.select('input.roo-' + this.inputType,true).first();
22765     },
22766     hiddenEl: function ()
22767     {
22768         return this.el.select('input.roo-hidden-value',true).first();
22769     },
22770     
22771     labelEl: function()
22772     {
22773         return this.el.select('label.control-label',true).first();
22774     },
22775     /* depricated... */
22776     
22777     label: function()
22778     {
22779         return this.labelEl();
22780     },
22781     
22782     boxLabelEl: function()
22783     {
22784         return this.el.select('label.box-label',true).first();
22785     },
22786     
22787     initEvents : function()
22788     {
22789 //        Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
22790         
22791         this.inputEl().on('click', this.onClick,  this);
22792         
22793         if (this.boxLabel) { 
22794             this.el.select('label.box-label',true).first().on('click', this.onClick,  this);
22795         }
22796         
22797         this.startValue = this.getValue();
22798         
22799         if(this.groupId){
22800             Roo.bootstrap.CheckBox.register(this);
22801         }
22802     },
22803     
22804     onClick : function(e)
22805     {   
22806         if(this.fireEvent('click', this, e) !== false){
22807             this.setChecked(!this.checked);
22808         }
22809         
22810     },
22811     
22812     setChecked : function(state,suppressEvent)
22813     {
22814         this.startValue = this.getValue();
22815
22816         if(this.inputType == 'radio'){
22817             
22818             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
22819                 e.dom.checked = false;
22820             });
22821             
22822             this.inputEl().dom.checked = true;
22823             
22824             this.inputEl().dom.value = this.inputValue;
22825             
22826             if(suppressEvent !== true){
22827                 this.fireEvent('check', this, true);
22828             }
22829             
22830             this.validate();
22831             
22832             return;
22833         }
22834         
22835         this.checked = state;
22836         
22837         this.inputEl().dom.checked = state;
22838         
22839         
22840         this.hiddenEl().dom.value = state ? this.inputValue : this.valueOff;
22841         
22842         if(suppressEvent !== true){
22843             this.fireEvent('check', this, state);
22844         }
22845         
22846         this.validate();
22847     },
22848     
22849     getValue : function()
22850     {
22851         if(this.inputType == 'radio'){
22852             return this.getGroupValue();
22853         }
22854         
22855         return this.hiddenEl().dom.value;
22856         
22857     },
22858     
22859     getGroupValue : function()
22860     {
22861         if(typeof(this.el.up('form').child('input[name='+this.name+']:checked', true)) == 'undefined'){
22862             return '';
22863         }
22864         
22865         return this.el.up('form').child('input[name='+this.name+']:checked', true).value;
22866     },
22867     
22868     setValue : function(v,suppressEvent)
22869     {
22870         if(this.inputType == 'radio'){
22871             this.setGroupValue(v, suppressEvent);
22872             return;
22873         }
22874         
22875         this.setChecked(((typeof(v) == 'undefined') ? this.checked : (String(v) === String(this.inputValue))), suppressEvent);
22876         
22877         this.validate();
22878     },
22879     
22880     setGroupValue : function(v, suppressEvent)
22881     {
22882         this.startValue = this.getValue();
22883         
22884         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
22885             e.dom.checked = false;
22886             
22887             if(e.dom.value == v){
22888                 e.dom.checked = true;
22889             }
22890         });
22891         
22892         if(suppressEvent !== true){
22893             this.fireEvent('check', this, true);
22894         }
22895
22896         this.validate();
22897         
22898         return;
22899     },
22900     
22901     validate : function()
22902     {
22903         if(this.getVisibilityEl().hasClass('hidden')){
22904             return true;
22905         }
22906         
22907         if(
22908                 this.disabled || 
22909                 (this.inputType == 'radio' && this.validateRadio()) ||
22910                 (this.inputType == 'checkbox' && this.validateCheckbox())
22911         ){
22912             this.markValid();
22913             return true;
22914         }
22915         
22916         this.markInvalid();
22917         return false;
22918     },
22919     
22920     validateRadio : function()
22921     {
22922         if(this.getVisibilityEl().hasClass('hidden')){
22923             return true;
22924         }
22925         
22926         if(this.allowBlank){
22927             return true;
22928         }
22929         
22930         var valid = false;
22931         
22932         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
22933             if(!e.dom.checked){
22934                 return;
22935             }
22936             
22937             valid = true;
22938             
22939             return false;
22940         });
22941         
22942         return valid;
22943     },
22944     
22945     validateCheckbox : function()
22946     {
22947         if(!this.groupId){
22948             return (this.getValue() == this.inputValue || this.allowBlank) ? true : false;
22949             //return (this.getValue() == this.inputValue) ? true : false;
22950         }
22951         
22952         var group = Roo.bootstrap.CheckBox.get(this.groupId);
22953         
22954         if(!group){
22955             return false;
22956         }
22957         
22958         var r = false;
22959         
22960         for(var i in group){
22961             if(group[i].el.isVisible(true)){
22962                 r = false;
22963                 break;
22964             }
22965             
22966             r = true;
22967         }
22968         
22969         for(var i in group){
22970             if(r){
22971                 break;
22972             }
22973             
22974             r = (group[i].getValue() == group[i].inputValue) ? true : false;
22975         }
22976         
22977         return r;
22978     },
22979     
22980     /**
22981      * Mark this field as valid
22982      */
22983     markValid : function()
22984     {
22985         var _this = this;
22986         
22987         this.fireEvent('valid', this);
22988         
22989         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
22990         
22991         if(this.groupId){
22992             label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
22993         }
22994         
22995         if(label){
22996             label.markValid();
22997         }
22998
22999         if(this.inputType == 'radio'){
23000             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23001                 var fg = e.findParent('.form-group', false, true);
23002                 if (Roo.bootstrap.version == 3) {
23003                     fg.removeClass([_this.invalidClass, _this.validClass]);
23004                     fg.addClass(_this.validClass);
23005                 } else {
23006                     fg.removeClass(['is-valid', 'is-invalid']);
23007                     fg.addClass('is-valid');
23008                 }
23009             });
23010             
23011             return;
23012         }
23013
23014         if(!this.groupId){
23015             var fg = this.el.findParent('.form-group', false, true);
23016             if (Roo.bootstrap.version == 3) {
23017                 fg.removeClass([this.invalidClass, this.validClass]);
23018                 fg.addClass(this.validClass);
23019             } else {
23020                 fg.removeClass(['is-valid', 'is-invalid']);
23021                 fg.addClass('is-valid');
23022             }
23023             return;
23024         }
23025         
23026         var group = Roo.bootstrap.CheckBox.get(this.groupId);
23027         
23028         if(!group){
23029             return;
23030         }
23031         
23032         for(var i in group){
23033             var fg = group[i].el.findParent('.form-group', false, true);
23034             if (Roo.bootstrap.version == 3) {
23035                 fg.removeClass([this.invalidClass, this.validClass]);
23036                 fg.addClass(this.validClass);
23037             } else {
23038                 fg.removeClass(['is-valid', 'is-invalid']);
23039                 fg.addClass('is-valid');
23040             }
23041         }
23042     },
23043     
23044      /**
23045      * Mark this field as invalid
23046      * @param {String} msg The validation message
23047      */
23048     markInvalid : function(msg)
23049     {
23050         if(this.allowBlank){
23051             return;
23052         }
23053         
23054         var _this = this;
23055         
23056         this.fireEvent('invalid', this, msg);
23057         
23058         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
23059         
23060         if(this.groupId){
23061             label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
23062         }
23063         
23064         if(label){
23065             label.markInvalid();
23066         }
23067             
23068         if(this.inputType == 'radio'){
23069             
23070             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23071                 var fg = e.findParent('.form-group', false, true);
23072                 if (Roo.bootstrap.version == 3) {
23073                     fg.removeClass([_this.invalidClass, _this.validClass]);
23074                     fg.addClass(_this.invalidClass);
23075                 } else {
23076                     fg.removeClass(['is-invalid', 'is-valid']);
23077                     fg.addClass('is-invalid');
23078                 }
23079             });
23080             
23081             return;
23082         }
23083         
23084         if(!this.groupId){
23085             var fg = this.el.findParent('.form-group', false, true);
23086             if (Roo.bootstrap.version == 3) {
23087                 fg.removeClass([_this.invalidClass, _this.validClass]);
23088                 fg.addClass(_this.invalidClass);
23089             } else {
23090                 fg.removeClass(['is-invalid', 'is-valid']);
23091                 fg.addClass('is-invalid');
23092             }
23093             return;
23094         }
23095         
23096         var group = Roo.bootstrap.CheckBox.get(this.groupId);
23097         
23098         if(!group){
23099             return;
23100         }
23101         
23102         for(var i in group){
23103             var fg = group[i].el.findParent('.form-group', false, true);
23104             if (Roo.bootstrap.version == 3) {
23105                 fg.removeClass([_this.invalidClass, _this.validClass]);
23106                 fg.addClass(_this.invalidClass);
23107             } else {
23108                 fg.removeClass(['is-invalid', 'is-valid']);
23109                 fg.addClass('is-invalid');
23110             }
23111         }
23112         
23113     },
23114     
23115     clearInvalid : function()
23116     {
23117         Roo.bootstrap.Input.prototype.clearInvalid.call(this);
23118         
23119         // this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
23120         
23121         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
23122         
23123         if (label && label.iconEl) {
23124             label.iconEl.removeClass([ label.validClass, label.invalidClass ]);
23125             label.iconEl.removeClass(['is-invalid', 'is-valid']);
23126         }
23127     },
23128     
23129     disable : function()
23130     {
23131         if(this.inputType != 'radio'){
23132             Roo.bootstrap.CheckBox.superclass.disable.call(this);
23133             return;
23134         }
23135         
23136         var _this = this;
23137         
23138         if(this.rendered){
23139             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23140                 _this.getActionEl().addClass(this.disabledClass);
23141                 e.dom.disabled = true;
23142             });
23143         }
23144         
23145         this.disabled = true;
23146         this.fireEvent("disable", this);
23147         return this;
23148     },
23149
23150     enable : function()
23151     {
23152         if(this.inputType != 'radio'){
23153             Roo.bootstrap.CheckBox.superclass.enable.call(this);
23154             return;
23155         }
23156         
23157         var _this = this;
23158         
23159         if(this.rendered){
23160             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23161                 _this.getActionEl().removeClass(this.disabledClass);
23162                 e.dom.disabled = false;
23163             });
23164         }
23165         
23166         this.disabled = false;
23167         this.fireEvent("enable", this);
23168         return this;
23169     },
23170     
23171     setBoxLabel : function(v)
23172     {
23173         this.boxLabel = v;
23174         
23175         if(this.rendered){
23176             this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
23177         }
23178     }
23179
23180 });
23181
23182 Roo.apply(Roo.bootstrap.CheckBox, {
23183     
23184     groups: {},
23185     
23186      /**
23187     * register a CheckBox Group
23188     * @param {Roo.bootstrap.CheckBox} the CheckBox to add
23189     */
23190     register : function(checkbox)
23191     {
23192         if(typeof(this.groups[checkbox.groupId]) == 'undefined'){
23193             this.groups[checkbox.groupId] = {};
23194         }
23195         
23196         if(this.groups[checkbox.groupId].hasOwnProperty(checkbox.name)){
23197             return;
23198         }
23199         
23200         this.groups[checkbox.groupId][checkbox.name] = checkbox;
23201         
23202     },
23203     /**
23204     * fetch a CheckBox Group based on the group ID
23205     * @param {string} the group ID
23206     * @returns {Roo.bootstrap.CheckBox} the CheckBox group
23207     */
23208     get: function(groupId) {
23209         if (typeof(this.groups[groupId]) == 'undefined') {
23210             return false;
23211         }
23212         
23213         return this.groups[groupId] ;
23214     }
23215     
23216     
23217 });
23218 /*
23219  * - LGPL
23220  *
23221  * RadioItem
23222  * 
23223  */
23224
23225 /**
23226  * @class Roo.bootstrap.Radio
23227  * @extends Roo.bootstrap.Component
23228  * Bootstrap Radio class
23229  * @cfg {String} boxLabel - the label associated
23230  * @cfg {String} value - the value of radio
23231  * 
23232  * @constructor
23233  * Create a new Radio
23234  * @param {Object} config The config object
23235  */
23236 Roo.bootstrap.Radio = function(config){
23237     Roo.bootstrap.Radio.superclass.constructor.call(this, config);
23238     
23239 };
23240
23241 Roo.extend(Roo.bootstrap.Radio, Roo.bootstrap.Component, {
23242     
23243     boxLabel : '',
23244     
23245     value : '',
23246     
23247     getAutoCreate : function()
23248     {
23249         var cfg = {
23250             tag : 'div',
23251             cls : 'form-group radio',
23252             cn : [
23253                 {
23254                     tag : 'label',
23255                     cls : 'box-label',
23256                     html : this.boxLabel
23257                 }
23258             ]
23259         };
23260         
23261         return cfg;
23262     },
23263     
23264     initEvents : function() 
23265     {
23266         this.parent().register(this);
23267         
23268         this.el.on('click', this.onClick, this);
23269         
23270     },
23271     
23272     onClick : function(e)
23273     {
23274         if(this.parent().fireEvent('click', this.parent(), this, e) !== false){
23275             this.setChecked(true);
23276         }
23277     },
23278     
23279     setChecked : function(state, suppressEvent)
23280     {
23281         this.parent().setValue(this.value, suppressEvent);
23282         
23283     },
23284     
23285     setBoxLabel : function(v)
23286     {
23287         this.boxLabel = v;
23288         
23289         if(this.rendered){
23290             this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
23291         }
23292     }
23293     
23294 });
23295  
23296
23297  /*
23298  * - LGPL
23299  *
23300  * Input
23301  * 
23302  */
23303
23304 /**
23305  * @class Roo.bootstrap.SecurePass
23306  * @extends Roo.bootstrap.Input
23307  * Bootstrap SecurePass class
23308  *
23309  * 
23310  * @constructor
23311  * Create a new SecurePass
23312  * @param {Object} config The config object
23313  */
23314  
23315 Roo.bootstrap.SecurePass = function (config) {
23316     // these go here, so the translation tool can replace them..
23317     this.errors = {
23318         PwdEmpty: "Please type a password, and then retype it to confirm.",
23319         PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
23320         PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
23321         PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
23322         IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
23323         FNInPwd: "Your password can't contain your first name. Please type a different password.",
23324         LNInPwd: "Your password can't contain your last name. Please type a different password.",
23325         TooWeak: "Your password is Too Weak."
23326     },
23327     this.meterLabel = "Password strength:";
23328     this.pwdStrengths = ["Too Weak", "Weak", "Medium", "Strong"];
23329     this.meterClass = [
23330         "roo-password-meter-tooweak", 
23331         "roo-password-meter-weak", 
23332         "roo-password-meter-medium", 
23333         "roo-password-meter-strong", 
23334         "roo-password-meter-grey"
23335     ];
23336     
23337     this.errors = {};
23338     
23339     Roo.bootstrap.SecurePass.superclass.constructor.call(this, config);
23340 }
23341
23342 Roo.extend(Roo.bootstrap.SecurePass, Roo.bootstrap.Input, {
23343     /**
23344      * @cfg {String/Object} errors A Error spec, or true for a default spec (defaults to
23345      * {
23346      *  PwdEmpty: "Please type a password, and then retype it to confirm.",
23347      *  PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
23348      *  PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
23349      *  PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
23350      *  IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
23351      *  FNInPwd: "Your password can't contain your first name. Please type a different password.",
23352      *  LNInPwd: "Your password can't contain your last name. Please type a different password."
23353      * })
23354      */
23355     // private
23356     
23357     meterWidth: 300,
23358     errorMsg :'',    
23359     errors: false,
23360     imageRoot: '/',
23361     /**
23362      * @cfg {String/Object} Label for the strength meter (defaults to
23363      * 'Password strength:')
23364      */
23365     // private
23366     meterLabel: '',
23367     /**
23368      * @cfg {String/Object} pwdStrengths A pwdStrengths spec, or true for a default spec (defaults to
23369      * ['Weak', 'Medium', 'Strong'])
23370      */
23371     // private    
23372     pwdStrengths: false,    
23373     // private
23374     strength: 0,
23375     // private
23376     _lastPwd: null,
23377     // private
23378     kCapitalLetter: 0,
23379     kSmallLetter: 1,
23380     kDigit: 2,
23381     kPunctuation: 3,
23382     
23383     insecure: false,
23384     // private
23385     initEvents: function ()
23386     {
23387         Roo.bootstrap.SecurePass.superclass.initEvents.call(this);
23388
23389         if (this.el.is('input[type=password]') && Roo.isSafari) {
23390             this.el.on('keydown', this.SafariOnKeyDown, this);
23391         }
23392
23393         this.el.on('keyup', this.checkStrength, this, {buffer: 50});
23394     },
23395     // private
23396     onRender: function (ct, position)
23397     {
23398         Roo.bootstrap.SecurePass.superclass.onRender.call(this, ct, position);
23399         this.wrap = this.el.wrap({cls: 'x-form-field-wrap'});
23400         this.trigger = this.wrap.createChild({tag: 'div', cls: 'StrengthMeter ' + this.triggerClass});
23401
23402         this.trigger.createChild({
23403                    cn: [
23404                     {
23405                     //id: 'PwdMeter',
23406                     tag: 'div',
23407                     cls: 'roo-password-meter-grey col-xs-12',
23408                     style: {
23409                         //width: 0,
23410                         //width: this.meterWidth + 'px'                                                
23411                         }
23412                     },
23413                     {                            
23414                          cls: 'roo-password-meter-text'                          
23415                     }
23416                 ]            
23417         });
23418
23419          
23420         if (this.hideTrigger) {
23421             this.trigger.setDisplayed(false);
23422         }
23423         this.setSize(this.width || '', this.height || '');
23424     },
23425     // private
23426     onDestroy: function ()
23427     {
23428         if (this.trigger) {
23429             this.trigger.removeAllListeners();
23430             this.trigger.remove();
23431         }
23432         if (this.wrap) {
23433             this.wrap.remove();
23434         }
23435         Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
23436     },
23437     // private
23438     checkStrength: function ()
23439     {
23440         var pwd = this.inputEl().getValue();
23441         if (pwd == this._lastPwd) {
23442             return;
23443         }
23444
23445         var strength;
23446         if (this.ClientSideStrongPassword(pwd)) {
23447             strength = 3;
23448         } else if (this.ClientSideMediumPassword(pwd)) {
23449             strength = 2;
23450         } else if (this.ClientSideWeakPassword(pwd)) {
23451             strength = 1;
23452         } else {
23453             strength = 0;
23454         }
23455         
23456         Roo.log('strength1: ' + strength);
23457         
23458         //var pm = this.trigger.child('div/div/div').dom;
23459         var pm = this.trigger.child('div/div');
23460         pm.removeClass(this.meterClass);
23461         pm.addClass(this.meterClass[strength]);
23462                 
23463         
23464         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
23465                 
23466         pt.innerHTML = this.meterLabel + '&nbsp;' + this.pwdStrengths[strength];
23467         
23468         this._lastPwd = pwd;
23469     },
23470     reset: function ()
23471     {
23472         Roo.bootstrap.SecurePass.superclass.reset.call(this);
23473         
23474         this._lastPwd = '';
23475         
23476         var pm = this.trigger.child('div/div');
23477         pm.removeClass(this.meterClass);
23478         pm.addClass('roo-password-meter-grey');        
23479         
23480         
23481         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
23482         
23483         pt.innerHTML = '';
23484         this.inputEl().dom.type='password';
23485     },
23486     // private
23487     validateValue: function (value)
23488     {
23489         
23490         if (!Roo.bootstrap.SecurePass.superclass.validateValue.call(this, value)) {
23491             return false;
23492         }
23493         if (value.length == 0) {
23494             if (this.allowBlank) {
23495                 this.clearInvalid();
23496                 return true;
23497             }
23498
23499             this.markInvalid(this.errors.PwdEmpty);
23500             this.errorMsg = this.errors.PwdEmpty;
23501             return false;
23502         }
23503         
23504         if(this.insecure){
23505             return true;
23506         }
23507         
23508         if ('[\x21-\x7e]*'.match(value)) {
23509             this.markInvalid(this.errors.PwdBadChar);
23510             this.errorMsg = this.errors.PwdBadChar;
23511             return false;
23512         }
23513         if (value.length < 6) {
23514             this.markInvalid(this.errors.PwdShort);
23515             this.errorMsg = this.errors.PwdShort;
23516             return false;
23517         }
23518         if (value.length > 16) {
23519             this.markInvalid(this.errors.PwdLong);
23520             this.errorMsg = this.errors.PwdLong;
23521             return false;
23522         }
23523         var strength;
23524         if (this.ClientSideStrongPassword(value)) {
23525             strength = 3;
23526         } else if (this.ClientSideMediumPassword(value)) {
23527             strength = 2;
23528         } else if (this.ClientSideWeakPassword(value)) {
23529             strength = 1;
23530         } else {
23531             strength = 0;
23532         }
23533
23534         
23535         if (strength < 2) {
23536             //this.markInvalid(this.errors.TooWeak);
23537             this.errorMsg = this.errors.TooWeak;
23538             //return false;
23539         }
23540         
23541         
23542         console.log('strength2: ' + strength);
23543         
23544         //var pm = this.trigger.child('div/div/div').dom;
23545         
23546         var pm = this.trigger.child('div/div');
23547         pm.removeClass(this.meterClass);
23548         pm.addClass(this.meterClass[strength]);
23549                 
23550         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
23551                 
23552         pt.innerHTML = this.meterLabel + '&nbsp;' + this.pwdStrengths[strength];
23553         
23554         this.errorMsg = ''; 
23555         return true;
23556     },
23557     // private
23558     CharacterSetChecks: function (type)
23559     {
23560         this.type = type;
23561         this.fResult = false;
23562     },
23563     // private
23564     isctype: function (character, type)
23565     {
23566         switch (type) {  
23567             case this.kCapitalLetter:
23568                 if (character >= 'A' && character <= 'Z') {
23569                     return true;
23570                 }
23571                 break;
23572             
23573             case this.kSmallLetter:
23574                 if (character >= 'a' && character <= 'z') {
23575                     return true;
23576                 }
23577                 break;
23578             
23579             case this.kDigit:
23580                 if (character >= '0' && character <= '9') {
23581                     return true;
23582                 }
23583                 break;
23584             
23585             case this.kPunctuation:
23586                 if ('!@#$%^&*()_+-=\'";:[{]}|.>,</?`~'.indexOf(character) >= 0) {
23587                     return true;
23588                 }
23589                 break;
23590             
23591             default:
23592                 return false;
23593         }
23594
23595     },
23596     // private
23597     IsLongEnough: function (pwd, size)
23598     {
23599         return !(pwd == null || isNaN(size) || pwd.length < size);
23600     },
23601     // private
23602     SpansEnoughCharacterSets: function (word, nb)
23603     {
23604         if (!this.IsLongEnough(word, nb))
23605         {
23606             return false;
23607         }
23608
23609         var characterSetChecks = new Array(
23610             new this.CharacterSetChecks(this.kCapitalLetter), new this.CharacterSetChecks(this.kSmallLetter),
23611             new this.CharacterSetChecks(this.kDigit), new this.CharacterSetChecks(this.kPunctuation)
23612         );
23613         
23614         for (var index = 0; index < word.length; ++index) {
23615             for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
23616                 if (!characterSetChecks[nCharSet].fResult && this.isctype(word.charAt(index), characterSetChecks[nCharSet].type)) {
23617                     characterSetChecks[nCharSet].fResult = true;
23618                     break;
23619                 }
23620             }
23621         }
23622
23623         var nCharSets = 0;
23624         for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
23625             if (characterSetChecks[nCharSet].fResult) {
23626                 ++nCharSets;
23627             }
23628         }
23629
23630         if (nCharSets < nb) {
23631             return false;
23632         }
23633         return true;
23634     },
23635     // private
23636     ClientSideStrongPassword: function (pwd)
23637     {
23638         return this.IsLongEnough(pwd, 8) && this.SpansEnoughCharacterSets(pwd, 3);
23639     },
23640     // private
23641     ClientSideMediumPassword: function (pwd)
23642     {
23643         return this.IsLongEnough(pwd, 7) && this.SpansEnoughCharacterSets(pwd, 2);
23644     },
23645     // private
23646     ClientSideWeakPassword: function (pwd)
23647     {
23648         return this.IsLongEnough(pwd, 6) || !this.IsLongEnough(pwd, 0);
23649     }
23650           
23651 })//<script type="text/javascript">
23652
23653 /*
23654  * Based  Ext JS Library 1.1.1
23655  * Copyright(c) 2006-2007, Ext JS, LLC.
23656  * LGPL
23657  *
23658  */
23659  
23660 /**
23661  * @class Roo.HtmlEditorCore
23662  * @extends Roo.Component
23663  * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
23664  *
23665  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
23666  */
23667
23668 Roo.HtmlEditorCore = function(config){
23669     
23670     
23671     Roo.HtmlEditorCore.superclass.constructor.call(this, config);
23672     
23673     
23674     this.addEvents({
23675         /**
23676          * @event initialize
23677          * Fires when the editor is fully initialized (including the iframe)
23678          * @param {Roo.HtmlEditorCore} this
23679          */
23680         initialize: true,
23681         /**
23682          * @event activate
23683          * Fires when the editor is first receives the focus. Any insertion must wait
23684          * until after this event.
23685          * @param {Roo.HtmlEditorCore} this
23686          */
23687         activate: true,
23688          /**
23689          * @event beforesync
23690          * Fires before the textarea is updated with content from the editor iframe. Return false
23691          * to cancel the sync.
23692          * @param {Roo.HtmlEditorCore} this
23693          * @param {String} html
23694          */
23695         beforesync: true,
23696          /**
23697          * @event beforepush
23698          * Fires before the iframe editor is updated with content from the textarea. Return false
23699          * to cancel the push.
23700          * @param {Roo.HtmlEditorCore} this
23701          * @param {String} html
23702          */
23703         beforepush: true,
23704          /**
23705          * @event sync
23706          * Fires when the textarea is updated with content from the editor iframe.
23707          * @param {Roo.HtmlEditorCore} this
23708          * @param {String} html
23709          */
23710         sync: true,
23711          /**
23712          * @event push
23713          * Fires when the iframe editor is updated with content from the textarea.
23714          * @param {Roo.HtmlEditorCore} this
23715          * @param {String} html
23716          */
23717         push: true,
23718         
23719         /**
23720          * @event editorevent
23721          * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
23722          * @param {Roo.HtmlEditorCore} this
23723          */
23724         editorevent: true
23725         
23726     });
23727     
23728     // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
23729     
23730     // defaults : white / black...
23731     this.applyBlacklists();
23732     
23733     
23734     
23735 };
23736
23737
23738 Roo.extend(Roo.HtmlEditorCore, Roo.Component,  {
23739
23740
23741      /**
23742      * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field 
23743      */
23744     
23745     owner : false,
23746     
23747      /**
23748      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
23749      *                        Roo.resizable.
23750      */
23751     resizable : false,
23752      /**
23753      * @cfg {Number} height (in pixels)
23754      */   
23755     height: 300,
23756    /**
23757      * @cfg {Number} width (in pixels)
23758      */   
23759     width: 500,
23760     
23761     /**
23762      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
23763      * 
23764      */
23765     stylesheets: false,
23766     
23767     // id of frame..
23768     frameId: false,
23769     
23770     // private properties
23771     validationEvent : false,
23772     deferHeight: true,
23773     initialized : false,
23774     activated : false,
23775     sourceEditMode : false,
23776     onFocus : Roo.emptyFn,
23777     iframePad:3,
23778     hideMode:'offsets',
23779     
23780     clearUp: true,
23781     
23782     // blacklist + whitelisted elements..
23783     black: false,
23784     white: false,
23785      
23786     bodyCls : '',
23787
23788     /**
23789      * Protected method that will not generally be called directly. It
23790      * is called when the editor initializes the iframe with HTML contents. Override this method if you
23791      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
23792      */
23793     getDocMarkup : function(){
23794         // body styles..
23795         var st = '';
23796         
23797         // inherit styels from page...?? 
23798         if (this.stylesheets === false) {
23799             
23800             Roo.get(document.head).select('style').each(function(node) {
23801                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
23802             });
23803             
23804             Roo.get(document.head).select('link').each(function(node) { 
23805                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
23806             });
23807             
23808         } else if (!this.stylesheets.length) {
23809                 // simple..
23810                 st = '<style type="text/css">' +
23811                     'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
23812                    '</style>';
23813         } else {
23814             for (var i in this.stylesheets) { 
23815                 st += '<link rel="stylesheet" href="' + this.stylesheets[i] +'" type="text/css">';
23816             }
23817             
23818         }
23819         
23820         st +=  '<style type="text/css">' +
23821             'IMG { cursor: pointer } ' +
23822         '</style>';
23823
23824         var cls = 'roo-htmleditor-body';
23825         
23826         if(this.bodyCls.length){
23827             cls += ' ' + this.bodyCls;
23828         }
23829         
23830         return '<html><head>' + st  +
23831             //<style type="text/css">' +
23832             //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
23833             //'</style>' +
23834             ' </head><body contenteditable="true" data-enable-grammerly="true" class="' +  cls + '"></body></html>';
23835     },
23836
23837     // private
23838     onRender : function(ct, position)
23839     {
23840         var _t = this;
23841         //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
23842         this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
23843         
23844         
23845         this.el.dom.style.border = '0 none';
23846         this.el.dom.setAttribute('tabIndex', -1);
23847         this.el.addClass('x-hidden hide');
23848         
23849         
23850         
23851         if(Roo.isIE){ // fix IE 1px bogus margin
23852             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
23853         }
23854        
23855         
23856         this.frameId = Roo.id();
23857         
23858          
23859         
23860         var iframe = this.owner.wrap.createChild({
23861             tag: 'iframe',
23862             cls: 'form-control', // bootstrap..
23863             id: this.frameId,
23864             name: this.frameId,
23865             frameBorder : 'no',
23866             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
23867         }, this.el
23868         );
23869         
23870         
23871         this.iframe = iframe.dom;
23872
23873          this.assignDocWin();
23874         
23875         this.doc.designMode = 'on';
23876        
23877         this.doc.open();
23878         this.doc.write(this.getDocMarkup());
23879         this.doc.close();
23880
23881         
23882         var task = { // must defer to wait for browser to be ready
23883             run : function(){
23884                 //console.log("run task?" + this.doc.readyState);
23885                 this.assignDocWin();
23886                 if(this.doc.body || this.doc.readyState == 'complete'){
23887                     try {
23888                         this.doc.designMode="on";
23889                     } catch (e) {
23890                         return;
23891                     }
23892                     Roo.TaskMgr.stop(task);
23893                     this.initEditor.defer(10, this);
23894                 }
23895             },
23896             interval : 10,
23897             duration: 10000,
23898             scope: this
23899         };
23900         Roo.TaskMgr.start(task);
23901
23902     },
23903
23904     // private
23905     onResize : function(w, h)
23906     {
23907          Roo.log('resize: ' +w + ',' + h );
23908         //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
23909         if(!this.iframe){
23910             return;
23911         }
23912         if(typeof w == 'number'){
23913             
23914             this.iframe.style.width = w + 'px';
23915         }
23916         if(typeof h == 'number'){
23917             
23918             this.iframe.style.height = h + 'px';
23919             if(this.doc){
23920                 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
23921             }
23922         }
23923         
23924     },
23925
23926     /**
23927      * Toggles the editor between standard and source edit mode.
23928      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
23929      */
23930     toggleSourceEdit : function(sourceEditMode){
23931         
23932         this.sourceEditMode = sourceEditMode === true;
23933         
23934         if(this.sourceEditMode){
23935  
23936             Roo.get(this.iframe).addClass(['x-hidden','hide']);     //FIXME - what's the BS styles for these
23937             
23938         }else{
23939             Roo.get(this.iframe).removeClass(['x-hidden','hide']);
23940             //this.iframe.className = '';
23941             this.deferFocus();
23942         }
23943         //this.setSize(this.owner.wrap.getSize());
23944         //this.fireEvent('editmodechange', this, this.sourceEditMode);
23945     },
23946
23947     
23948   
23949
23950     /**
23951      * Protected method that will not generally be called directly. If you need/want
23952      * custom HTML cleanup, this is the method you should override.
23953      * @param {String} html The HTML to be cleaned
23954      * return {String} The cleaned HTML
23955      */
23956     cleanHtml : function(html){
23957         html = String(html);
23958         if(html.length > 5){
23959             if(Roo.isSafari){ // strip safari nonsense
23960                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
23961             }
23962         }
23963         if(html == '&nbsp;'){
23964             html = '';
23965         }
23966         return html;
23967     },
23968
23969     /**
23970      * HTML Editor -> Textarea
23971      * Protected method that will not generally be called directly. Syncs the contents
23972      * of the editor iframe with the textarea.
23973      */
23974     syncValue : function(){
23975         if(this.initialized){
23976             var bd = (this.doc.body || this.doc.documentElement);
23977             //this.cleanUpPaste(); -- this is done else where and causes havoc..
23978             var html = bd.innerHTML;
23979             if(Roo.isSafari){
23980                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
23981                 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
23982                 if(m && m[1]){
23983                     html = '<div style="'+m[0]+'">' + html + '</div>';
23984                 }
23985             }
23986             html = this.cleanHtml(html);
23987             // fix up the special chars.. normaly like back quotes in word...
23988             // however we do not want to do this with chinese..
23989             html = html.replace(/[\uD800-\uDBFF][\uDC00-\uDFFF]|[\u0080-\uFFFF]/g, function(match) {
23990                 
23991                 var cc = match.charCodeAt();
23992
23993                 // Get the character value, handling surrogate pairs
23994                 if (match.length == 2) {
23995                     // It's a surrogate pair, calculate the Unicode code point
23996                     var high = match.charCodeAt(0) - 0xD800;
23997                     var low  = match.charCodeAt(1) - 0xDC00;
23998                     cc = (high * 0x400) + low + 0x10000;
23999                 }  else if (
24000                     (cc >= 0x4E00 && cc < 0xA000 ) ||
24001                     (cc >= 0x3400 && cc < 0x4E00 ) ||
24002                     (cc >= 0xf900 && cc < 0xfb00 )
24003                 ) {
24004                         return match;
24005                 }  
24006          
24007                 // No, use a numeric entity. Here we brazenly (and possibly mistakenly)
24008                 return "&#" + cc + ";";
24009                 
24010                 
24011             });
24012             
24013             
24014              
24015             if(this.owner.fireEvent('beforesync', this, html) !== false){
24016                 this.el.dom.value = html;
24017                 this.owner.fireEvent('sync', this, html);
24018             }
24019         }
24020     },
24021
24022     /**
24023      * Protected method that will not generally be called directly. Pushes the value of the textarea
24024      * into the iframe editor.
24025      */
24026     pushValue : function(){
24027         if(this.initialized){
24028             var v = this.el.dom.value.trim();
24029             
24030 //            if(v.length < 1){
24031 //                v = '&#160;';
24032 //            }
24033             
24034             if(this.owner.fireEvent('beforepush', this, v) !== false){
24035                 var d = (this.doc.body || this.doc.documentElement);
24036                 d.innerHTML = v;
24037                 this.cleanUpPaste();
24038                 this.el.dom.value = d.innerHTML;
24039                 this.owner.fireEvent('push', this, v);
24040             }
24041         }
24042     },
24043
24044     // private
24045     deferFocus : function(){
24046         this.focus.defer(10, this);
24047     },
24048
24049     // doc'ed in Field
24050     focus : function(){
24051         if(this.win && !this.sourceEditMode){
24052             this.win.focus();
24053         }else{
24054             this.el.focus();
24055         }
24056     },
24057     
24058     assignDocWin: function()
24059     {
24060         var iframe = this.iframe;
24061         
24062          if(Roo.isIE){
24063             this.doc = iframe.contentWindow.document;
24064             this.win = iframe.contentWindow;
24065         } else {
24066 //            if (!Roo.get(this.frameId)) {
24067 //                return;
24068 //            }
24069 //            this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
24070 //            this.win = Roo.get(this.frameId).dom.contentWindow;
24071             
24072             if (!Roo.get(this.frameId) && !iframe.contentDocument) {
24073                 return;
24074             }
24075             
24076             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
24077             this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
24078         }
24079     },
24080     
24081     // private
24082     initEditor : function(){
24083         //console.log("INIT EDITOR");
24084         this.assignDocWin();
24085         
24086         
24087         
24088         this.doc.designMode="on";
24089         this.doc.open();
24090         this.doc.write(this.getDocMarkup());
24091         this.doc.close();
24092         
24093         var dbody = (this.doc.body || this.doc.documentElement);
24094         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
24095         // this copies styles from the containing element into thsi one..
24096         // not sure why we need all of this..
24097         //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
24098         
24099         //var ss = this.el.getStyles( 'background-image', 'background-repeat');
24100         //ss['background-attachment'] = 'fixed'; // w3c
24101         dbody.bgProperties = 'fixed'; // ie
24102         //Roo.DomHelper.applyStyles(dbody, ss);
24103         Roo.EventManager.on(this.doc, {
24104             //'mousedown': this.onEditorEvent,
24105             'mouseup': this.onEditorEvent,
24106             'dblclick': this.onEditorEvent,
24107             'click': this.onEditorEvent,
24108             'keyup': this.onEditorEvent,
24109             buffer:100,
24110             scope: this
24111         });
24112         if(Roo.isGecko){
24113             Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
24114         }
24115         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
24116             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
24117         }
24118         this.initialized = true;
24119
24120         this.owner.fireEvent('initialize', this);
24121         this.pushValue();
24122     },
24123
24124     // private
24125     onDestroy : function(){
24126         
24127         
24128         
24129         if(this.rendered){
24130             
24131             //for (var i =0; i < this.toolbars.length;i++) {
24132             //    // fixme - ask toolbars for heights?
24133             //    this.toolbars[i].onDestroy();
24134            // }
24135             
24136             //this.wrap.dom.innerHTML = '';
24137             //this.wrap.remove();
24138         }
24139     },
24140
24141     // private
24142     onFirstFocus : function(){
24143         
24144         this.assignDocWin();
24145         
24146         
24147         this.activated = true;
24148          
24149     
24150         if(Roo.isGecko){ // prevent silly gecko errors
24151             this.win.focus();
24152             var s = this.win.getSelection();
24153             if(!s.focusNode || s.focusNode.nodeType != 3){
24154                 var r = s.getRangeAt(0);
24155                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
24156                 r.collapse(true);
24157                 this.deferFocus();
24158             }
24159             try{
24160                 this.execCmd('useCSS', true);
24161                 this.execCmd('styleWithCSS', false);
24162             }catch(e){}
24163         }
24164         this.owner.fireEvent('activate', this);
24165     },
24166
24167     // private
24168     adjustFont: function(btn){
24169         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
24170         //if(Roo.isSafari){ // safari
24171         //    adjust *= 2;
24172        // }
24173         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
24174         if(Roo.isSafari){ // safari
24175             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
24176             v =  (v < 10) ? 10 : v;
24177             v =  (v > 48) ? 48 : v;
24178             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
24179             
24180         }
24181         
24182         
24183         v = Math.max(1, v+adjust);
24184         
24185         this.execCmd('FontSize', v  );
24186     },
24187
24188     onEditorEvent : function(e)
24189     {
24190         this.owner.fireEvent('editorevent', this, e);
24191       //  this.updateToolbar();
24192         this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
24193     },
24194
24195     insertTag : function(tg)
24196     {
24197         // could be a bit smarter... -> wrap the current selected tRoo..
24198         if (tg.toLowerCase() == 'span' ||
24199             tg.toLowerCase() == 'code' ||
24200             tg.toLowerCase() == 'sup' ||
24201             tg.toLowerCase() == 'sub' 
24202             ) {
24203             
24204             range = this.createRange(this.getSelection());
24205             var wrappingNode = this.doc.createElement(tg.toLowerCase());
24206             wrappingNode.appendChild(range.extractContents());
24207             range.insertNode(wrappingNode);
24208
24209             return;
24210             
24211             
24212             
24213         }
24214         this.execCmd("formatblock",   tg);
24215         
24216     },
24217     
24218     insertText : function(txt)
24219     {
24220         
24221         
24222         var range = this.createRange();
24223         range.deleteContents();
24224                //alert(Sender.getAttribute('label'));
24225                
24226         range.insertNode(this.doc.createTextNode(txt));
24227     } ,
24228     
24229      
24230
24231     /**
24232      * Executes a Midas editor command on the editor document and performs necessary focus and
24233      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
24234      * @param {String} cmd The Midas command
24235      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
24236      */
24237     relayCmd : function(cmd, value){
24238         this.win.focus();
24239         this.execCmd(cmd, value);
24240         this.owner.fireEvent('editorevent', this);
24241         //this.updateToolbar();
24242         this.owner.deferFocus();
24243     },
24244
24245     /**
24246      * Executes a Midas editor command directly on the editor document.
24247      * For visual commands, you should use {@link #relayCmd} instead.
24248      * <b>This should only be called after the editor is initialized.</b>
24249      * @param {String} cmd The Midas command
24250      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
24251      */
24252     execCmd : function(cmd, value){
24253         this.doc.execCommand(cmd, false, value === undefined ? null : value);
24254         this.syncValue();
24255     },
24256  
24257  
24258    
24259     /**
24260      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
24261      * to insert tRoo.
24262      * @param {String} text | dom node.. 
24263      */
24264     insertAtCursor : function(text)
24265     {
24266         
24267         if(!this.activated){
24268             return;
24269         }
24270         /*
24271         if(Roo.isIE){
24272             this.win.focus();
24273             var r = this.doc.selection.createRange();
24274             if(r){
24275                 r.collapse(true);
24276                 r.pasteHTML(text);
24277                 this.syncValue();
24278                 this.deferFocus();
24279             
24280             }
24281             return;
24282         }
24283         */
24284         if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
24285             this.win.focus();
24286             
24287             
24288             // from jquery ui (MIT licenced)
24289             var range, node;
24290             var win = this.win;
24291             
24292             if (win.getSelection && win.getSelection().getRangeAt) {
24293                 range = win.getSelection().getRangeAt(0);
24294                 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
24295                 range.insertNode(node);
24296             } else if (win.document.selection && win.document.selection.createRange) {
24297                 // no firefox support
24298                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
24299                 win.document.selection.createRange().pasteHTML(txt);
24300             } else {
24301                 // no firefox support
24302                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
24303                 this.execCmd('InsertHTML', txt);
24304             } 
24305             
24306             this.syncValue();
24307             
24308             this.deferFocus();
24309         }
24310     },
24311  // private
24312     mozKeyPress : function(e){
24313         if(e.ctrlKey){
24314             var c = e.getCharCode(), cmd;
24315           
24316             if(c > 0){
24317                 c = String.fromCharCode(c).toLowerCase();
24318                 switch(c){
24319                     case 'b':
24320                         cmd = 'bold';
24321                         break;
24322                     case 'i':
24323                         cmd = 'italic';
24324                         break;
24325                     
24326                     case 'u':
24327                         cmd = 'underline';
24328                         break;
24329                     
24330                     case 'v':
24331                         this.cleanUpPaste.defer(100, this);
24332                         return;
24333                         
24334                 }
24335                 if(cmd){
24336                     this.win.focus();
24337                     this.execCmd(cmd);
24338                     this.deferFocus();
24339                     e.preventDefault();
24340                 }
24341                 
24342             }
24343         }
24344     },
24345
24346     // private
24347     fixKeys : function(){ // load time branching for fastest keydown performance
24348         if(Roo.isIE){
24349             return function(e){
24350                 var k = e.getKey(), r;
24351                 if(k == e.TAB){
24352                     e.stopEvent();
24353                     r = this.doc.selection.createRange();
24354                     if(r){
24355                         r.collapse(true);
24356                         r.pasteHTML('&#160;&#160;&#160;&#160;');
24357                         this.deferFocus();
24358                     }
24359                     return;
24360                 }
24361                 
24362                 if(k == e.ENTER){
24363                     r = this.doc.selection.createRange();
24364                     if(r){
24365                         var target = r.parentElement();
24366                         if(!target || target.tagName.toLowerCase() != 'li'){
24367                             e.stopEvent();
24368                             r.pasteHTML('<br />');
24369                             r.collapse(false);
24370                             r.select();
24371                         }
24372                     }
24373                 }
24374                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
24375                     this.cleanUpPaste.defer(100, this);
24376                     return;
24377                 }
24378                 
24379                 
24380             };
24381         }else if(Roo.isOpera){
24382             return function(e){
24383                 var k = e.getKey();
24384                 if(k == e.TAB){
24385                     e.stopEvent();
24386                     this.win.focus();
24387                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
24388                     this.deferFocus();
24389                 }
24390                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
24391                     this.cleanUpPaste.defer(100, this);
24392                     return;
24393                 }
24394                 
24395             };
24396         }else if(Roo.isSafari){
24397             return function(e){
24398                 var k = e.getKey();
24399                 
24400                 if(k == e.TAB){
24401                     e.stopEvent();
24402                     this.execCmd('InsertText','\t');
24403                     this.deferFocus();
24404                     return;
24405                 }
24406                if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
24407                     this.cleanUpPaste.defer(100, this);
24408                     return;
24409                 }
24410                 
24411              };
24412         }
24413     }(),
24414     
24415     getAllAncestors: function()
24416     {
24417         var p = this.getSelectedNode();
24418         var a = [];
24419         if (!p) {
24420             a.push(p); // push blank onto stack..
24421             p = this.getParentElement();
24422         }
24423         
24424         
24425         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
24426             a.push(p);
24427             p = p.parentNode;
24428         }
24429         a.push(this.doc.body);
24430         return a;
24431     },
24432     lastSel : false,
24433     lastSelNode : false,
24434     
24435     
24436     getSelection : function() 
24437     {
24438         this.assignDocWin();
24439         return Roo.isIE ? this.doc.selection : this.win.getSelection();
24440     },
24441     
24442     getSelectedNode: function() 
24443     {
24444         // this may only work on Gecko!!!
24445         
24446         // should we cache this!!!!
24447         
24448         
24449         
24450          
24451         var range = this.createRange(this.getSelection()).cloneRange();
24452         
24453         if (Roo.isIE) {
24454             var parent = range.parentElement();
24455             while (true) {
24456                 var testRange = range.duplicate();
24457                 testRange.moveToElementText(parent);
24458                 if (testRange.inRange(range)) {
24459                     break;
24460                 }
24461                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
24462                     break;
24463                 }
24464                 parent = parent.parentElement;
24465             }
24466             return parent;
24467         }
24468         
24469         // is ancestor a text element.
24470         var ac =  range.commonAncestorContainer;
24471         if (ac.nodeType == 3) {
24472             ac = ac.parentNode;
24473         }
24474         
24475         var ar = ac.childNodes;
24476          
24477         var nodes = [];
24478         var other_nodes = [];
24479         var has_other_nodes = false;
24480         for (var i=0;i<ar.length;i++) {
24481             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
24482                 continue;
24483             }
24484             // fullly contained node.
24485             
24486             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
24487                 nodes.push(ar[i]);
24488                 continue;
24489             }
24490             
24491             // probably selected..
24492             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
24493                 other_nodes.push(ar[i]);
24494                 continue;
24495             }
24496             // outer..
24497             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
24498                 continue;
24499             }
24500             
24501             
24502             has_other_nodes = true;
24503         }
24504         if (!nodes.length && other_nodes.length) {
24505             nodes= other_nodes;
24506         }
24507         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
24508             return false;
24509         }
24510         
24511         return nodes[0];
24512     },
24513     createRange: function(sel)
24514     {
24515         // this has strange effects when using with 
24516         // top toolbar - not sure if it's a great idea.
24517         //this.editor.contentWindow.focus();
24518         if (typeof sel != "undefined") {
24519             try {
24520                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
24521             } catch(e) {
24522                 return this.doc.createRange();
24523             }
24524         } else {
24525             return this.doc.createRange();
24526         }
24527     },
24528     getParentElement: function()
24529     {
24530         
24531         this.assignDocWin();
24532         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
24533         
24534         var range = this.createRange(sel);
24535          
24536         try {
24537             var p = range.commonAncestorContainer;
24538             while (p.nodeType == 3) { // text node
24539                 p = p.parentNode;
24540             }
24541             return p;
24542         } catch (e) {
24543             return null;
24544         }
24545     
24546     },
24547     /***
24548      *
24549      * Range intersection.. the hard stuff...
24550      *  '-1' = before
24551      *  '0' = hits..
24552      *  '1' = after.
24553      *         [ -- selected range --- ]
24554      *   [fail]                        [fail]
24555      *
24556      *    basically..
24557      *      if end is before start or  hits it. fail.
24558      *      if start is after end or hits it fail.
24559      *
24560      *   if either hits (but other is outside. - then it's not 
24561      *   
24562      *    
24563      **/
24564     
24565     
24566     // @see http://www.thismuchiknow.co.uk/?p=64.
24567     rangeIntersectsNode : function(range, node)
24568     {
24569         var nodeRange = node.ownerDocument.createRange();
24570         try {
24571             nodeRange.selectNode(node);
24572         } catch (e) {
24573             nodeRange.selectNodeContents(node);
24574         }
24575     
24576         var rangeStartRange = range.cloneRange();
24577         rangeStartRange.collapse(true);
24578     
24579         var rangeEndRange = range.cloneRange();
24580         rangeEndRange.collapse(false);
24581     
24582         var nodeStartRange = nodeRange.cloneRange();
24583         nodeStartRange.collapse(true);
24584     
24585         var nodeEndRange = nodeRange.cloneRange();
24586         nodeEndRange.collapse(false);
24587     
24588         return rangeStartRange.compareBoundaryPoints(
24589                  Range.START_TO_START, nodeEndRange) == -1 &&
24590                rangeEndRange.compareBoundaryPoints(
24591                  Range.START_TO_START, nodeStartRange) == 1;
24592         
24593          
24594     },
24595     rangeCompareNode : function(range, node)
24596     {
24597         var nodeRange = node.ownerDocument.createRange();
24598         try {
24599             nodeRange.selectNode(node);
24600         } catch (e) {
24601             nodeRange.selectNodeContents(node);
24602         }
24603         
24604         
24605         range.collapse(true);
24606     
24607         nodeRange.collapse(true);
24608      
24609         var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
24610         var ee = range.compareBoundaryPoints(  Range.END_TO_END, nodeRange);
24611          
24612         //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
24613         
24614         var nodeIsBefore   =  ss == 1;
24615         var nodeIsAfter    = ee == -1;
24616         
24617         if (nodeIsBefore && nodeIsAfter) {
24618             return 0; // outer
24619         }
24620         if (!nodeIsBefore && nodeIsAfter) {
24621             return 1; //right trailed.
24622         }
24623         
24624         if (nodeIsBefore && !nodeIsAfter) {
24625             return 2;  // left trailed.
24626         }
24627         // fully contined.
24628         return 3;
24629     },
24630
24631     // private? - in a new class?
24632     cleanUpPaste :  function()
24633     {
24634         // cleans up the whole document..
24635         Roo.log('cleanuppaste');
24636         
24637         this.cleanUpChildren(this.doc.body);
24638         var clean = this.cleanWordChars(this.doc.body.innerHTML);
24639         if (clean != this.doc.body.innerHTML) {
24640             this.doc.body.innerHTML = clean;
24641         }
24642         
24643     },
24644     
24645     cleanWordChars : function(input) {// change the chars to hex code
24646         var he = Roo.HtmlEditorCore;
24647         
24648         var output = input;
24649         Roo.each(he.swapCodes, function(sw) { 
24650             var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
24651             
24652             output = output.replace(swapper, sw[1]);
24653         });
24654         
24655         return output;
24656     },
24657     
24658     
24659     cleanUpChildren : function (n)
24660     {
24661         if (!n.childNodes.length) {
24662             return;
24663         }
24664         for (var i = n.childNodes.length-1; i > -1 ; i--) {
24665            this.cleanUpChild(n.childNodes[i]);
24666         }
24667     },
24668     
24669     
24670         
24671     
24672     cleanUpChild : function (node)
24673     {
24674         var ed = this;
24675         //console.log(node);
24676         if (node.nodeName == "#text") {
24677             // clean up silly Windows -- stuff?
24678             return; 
24679         }
24680         if (node.nodeName == "#comment") {
24681             node.parentNode.removeChild(node);
24682             // clean up silly Windows -- stuff?
24683             return; 
24684         }
24685         var lcname = node.tagName.toLowerCase();
24686         // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
24687         // whitelist of tags..
24688         
24689         if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
24690             // remove node.
24691             node.parentNode.removeChild(node);
24692             return;
24693             
24694         }
24695         
24696         var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
24697         
24698         // spans with no attributes - just remove them..
24699         if ((!node.attributes || !node.attributes.length) && lcname == 'span') { 
24700             remove_keep_children = true;
24701         }
24702         
24703         // remove <a name=....> as rendering on yahoo mailer is borked with this.
24704         // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
24705         
24706         //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
24707         //    remove_keep_children = true;
24708         //}
24709         
24710         if (remove_keep_children) {
24711             this.cleanUpChildren(node);
24712             // inserts everything just before this node...
24713             while (node.childNodes.length) {
24714                 var cn = node.childNodes[0];
24715                 node.removeChild(cn);
24716                 node.parentNode.insertBefore(cn, node);
24717             }
24718             node.parentNode.removeChild(node);
24719             return;
24720         }
24721         
24722         if (!node.attributes || !node.attributes.length) {
24723             
24724           
24725             
24726             
24727             this.cleanUpChildren(node);
24728             return;
24729         }
24730         
24731         function cleanAttr(n,v)
24732         {
24733             
24734             if (v.match(/^\./) || v.match(/^\//)) {
24735                 return;
24736             }
24737             if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/) || v.match(/^ftp:/)) {
24738                 return;
24739             }
24740             if (v.match(/^#/)) {
24741                 return;
24742             }
24743             if (v.match(/^\{/)) { // allow template editing.
24744                 return;
24745             }
24746 //            Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
24747             node.removeAttribute(n);
24748             
24749         }
24750         
24751         var cwhite = this.cwhite;
24752         var cblack = this.cblack;
24753             
24754         function cleanStyle(n,v)
24755         {
24756             if (v.match(/expression/)) { //XSS?? should we even bother..
24757                 node.removeAttribute(n);
24758                 return;
24759             }
24760             
24761             var parts = v.split(/;/);
24762             var clean = [];
24763             
24764             Roo.each(parts, function(p) {
24765                 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
24766                 if (!p.length) {
24767                     return true;
24768                 }
24769                 var l = p.split(':').shift().replace(/\s+/g,'');
24770                 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
24771                 
24772                 if ( cwhite.length && cblack.indexOf(l) > -1) {
24773 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
24774                     //node.removeAttribute(n);
24775                     return true;
24776                 }
24777                 //Roo.log()
24778                 // only allow 'c whitelisted system attributes'
24779                 if ( cwhite.length &&  cwhite.indexOf(l) < 0) {
24780 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
24781                     //node.removeAttribute(n);
24782                     return true;
24783                 }
24784                 
24785                 
24786                  
24787                 
24788                 clean.push(p);
24789                 return true;
24790             });
24791             if (clean.length) { 
24792                 node.setAttribute(n, clean.join(';'));
24793             } else {
24794                 node.removeAttribute(n);
24795             }
24796             
24797         }
24798         
24799         
24800         for (var i = node.attributes.length-1; i > -1 ; i--) {
24801             var a = node.attributes[i];
24802             //console.log(a);
24803             
24804             if (a.name.toLowerCase().substr(0,2)=='on')  {
24805                 node.removeAttribute(a.name);
24806                 continue;
24807             }
24808             if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
24809                 node.removeAttribute(a.name);
24810                 continue;
24811             }
24812             if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
24813                 cleanAttr(a.name,a.value); // fixme..
24814                 continue;
24815             }
24816             if (a.name == 'style') {
24817                 cleanStyle(a.name,a.value);
24818                 continue;
24819             }
24820             /// clean up MS crap..
24821             // tecnically this should be a list of valid class'es..
24822             
24823             
24824             if (a.name == 'class') {
24825                 if (a.value.match(/^Mso/)) {
24826                     node.removeAttribute('class');
24827                 }
24828                 
24829                 if (a.value.match(/^body$/)) {
24830                     node.removeAttribute('class');
24831                 }
24832                 continue;
24833             }
24834             
24835             // style cleanup!?
24836             // class cleanup?
24837             
24838         }
24839         
24840         
24841         this.cleanUpChildren(node);
24842         
24843         
24844     },
24845     
24846     /**
24847      * Clean up MS wordisms...
24848      */
24849     cleanWord : function(node)
24850     {
24851         if (!node) {
24852             this.cleanWord(this.doc.body);
24853             return;
24854         }
24855         
24856         if(
24857                 node.nodeName == 'SPAN' &&
24858                 !node.hasAttributes() &&
24859                 node.childNodes.length == 1 &&
24860                 node.firstChild.nodeName == "#text"  
24861         ) {
24862             var textNode = node.firstChild;
24863             node.removeChild(textNode);
24864             if (node.getAttribute('lang') != 'zh-CN') {   // do not space pad on chinese characters..
24865                 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" "), node);
24866             }
24867             node.parentNode.insertBefore(textNode, node);
24868             if (node.getAttribute('lang') != 'zh-CN') {   // do not space pad on chinese characters..
24869                 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" ") , node);
24870             }
24871             node.parentNode.removeChild(node);
24872         }
24873         
24874         if (node.nodeName == "#text") {
24875             // clean up silly Windows -- stuff?
24876             return; 
24877         }
24878         if (node.nodeName == "#comment") {
24879             node.parentNode.removeChild(node);
24880             // clean up silly Windows -- stuff?
24881             return; 
24882         }
24883         
24884         if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
24885             node.parentNode.removeChild(node);
24886             return;
24887         }
24888         //Roo.log(node.tagName);
24889         // remove - but keep children..
24890         if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|v:|font)/)) {
24891             //Roo.log('-- removed');
24892             while (node.childNodes.length) {
24893                 var cn = node.childNodes[0];
24894                 node.removeChild(cn);
24895                 node.parentNode.insertBefore(cn, node);
24896                 // move node to parent - and clean it..
24897                 this.cleanWord(cn);
24898             }
24899             node.parentNode.removeChild(node);
24900             /// no need to iterate chidlren = it's got none..
24901             //this.iterateChildren(node, this.cleanWord);
24902             return;
24903         }
24904         // clean styles
24905         if (node.className.length) {
24906             
24907             var cn = node.className.split(/\W+/);
24908             var cna = [];
24909             Roo.each(cn, function(cls) {
24910                 if (cls.match(/Mso[a-zA-Z]+/)) {
24911                     return;
24912                 }
24913                 cna.push(cls);
24914             });
24915             node.className = cna.length ? cna.join(' ') : '';
24916             if (!cna.length) {
24917                 node.removeAttribute("class");
24918             }
24919         }
24920         
24921         if (node.hasAttribute("lang")) {
24922             node.removeAttribute("lang");
24923         }
24924         
24925         if (node.hasAttribute("style")) {
24926             
24927             var styles = node.getAttribute("style").split(";");
24928             var nstyle = [];
24929             Roo.each(styles, function(s) {
24930                 if (!s.match(/:/)) {
24931                     return;
24932                 }
24933                 var kv = s.split(":");
24934                 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
24935                     return;
24936                 }
24937                 // what ever is left... we allow.
24938                 nstyle.push(s);
24939             });
24940             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
24941             if (!nstyle.length) {
24942                 node.removeAttribute('style');
24943             }
24944         }
24945         this.iterateChildren(node, this.cleanWord);
24946         
24947         
24948         
24949     },
24950     /**
24951      * iterateChildren of a Node, calling fn each time, using this as the scole..
24952      * @param {DomNode} node node to iterate children of.
24953      * @param {Function} fn method of this class to call on each item.
24954      */
24955     iterateChildren : function(node, fn)
24956     {
24957         if (!node.childNodes.length) {
24958                 return;
24959         }
24960         for (var i = node.childNodes.length-1; i > -1 ; i--) {
24961            fn.call(this, node.childNodes[i])
24962         }
24963     },
24964     
24965     
24966     /**
24967      * cleanTableWidths.
24968      *
24969      * Quite often pasting from word etc.. results in tables with column and widths.
24970      * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
24971      *
24972      */
24973     cleanTableWidths : function(node)
24974     {
24975          
24976          
24977         if (!node) {
24978             this.cleanTableWidths(this.doc.body);
24979             return;
24980         }
24981         
24982         // ignore list...
24983         if (node.nodeName == "#text" || node.nodeName == "#comment") {
24984             return; 
24985         }
24986         Roo.log(node.tagName);
24987         if (!node.tagName.toLowerCase().match(/^(table|td|tr)$/)) {
24988             this.iterateChildren(node, this.cleanTableWidths);
24989             return;
24990         }
24991         if (node.hasAttribute('width')) {
24992             node.removeAttribute('width');
24993         }
24994         
24995          
24996         if (node.hasAttribute("style")) {
24997             // pretty basic...
24998             
24999             var styles = node.getAttribute("style").split(";");
25000             var nstyle = [];
25001             Roo.each(styles, function(s) {
25002                 if (!s.match(/:/)) {
25003                     return;
25004                 }
25005                 var kv = s.split(":");
25006                 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
25007                     return;
25008                 }
25009                 // what ever is left... we allow.
25010                 nstyle.push(s);
25011             });
25012             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
25013             if (!nstyle.length) {
25014                 node.removeAttribute('style');
25015             }
25016         }
25017         
25018         this.iterateChildren(node, this.cleanTableWidths);
25019         
25020         
25021     },
25022     
25023     
25024     
25025     
25026     domToHTML : function(currentElement, depth, nopadtext) {
25027         
25028         depth = depth || 0;
25029         nopadtext = nopadtext || false;
25030     
25031         if (!currentElement) {
25032             return this.domToHTML(this.doc.body);
25033         }
25034         
25035         //Roo.log(currentElement);
25036         var j;
25037         var allText = false;
25038         var nodeName = currentElement.nodeName;
25039         var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
25040         
25041         if  (nodeName == '#text') {
25042             
25043             return nopadtext ? currentElement.nodeValue : currentElement.nodeValue.trim();
25044         }
25045         
25046         
25047         var ret = '';
25048         if (nodeName != 'BODY') {
25049              
25050             var i = 0;
25051             // Prints the node tagName, such as <A>, <IMG>, etc
25052             if (tagName) {
25053                 var attr = [];
25054                 for(i = 0; i < currentElement.attributes.length;i++) {
25055                     // quoting?
25056                     var aname = currentElement.attributes.item(i).name;
25057                     if (!currentElement.attributes.item(i).value.length) {
25058                         continue;
25059                     }
25060                     attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
25061                 }
25062                 
25063                 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
25064             } 
25065             else {
25066                 
25067                 // eack
25068             }
25069         } else {
25070             tagName = false;
25071         }
25072         if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
25073             return ret;
25074         }
25075         if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
25076             nopadtext = true;
25077         }
25078         
25079         
25080         // Traverse the tree
25081         i = 0;
25082         var currentElementChild = currentElement.childNodes.item(i);
25083         var allText = true;
25084         var innerHTML  = '';
25085         lastnode = '';
25086         while (currentElementChild) {
25087             // Formatting code (indent the tree so it looks nice on the screen)
25088             var nopad = nopadtext;
25089             if (lastnode == 'SPAN') {
25090                 nopad  = true;
25091             }
25092             // text
25093             if  (currentElementChild.nodeName == '#text') {
25094                 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
25095                 toadd = nopadtext ? toadd : toadd.trim();
25096                 if (!nopad && toadd.length > 80) {
25097                     innerHTML  += "\n" + (new Array( depth + 1 )).join( "  "  );
25098                 }
25099                 innerHTML  += toadd;
25100                 
25101                 i++;
25102                 currentElementChild = currentElement.childNodes.item(i);
25103                 lastNode = '';
25104                 continue;
25105             }
25106             allText = false;
25107             
25108             innerHTML  += nopad ? '' : "\n" + (new Array( depth + 1 )).join( "  "  );
25109                 
25110             // Recursively traverse the tree structure of the child node
25111             innerHTML   += this.domToHTML(currentElementChild, depth+1, nopadtext);
25112             lastnode = currentElementChild.nodeName;
25113             i++;
25114             currentElementChild=currentElement.childNodes.item(i);
25115         }
25116         
25117         ret += innerHTML;
25118         
25119         if (!allText) {
25120                 // The remaining code is mostly for formatting the tree
25121             ret+= nopadtext ? '' : "\n" + (new Array( depth  )).join( "  "  );
25122         }
25123         
25124         
25125         if (tagName) {
25126             ret+= "</"+tagName+">";
25127         }
25128         return ret;
25129         
25130     },
25131         
25132     applyBlacklists : function()
25133     {
25134         var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white  : [];
25135         var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black :  [];
25136         
25137         this.white = [];
25138         this.black = [];
25139         Roo.each(Roo.HtmlEditorCore.white, function(tag) {
25140             if (b.indexOf(tag) > -1) {
25141                 return;
25142             }
25143             this.white.push(tag);
25144             
25145         }, this);
25146         
25147         Roo.each(w, function(tag) {
25148             if (b.indexOf(tag) > -1) {
25149                 return;
25150             }
25151             if (this.white.indexOf(tag) > -1) {
25152                 return;
25153             }
25154             this.white.push(tag);
25155             
25156         }, this);
25157         
25158         
25159         Roo.each(Roo.HtmlEditorCore.black, function(tag) {
25160             if (w.indexOf(tag) > -1) {
25161                 return;
25162             }
25163             this.black.push(tag);
25164             
25165         }, this);
25166         
25167         Roo.each(b, function(tag) {
25168             if (w.indexOf(tag) > -1) {
25169                 return;
25170             }
25171             if (this.black.indexOf(tag) > -1) {
25172                 return;
25173             }
25174             this.black.push(tag);
25175             
25176         }, this);
25177         
25178         
25179         w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite  : [];
25180         b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack :  [];
25181         
25182         this.cwhite = [];
25183         this.cblack = [];
25184         Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
25185             if (b.indexOf(tag) > -1) {
25186                 return;
25187             }
25188             this.cwhite.push(tag);
25189             
25190         }, this);
25191         
25192         Roo.each(w, function(tag) {
25193             if (b.indexOf(tag) > -1) {
25194                 return;
25195             }
25196             if (this.cwhite.indexOf(tag) > -1) {
25197                 return;
25198             }
25199             this.cwhite.push(tag);
25200             
25201         }, this);
25202         
25203         
25204         Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
25205             if (w.indexOf(tag) > -1) {
25206                 return;
25207             }
25208             this.cblack.push(tag);
25209             
25210         }, this);
25211         
25212         Roo.each(b, function(tag) {
25213             if (w.indexOf(tag) > -1) {
25214                 return;
25215             }
25216             if (this.cblack.indexOf(tag) > -1) {
25217                 return;
25218             }
25219             this.cblack.push(tag);
25220             
25221         }, this);
25222     },
25223     
25224     setStylesheets : function(stylesheets)
25225     {
25226         if(typeof(stylesheets) == 'string'){
25227             Roo.get(this.iframe.contentDocument.head).createChild({
25228                 tag : 'link',
25229                 rel : 'stylesheet',
25230                 type : 'text/css',
25231                 href : stylesheets
25232             });
25233             
25234             return;
25235         }
25236         var _this = this;
25237      
25238         Roo.each(stylesheets, function(s) {
25239             if(!s.length){
25240                 return;
25241             }
25242             
25243             Roo.get(_this.iframe.contentDocument.head).createChild({
25244                 tag : 'link',
25245                 rel : 'stylesheet',
25246                 type : 'text/css',
25247                 href : s
25248             });
25249         });
25250
25251         
25252     },
25253     
25254     removeStylesheets : function()
25255     {
25256         var _this = this;
25257         
25258         Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
25259             s.remove();
25260         });
25261     },
25262     
25263     setStyle : function(style)
25264     {
25265         Roo.get(this.iframe.contentDocument.head).createChild({
25266             tag : 'style',
25267             type : 'text/css',
25268             html : style
25269         });
25270
25271         return;
25272     }
25273     
25274     // hide stuff that is not compatible
25275     /**
25276      * @event blur
25277      * @hide
25278      */
25279     /**
25280      * @event change
25281      * @hide
25282      */
25283     /**
25284      * @event focus
25285      * @hide
25286      */
25287     /**
25288      * @event specialkey
25289      * @hide
25290      */
25291     /**
25292      * @cfg {String} fieldClass @hide
25293      */
25294     /**
25295      * @cfg {String} focusClass @hide
25296      */
25297     /**
25298      * @cfg {String} autoCreate @hide
25299      */
25300     /**
25301      * @cfg {String} inputType @hide
25302      */
25303     /**
25304      * @cfg {String} invalidClass @hide
25305      */
25306     /**
25307      * @cfg {String} invalidText @hide
25308      */
25309     /**
25310      * @cfg {String} msgFx @hide
25311      */
25312     /**
25313      * @cfg {String} validateOnBlur @hide
25314      */
25315 });
25316
25317 Roo.HtmlEditorCore.white = [
25318         'area', 'br', 'img', 'input', 'hr', 'wbr',
25319         
25320        'address', 'blockquote', 'center', 'dd',      'dir',       'div', 
25321        'dl',      'dt',         'h1',     'h2',      'h3',        'h4', 
25322        'h5',      'h6',         'hr',     'isindex', 'listing',   'marquee', 
25323        'menu',    'multicol',   'ol',     'p',       'plaintext', 'pre', 
25324        'table',   'ul',         'xmp', 
25325        
25326        'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th', 
25327       'thead',   'tr', 
25328      
25329       'dir', 'menu', 'ol', 'ul', 'dl',
25330        
25331       'embed',  'object'
25332 ];
25333
25334
25335 Roo.HtmlEditorCore.black = [
25336     //    'embed',  'object', // enable - backend responsiblity to clean thiese
25337         'applet', // 
25338         'base',   'basefont', 'bgsound', 'blink',  'body', 
25339         'frame',  'frameset', 'head',    'html',   'ilayer', 
25340         'iframe', 'layer',  'link',     'meta',    'object',   
25341         'script', 'style' ,'title',  'xml' // clean later..
25342 ];
25343 Roo.HtmlEditorCore.clean = [
25344     'script', 'style', 'title', 'xml'
25345 ];
25346 Roo.HtmlEditorCore.remove = [
25347     'font'
25348 ];
25349 // attributes..
25350
25351 Roo.HtmlEditorCore.ablack = [
25352     'on'
25353 ];
25354     
25355 Roo.HtmlEditorCore.aclean = [ 
25356     'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc' 
25357 ];
25358
25359 // protocols..
25360 Roo.HtmlEditorCore.pwhite= [
25361         'http',  'https',  'mailto'
25362 ];
25363
25364 // white listed style attributes.
25365 Roo.HtmlEditorCore.cwhite= [
25366       //  'text-align', /// default is to allow most things..
25367       
25368          
25369 //        'font-size'//??
25370 ];
25371
25372 // black listed style attributes.
25373 Roo.HtmlEditorCore.cblack= [
25374       //  'font-size' -- this can be set by the project 
25375 ];
25376
25377
25378 Roo.HtmlEditorCore.swapCodes   =[ 
25379     [    8211, "--" ], 
25380     [    8212, "--" ], 
25381     [    8216,  "'" ],  
25382     [    8217, "'" ],  
25383     [    8220, '"' ],  
25384     [    8221, '"' ],  
25385     [    8226, "*" ],  
25386     [    8230, "..." ]
25387 ]; 
25388
25389     /*
25390  * - LGPL
25391  *
25392  * HtmlEditor
25393  * 
25394  */
25395
25396 /**
25397  * @class Roo.bootstrap.HtmlEditor
25398  * @extends Roo.bootstrap.TextArea
25399  * Bootstrap HtmlEditor class
25400
25401  * @constructor
25402  * Create a new HtmlEditor
25403  * @param {Object} config The config object
25404  */
25405
25406 Roo.bootstrap.HtmlEditor = function(config){
25407     Roo.bootstrap.HtmlEditor.superclass.constructor.call(this, config);
25408     if (!this.toolbars) {
25409         this.toolbars = [];
25410     }
25411     
25412     this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
25413     this.addEvents({
25414             /**
25415              * @event initialize
25416              * Fires when the editor is fully initialized (including the iframe)
25417              * @param {HtmlEditor} this
25418              */
25419             initialize: true,
25420             /**
25421              * @event activate
25422              * Fires when the editor is first receives the focus. Any insertion must wait
25423              * until after this event.
25424              * @param {HtmlEditor} this
25425              */
25426             activate: true,
25427              /**
25428              * @event beforesync
25429              * Fires before the textarea is updated with content from the editor iframe. Return false
25430              * to cancel the sync.
25431              * @param {HtmlEditor} this
25432              * @param {String} html
25433              */
25434             beforesync: true,
25435              /**
25436              * @event beforepush
25437              * Fires before the iframe editor is updated with content from the textarea. Return false
25438              * to cancel the push.
25439              * @param {HtmlEditor} this
25440              * @param {String} html
25441              */
25442             beforepush: true,
25443              /**
25444              * @event sync
25445              * Fires when the textarea is updated with content from the editor iframe.
25446              * @param {HtmlEditor} this
25447              * @param {String} html
25448              */
25449             sync: true,
25450              /**
25451              * @event push
25452              * Fires when the iframe editor is updated with content from the textarea.
25453              * @param {HtmlEditor} this
25454              * @param {String} html
25455              */
25456             push: true,
25457              /**
25458              * @event editmodechange
25459              * Fires when the editor switches edit modes
25460              * @param {HtmlEditor} this
25461              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
25462              */
25463             editmodechange: true,
25464             /**
25465              * @event editorevent
25466              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
25467              * @param {HtmlEditor} this
25468              */
25469             editorevent: true,
25470             /**
25471              * @event firstfocus
25472              * Fires when on first focus - needed by toolbars..
25473              * @param {HtmlEditor} this
25474              */
25475             firstfocus: true,
25476             /**
25477              * @event autosave
25478              * Auto save the htmlEditor value as a file into Events
25479              * @param {HtmlEditor} this
25480              */
25481             autosave: true,
25482             /**
25483              * @event savedpreview
25484              * preview the saved version of htmlEditor
25485              * @param {HtmlEditor} this
25486              */
25487             savedpreview: true
25488         });
25489 };
25490
25491
25492 Roo.extend(Roo.bootstrap.HtmlEditor, Roo.bootstrap.TextArea,  {
25493     
25494     
25495       /**
25496      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
25497      */
25498     toolbars : false,
25499     
25500      /**
25501     * @cfg {Array} buttons Array of toolbar's buttons. - defaults to empty
25502     */
25503     btns : [],
25504    
25505      /**
25506      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
25507      *                        Roo.resizable.
25508      */
25509     resizable : false,
25510      /**
25511      * @cfg {Number} height (in pixels)
25512      */   
25513     height: 300,
25514    /**
25515      * @cfg {Number} width (in pixels)
25516      */   
25517     width: false,
25518     
25519     /**
25520      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
25521      * 
25522      */
25523     stylesheets: false,
25524     
25525     // id of frame..
25526     frameId: false,
25527     
25528     // private properties
25529     validationEvent : false,
25530     deferHeight: true,
25531     initialized : false,
25532     activated : false,
25533     
25534     onFocus : Roo.emptyFn,
25535     iframePad:3,
25536     hideMode:'offsets',
25537     
25538     tbContainer : false,
25539     
25540     bodyCls : '',
25541     
25542     toolbarContainer :function() {
25543         return this.wrap.select('.x-html-editor-tb',true).first();
25544     },
25545
25546     /**
25547      * Protected method that will not generally be called directly. It
25548      * is called when the editor creates its toolbar. Override this method if you need to
25549      * add custom toolbar buttons.
25550      * @param {HtmlEditor} editor
25551      */
25552     createToolbar : function(){
25553         Roo.log('renewing');
25554         Roo.log("create toolbars");
25555         
25556         this.toolbars = [ new Roo.bootstrap.htmleditor.ToolbarStandard({editor: this} ) ];
25557         this.toolbars[0].render(this.toolbarContainer());
25558         
25559         return;
25560         
25561 //        if (!editor.toolbars || !editor.toolbars.length) {
25562 //            editor.toolbars = [ new Roo.bootstrap.HtmlEditor.ToolbarStandard() ]; // can be empty?
25563 //        }
25564 //        
25565 //        for (var i =0 ; i < editor.toolbars.length;i++) {
25566 //            editor.toolbars[i] = Roo.factory(
25567 //                    typeof(editor.toolbars[i]) == 'string' ?
25568 //                        { xtype: editor.toolbars[i]} : editor.toolbars[i],
25569 //                Roo.bootstrap.HtmlEditor);
25570 //            editor.toolbars[i].init(editor);
25571 //        }
25572     },
25573
25574      
25575     // private
25576     onRender : function(ct, position)
25577     {
25578        // Roo.log("Call onRender: " + this.xtype);
25579         var _t = this;
25580         Roo.bootstrap.HtmlEditor.superclass.onRender.call(this, ct, position);
25581       
25582         this.wrap = this.inputEl().wrap({
25583             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
25584         });
25585         
25586         this.editorcore.onRender(ct, position);
25587          
25588         if (this.resizable) {
25589             this.resizeEl = new Roo.Resizable(this.wrap, {
25590                 pinned : true,
25591                 wrap: true,
25592                 dynamic : true,
25593                 minHeight : this.height,
25594                 height: this.height,
25595                 handles : this.resizable,
25596                 width: this.width,
25597                 listeners : {
25598                     resize : function(r, w, h) {
25599                         _t.onResize(w,h); // -something
25600                     }
25601                 }
25602             });
25603             
25604         }
25605         this.createToolbar(this);
25606        
25607         
25608         if(!this.width && this.resizable){
25609             this.setSize(this.wrap.getSize());
25610         }
25611         if (this.resizeEl) {
25612             this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
25613             // should trigger onReize..
25614         }
25615         
25616     },
25617
25618     // private
25619     onResize : function(w, h)
25620     {
25621         Roo.log('resize: ' +w + ',' + h );
25622         Roo.bootstrap.HtmlEditor.superclass.onResize.apply(this, arguments);
25623         var ew = false;
25624         var eh = false;
25625         
25626         if(this.inputEl() ){
25627             if(typeof w == 'number'){
25628                 var aw = w - this.wrap.getFrameWidth('lr');
25629                 this.inputEl().setWidth(this.adjustWidth('textarea', aw));
25630                 ew = aw;
25631             }
25632             if(typeof h == 'number'){
25633                  var tbh = -11;  // fixme it needs to tool bar size!
25634                 for (var i =0; i < this.toolbars.length;i++) {
25635                     // fixme - ask toolbars for heights?
25636                     tbh += this.toolbars[i].el.getHeight();
25637                     //if (this.toolbars[i].footer) {
25638                     //    tbh += this.toolbars[i].footer.el.getHeight();
25639                     //}
25640                 }
25641               
25642                 
25643                 
25644                 
25645                 
25646                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
25647                 ah -= 5; // knock a few pixes off for look..
25648                 this.inputEl().setHeight(this.adjustWidth('textarea', ah));
25649                 var eh = ah;
25650             }
25651         }
25652         Roo.log('onResize:' + [w,h,ew,eh].join(',') );
25653         this.editorcore.onResize(ew,eh);
25654         
25655     },
25656
25657     /**
25658      * Toggles the editor between standard and source edit mode.
25659      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
25660      */
25661     toggleSourceEdit : function(sourceEditMode)
25662     {
25663         this.editorcore.toggleSourceEdit(sourceEditMode);
25664         
25665         if(this.editorcore.sourceEditMode){
25666             Roo.log('editor - showing textarea');
25667             
25668 //            Roo.log('in');
25669 //            Roo.log(this.syncValue());
25670             this.syncValue();
25671             this.inputEl().removeClass(['hide', 'x-hidden']);
25672             this.inputEl().dom.removeAttribute('tabIndex');
25673             this.inputEl().focus();
25674         }else{
25675             Roo.log('editor - hiding textarea');
25676 //            Roo.log('out')
25677 //            Roo.log(this.pushValue()); 
25678             this.pushValue();
25679             
25680             this.inputEl().addClass(['hide', 'x-hidden']);
25681             this.inputEl().dom.setAttribute('tabIndex', -1);
25682             //this.deferFocus();
25683         }
25684          
25685         if(this.resizable){
25686             this.setSize(this.wrap.getSize());
25687         }
25688         
25689         this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
25690     },
25691  
25692     // private (for BoxComponent)
25693     adjustSize : Roo.BoxComponent.prototype.adjustSize,
25694
25695     // private (for BoxComponent)
25696     getResizeEl : function(){
25697         return this.wrap;
25698     },
25699
25700     // private (for BoxComponent)
25701     getPositionEl : function(){
25702         return this.wrap;
25703     },
25704
25705     // private
25706     initEvents : function(){
25707         this.originalValue = this.getValue();
25708     },
25709
25710 //    /**
25711 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
25712 //     * @method
25713 //     */
25714 //    markInvalid : Roo.emptyFn,
25715 //    /**
25716 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
25717 //     * @method
25718 //     */
25719 //    clearInvalid : Roo.emptyFn,
25720
25721     setValue : function(v){
25722         Roo.bootstrap.HtmlEditor.superclass.setValue.call(this, v);
25723         this.editorcore.pushValue();
25724     },
25725
25726      
25727     // private
25728     deferFocus : function(){
25729         this.focus.defer(10, this);
25730     },
25731
25732     // doc'ed in Field
25733     focus : function(){
25734         this.editorcore.focus();
25735         
25736     },
25737       
25738
25739     // private
25740     onDestroy : function(){
25741         
25742         
25743         
25744         if(this.rendered){
25745             
25746             for (var i =0; i < this.toolbars.length;i++) {
25747                 // fixme - ask toolbars for heights?
25748                 this.toolbars[i].onDestroy();
25749             }
25750             
25751             this.wrap.dom.innerHTML = '';
25752             this.wrap.remove();
25753         }
25754     },
25755
25756     // private
25757     onFirstFocus : function(){
25758         //Roo.log("onFirstFocus");
25759         this.editorcore.onFirstFocus();
25760          for (var i =0; i < this.toolbars.length;i++) {
25761             this.toolbars[i].onFirstFocus();
25762         }
25763         
25764     },
25765     
25766     // private
25767     syncValue : function()
25768     {   
25769         this.editorcore.syncValue();
25770     },
25771     
25772     pushValue : function()
25773     {   
25774         this.editorcore.pushValue();
25775     }
25776      
25777     
25778     // hide stuff that is not compatible
25779     /**
25780      * @event blur
25781      * @hide
25782      */
25783     /**
25784      * @event change
25785      * @hide
25786      */
25787     /**
25788      * @event focus
25789      * @hide
25790      */
25791     /**
25792      * @event specialkey
25793      * @hide
25794      */
25795     /**
25796      * @cfg {String} fieldClass @hide
25797      */
25798     /**
25799      * @cfg {String} focusClass @hide
25800      */
25801     /**
25802      * @cfg {String} autoCreate @hide
25803      */
25804     /**
25805      * @cfg {String} inputType @hide
25806      */
25807      
25808     /**
25809      * @cfg {String} invalidText @hide
25810      */
25811     /**
25812      * @cfg {String} msgFx @hide
25813      */
25814     /**
25815      * @cfg {String} validateOnBlur @hide
25816      */
25817 });
25818  
25819     
25820    
25821    
25822    
25823       
25824 Roo.namespace('Roo.bootstrap.htmleditor');
25825 /**
25826  * @class Roo.bootstrap.HtmlEditorToolbar1
25827  * Basic Toolbar
25828  * 
25829  * @example
25830  * Usage:
25831  *
25832  new Roo.bootstrap.HtmlEditor({
25833     ....
25834     toolbars : [
25835         new Roo.bootstrap.HtmlEditorToolbar1({
25836             disable : { fonts: 1 , format: 1, ..., ... , ...],
25837             btns : [ .... ]
25838         })
25839     }
25840      
25841  * 
25842  * @cfg {Object} disable List of elements to disable..
25843  * @cfg {Array} btns List of additional buttons.
25844  * 
25845  * 
25846  * NEEDS Extra CSS? 
25847  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
25848  */
25849  
25850 Roo.bootstrap.htmleditor.ToolbarStandard = function(config)
25851 {
25852     
25853     Roo.apply(this, config);
25854     
25855     // default disabled, based on 'good practice'..
25856     this.disable = this.disable || {};
25857     Roo.applyIf(this.disable, {
25858         fontSize : true,
25859         colors : true,
25860         specialElements : true
25861     });
25862     Roo.bootstrap.htmleditor.ToolbarStandard.superclass.constructor.call(this, config);
25863     
25864     this.editor = config.editor;
25865     this.editorcore = config.editor.editorcore;
25866     
25867     this.buttons   = new Roo.util.MixedCollection(false, function(o) { return o.cmd; });
25868     
25869     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
25870     // dont call parent... till later.
25871 }
25872 Roo.extend(Roo.bootstrap.htmleditor.ToolbarStandard, Roo.bootstrap.NavSimplebar,  {
25873      
25874     bar : true,
25875     
25876     editor : false,
25877     editorcore : false,
25878     
25879     
25880     formats : [
25881         "p" ,  
25882         "h1","h2","h3","h4","h5","h6", 
25883         "pre", "code", 
25884         "abbr", "acronym", "address", "cite", "samp", "var",
25885         'div','span'
25886     ],
25887     
25888     onRender : function(ct, position)
25889     {
25890        // Roo.log("Call onRender: " + this.xtype);
25891         
25892        Roo.bootstrap.htmleditor.ToolbarStandard.superclass.onRender.call(this, ct, position);
25893        Roo.log(this.el);
25894        this.el.dom.style.marginBottom = '0';
25895        var _this = this;
25896        var editorcore = this.editorcore;
25897        var editor= this.editor;
25898        
25899        var children = [];
25900        var btn = function(id,cmd , toggle, handler, html){
25901        
25902             var  event = toggle ? 'toggle' : 'click';
25903        
25904             var a = {
25905                 size : 'sm',
25906                 xtype: 'Button',
25907                 xns: Roo.bootstrap,
25908                 //glyphicon : id,
25909                 fa: id,
25910                 cmd : id || cmd,
25911                 enableToggle:toggle !== false,
25912                 html : html || '',
25913                 pressed : toggle ? false : null,
25914                 listeners : {}
25915             };
25916             a.listeners[toggle ? 'toggle' : 'click'] = function() {
25917                 handler ? handler.call(_this,this) :_this.onBtnClick.call(_this, cmd ||  id);
25918             };
25919             children.push(a);
25920             return a;
25921        }
25922        
25923     //    var cb_box = function...
25924         
25925         var style = {
25926                 xtype: 'Button',
25927                 size : 'sm',
25928                 xns: Roo.bootstrap,
25929                 fa : 'font',
25930                 //html : 'submit'
25931                 menu : {
25932                     xtype: 'Menu',
25933                     xns: Roo.bootstrap,
25934                     items:  []
25935                 }
25936         };
25937         Roo.each(this.formats, function(f) {
25938             style.menu.items.push({
25939                 xtype :'MenuItem',
25940                 xns: Roo.bootstrap,
25941                 html : '<'+ f+' style="margin:2px">'+f +'</'+ f+'>',
25942                 tagname : f,
25943                 listeners : {
25944                     click : function()
25945                     {
25946                         editorcore.insertTag(this.tagname);
25947                         editor.focus();
25948                     }
25949                 }
25950                 
25951             });
25952         });
25953         children.push(style);   
25954         
25955         btn('bold',false,true);
25956         btn('italic',false,true);
25957         btn('align-left', 'justifyleft',true);
25958         btn('align-center', 'justifycenter',true);
25959         btn('align-right' , 'justifyright',true);
25960         btn('link', false, false, function(btn) {
25961             //Roo.log("create link?");
25962             var url = prompt(this.createLinkText, this.defaultLinkValue);
25963             if(url && url != 'http:/'+'/'){
25964                 this.editorcore.relayCmd('createlink', url);
25965             }
25966         }),
25967         btn('list','insertunorderedlist',true);
25968         btn('pencil', false,true, function(btn){
25969                 Roo.log(this);
25970                 this.toggleSourceEdit(btn.pressed);
25971         });
25972         
25973         if (this.editor.btns.length > 0) {
25974             for (var i = 0; i<this.editor.btns.length; i++) {
25975                 children.push(this.editor.btns[i]);
25976             }
25977         }
25978         
25979         /*
25980         var cog = {
25981                 xtype: 'Button',
25982                 size : 'sm',
25983                 xns: Roo.bootstrap,
25984                 glyphicon : 'cog',
25985                 //html : 'submit'
25986                 menu : {
25987                     xtype: 'Menu',
25988                     xns: Roo.bootstrap,
25989                     items:  []
25990                 }
25991         };
25992         
25993         cog.menu.items.push({
25994             xtype :'MenuItem',
25995             xns: Roo.bootstrap,
25996             html : Clean styles,
25997             tagname : f,
25998             listeners : {
25999                 click : function()
26000                 {
26001                     editorcore.insertTag(this.tagname);
26002                     editor.focus();
26003                 }
26004             }
26005             
26006         });
26007        */
26008         
26009          
26010        this.xtype = 'NavSimplebar';
26011         
26012         for(var i=0;i< children.length;i++) {
26013             
26014             this.buttons.add(this.addxtypeChild(children[i]));
26015             
26016         }
26017         
26018         editor.on('editorevent', this.updateToolbar, this);
26019     },
26020     onBtnClick : function(id)
26021     {
26022        this.editorcore.relayCmd(id);
26023        this.editorcore.focus();
26024     },
26025     
26026     /**
26027      * Protected method that will not generally be called directly. It triggers
26028      * a toolbar update by reading the markup state of the current selection in the editor.
26029      */
26030     updateToolbar: function(){
26031
26032         if(!this.editorcore.activated){
26033             this.editor.onFirstFocus(); // is this neeed?
26034             return;
26035         }
26036
26037         var btns = this.buttons; 
26038         var doc = this.editorcore.doc;
26039         btns.get('bold').setActive(doc.queryCommandState('bold'));
26040         btns.get('italic').setActive(doc.queryCommandState('italic'));
26041         //btns.get('underline').setActive(doc.queryCommandState('underline'));
26042         
26043         btns.get('align-left').setActive(doc.queryCommandState('justifyleft'));
26044         btns.get('align-center').setActive(doc.queryCommandState('justifycenter'));
26045         btns.get('align-right').setActive(doc.queryCommandState('justifyright'));
26046         
26047         //btns[frameId + '-insertorderedlist').setActive(doc.queryCommandState('insertorderedlist'));
26048         btns.get('list').setActive(doc.queryCommandState('insertunorderedlist'));
26049          /*
26050         
26051         var ans = this.editorcore.getAllAncestors();
26052         if (this.formatCombo) {
26053             
26054             
26055             var store = this.formatCombo.store;
26056             this.formatCombo.setValue("");
26057             for (var i =0; i < ans.length;i++) {
26058                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
26059                     // select it..
26060                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
26061                     break;
26062                 }
26063             }
26064         }
26065         
26066         
26067         
26068         // hides menus... - so this cant be on a menu...
26069         Roo.bootstrap.MenuMgr.hideAll();
26070         */
26071         Roo.bootstrap.MenuMgr.hideAll();
26072         //this.editorsyncValue();
26073     },
26074     onFirstFocus: function() {
26075         this.buttons.each(function(item){
26076            item.enable();
26077         });
26078     },
26079     toggleSourceEdit : function(sourceEditMode){
26080         
26081           
26082         if(sourceEditMode){
26083             Roo.log("disabling buttons");
26084            this.buttons.each( function(item){
26085                 if(item.cmd != 'pencil'){
26086                     item.disable();
26087                 }
26088             });
26089           
26090         }else{
26091             Roo.log("enabling buttons");
26092             if(this.editorcore.initialized){
26093                 this.buttons.each( function(item){
26094                     item.enable();
26095                 });
26096             }
26097             
26098         }
26099         Roo.log("calling toggole on editor");
26100         // tell the editor that it's been pressed..
26101         this.editor.toggleSourceEdit(sourceEditMode);
26102        
26103     }
26104 });
26105
26106
26107
26108
26109  
26110 /*
26111  * - LGPL
26112  */
26113
26114 /**
26115  * @class Roo.bootstrap.Markdown
26116  * @extends Roo.bootstrap.TextArea
26117  * Bootstrap Showdown editable area
26118  * @cfg {string} content
26119  * 
26120  * @constructor
26121  * Create a new Showdown
26122  */
26123
26124 Roo.bootstrap.Markdown = function(config){
26125     Roo.bootstrap.Markdown.superclass.constructor.call(this, config);
26126    
26127 };
26128
26129 Roo.extend(Roo.bootstrap.Markdown, Roo.bootstrap.TextArea,  {
26130     
26131     editing :false,
26132     
26133     initEvents : function()
26134     {
26135         
26136         Roo.bootstrap.TextArea.prototype.initEvents.call(this);
26137         this.markdownEl = this.el.createChild({
26138             cls : 'roo-markdown-area'
26139         });
26140         this.inputEl().addClass('d-none');
26141         this.markdownEl.dom.innerHTML = Roo.Markdown.toHtml(Roo.util.Format.htmlEncode(this.getValue()));
26142         this.markdownEl.on('click', this.toggleTextEdit, this);
26143         this.on('blur', this.toggleTextEdit, this);
26144         this.on('specialkey', this.resizeTextArea, this);
26145     },
26146     
26147     toggleTextEdit : function()
26148     {
26149         var sh = this.markdownEl.getHeight();
26150         this.inputEl().addClass('d-none');
26151         this.markdownEl.addClass('d-none');
26152         if (!this.editing) {
26153             // show editor?
26154             this.inputEl().setHeight(Math.min(500, Math.max(sh,(this.getValue().split("\n").length+1) * 30)));
26155             this.inputEl().removeClass('d-none');
26156             this.inputEl().focus();
26157             this.editing = true;
26158             return;
26159         }
26160         // show showdown...
26161         this.updateMarkdown();
26162         this.markdownEl.removeClass('d-none');
26163         this.editing = false;
26164         return;
26165     },
26166     updateMarkdown : function()
26167     {
26168         if (this.getValue() == '') {
26169             this.markdownEl.dom.innerHTML = String.format('<span class="roo-placeholder">{0}</span>', this.placeholder);
26170             return;
26171         }
26172         this.markdownEl.dom.innerHTML = Roo.Markdown.toHtml(Roo.util.Format.htmlEncode(this.getValue()));
26173     },
26174     
26175     resizeTextArea: function () {
26176         
26177         var sh = 100;
26178         Roo.log([sh, this.getValue().split("\n").length * 30]);
26179         this.inputEl().setHeight(Math.min(500, Math.max(sh, (this.getValue().split("\n").length +1) * 30)));
26180     },
26181     setValue : function(val)
26182     {
26183         Roo.bootstrap.TextArea.prototype.setValue.call(this,val);
26184         if (!this.editing) {
26185             this.updateMarkdown();
26186         }
26187         
26188     },
26189     focus : function()
26190     {
26191         if (!this.editing) {
26192             this.toggleTextEdit();
26193         }
26194         
26195     }
26196
26197
26198 });
26199 /**
26200  * @class Roo.bootstrap.Table.AbstractSelectionModel
26201  * @extends Roo.util.Observable
26202  * Abstract base class for grid SelectionModels.  It provides the interface that should be
26203  * implemented by descendant classes.  This class should not be directly instantiated.
26204  * @constructor
26205  */
26206 Roo.bootstrap.Table.AbstractSelectionModel = function(){
26207     this.locked = false;
26208     Roo.bootstrap.Table.AbstractSelectionModel.superclass.constructor.call(this);
26209 };
26210
26211
26212 Roo.extend(Roo.bootstrap.Table.AbstractSelectionModel, Roo.util.Observable,  {
26213     /** @ignore Called by the grid automatically. Do not call directly. */
26214     init : function(grid){
26215         this.grid = grid;
26216         this.initEvents();
26217     },
26218
26219     /**
26220      * Locks the selections.
26221      */
26222     lock : function(){
26223         this.locked = true;
26224     },
26225
26226     /**
26227      * Unlocks the selections.
26228      */
26229     unlock : function(){
26230         this.locked = false;
26231     },
26232
26233     /**
26234      * Returns true if the selections are locked.
26235      * @return {Boolean}
26236      */
26237     isLocked : function(){
26238         return this.locked;
26239     },
26240     
26241     
26242     initEvents : function ()
26243     {
26244         
26245     }
26246 });
26247 /**
26248  * @extends Roo.bootstrap.Table.AbstractSelectionModel
26249  * @class Roo.bootstrap.Table.RowSelectionModel
26250  * The default SelectionModel used by {@link Roo.bootstrap.Table}.
26251  * It supports multiple selections and keyboard selection/navigation. 
26252  * @constructor
26253  * @param {Object} config
26254  */
26255
26256 Roo.bootstrap.Table.RowSelectionModel = function(config){
26257     Roo.apply(this, config);
26258     this.selections = new Roo.util.MixedCollection(false, function(o){
26259         return o.id;
26260     });
26261
26262     this.last = false;
26263     this.lastActive = false;
26264
26265     this.addEvents({
26266         /**
26267              * @event selectionchange
26268              * Fires when the selection changes
26269              * @param {SelectionModel} this
26270              */
26271             "selectionchange" : true,
26272         /**
26273              * @event afterselectionchange
26274              * Fires after the selection changes (eg. by key press or clicking)
26275              * @param {SelectionModel} this
26276              */
26277             "afterselectionchange" : true,
26278         /**
26279              * @event beforerowselect
26280              * Fires when a row is selected being selected, return false to cancel.
26281              * @param {SelectionModel} this
26282              * @param {Number} rowIndex The selected index
26283              * @param {Boolean} keepExisting False if other selections will be cleared
26284              */
26285             "beforerowselect" : true,
26286         /**
26287              * @event rowselect
26288              * Fires when a row is selected.
26289              * @param {SelectionModel} this
26290              * @param {Number} rowIndex The selected index
26291              * @param {Roo.data.Record} r The record
26292              */
26293             "rowselect" : true,
26294         /**
26295              * @event rowdeselect
26296              * Fires when a row is deselected.
26297              * @param {SelectionModel} this
26298              * @param {Number} rowIndex The selected index
26299              */
26300         "rowdeselect" : true
26301     });
26302     Roo.bootstrap.Table.RowSelectionModel.superclass.constructor.call(this);
26303     this.locked = false;
26304  };
26305
26306 Roo.extend(Roo.bootstrap.Table.RowSelectionModel, Roo.bootstrap.Table.AbstractSelectionModel,  {
26307     /**
26308      * @cfg {Boolean} singleSelect
26309      * True to allow selection of only one row at a time (defaults to false)
26310      */
26311     singleSelect : false,
26312
26313     // private
26314     initEvents : function()
26315     {
26316
26317         //if(!this.grid.enableDragDrop && !this.grid.enableDrag){
26318         //    this.growclickrid.on("mousedown", this.handleMouseDown, this);
26319         //}else{ // allow click to work like normal
26320          //   this.grid.on("rowclick", this.handleDragableRowClick, this);
26321         //}
26322         //this.grid.on("rowdblclick", this.handleMouseDBClick, this);
26323         this.grid.on("rowclick", this.handleMouseDown, this);
26324         
26325         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
26326             "up" : function(e){
26327                 if(!e.shiftKey){
26328                     this.selectPrevious(e.shiftKey);
26329                 }else if(this.last !== false && this.lastActive !== false){
26330                     var last = this.last;
26331                     this.selectRange(this.last,  this.lastActive-1);
26332                     this.grid.getView().focusRow(this.lastActive);
26333                     if(last !== false){
26334                         this.last = last;
26335                     }
26336                 }else{
26337                     this.selectFirstRow();
26338                 }
26339                 this.fireEvent("afterselectionchange", this);
26340             },
26341             "down" : function(e){
26342                 if(!e.shiftKey){
26343                     this.selectNext(e.shiftKey);
26344                 }else if(this.last !== false && this.lastActive !== false){
26345                     var last = this.last;
26346                     this.selectRange(this.last,  this.lastActive+1);
26347                     this.grid.getView().focusRow(this.lastActive);
26348                     if(last !== false){
26349                         this.last = last;
26350                     }
26351                 }else{
26352                     this.selectFirstRow();
26353                 }
26354                 this.fireEvent("afterselectionchange", this);
26355             },
26356             scope: this
26357         });
26358         this.grid.store.on('load', function(){
26359             this.selections.clear();
26360         },this);
26361         /*
26362         var view = this.grid.view;
26363         view.on("refresh", this.onRefresh, this);
26364         view.on("rowupdated", this.onRowUpdated, this);
26365         view.on("rowremoved", this.onRemove, this);
26366         */
26367     },
26368
26369     // private
26370     onRefresh : function()
26371     {
26372         var ds = this.grid.store, i, v = this.grid.view;
26373         var s = this.selections;
26374         s.each(function(r){
26375             if((i = ds.indexOfId(r.id)) != -1){
26376                 v.onRowSelect(i);
26377             }else{
26378                 s.remove(r);
26379             }
26380         });
26381     },
26382
26383     // private
26384     onRemove : function(v, index, r){
26385         this.selections.remove(r);
26386     },
26387
26388     // private
26389     onRowUpdated : function(v, index, r){
26390         if(this.isSelected(r)){
26391             v.onRowSelect(index);
26392         }
26393     },
26394
26395     /**
26396      * Select records.
26397      * @param {Array} records The records to select
26398      * @param {Boolean} keepExisting (optional) True to keep existing selections
26399      */
26400     selectRecords : function(records, keepExisting)
26401     {
26402         if(!keepExisting){
26403             this.clearSelections();
26404         }
26405             var ds = this.grid.store;
26406         for(var i = 0, len = records.length; i < len; i++){
26407             this.selectRow(ds.indexOf(records[i]), true);
26408         }
26409     },
26410
26411     /**
26412      * Gets the number of selected rows.
26413      * @return {Number}
26414      */
26415     getCount : function(){
26416         return this.selections.length;
26417     },
26418
26419     /**
26420      * Selects the first row in the grid.
26421      */
26422     selectFirstRow : function(){
26423         this.selectRow(0);
26424     },
26425
26426     /**
26427      * Select the last row.
26428      * @param {Boolean} keepExisting (optional) True to keep existing selections
26429      */
26430     selectLastRow : function(keepExisting){
26431         //this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
26432         this.selectRow(this.grid.store.getCount() - 1, keepExisting);
26433     },
26434
26435     /**
26436      * Selects the row immediately following the last selected row.
26437      * @param {Boolean} keepExisting (optional) True to keep existing selections
26438      */
26439     selectNext : function(keepExisting)
26440     {
26441             if(this.last !== false && (this.last+1) < this.grid.store.getCount()){
26442             this.selectRow(this.last+1, keepExisting);
26443             this.grid.getView().focusRow(this.last);
26444         }
26445     },
26446
26447     /**
26448      * Selects the row that precedes the last selected row.
26449      * @param {Boolean} keepExisting (optional) True to keep existing selections
26450      */
26451     selectPrevious : function(keepExisting){
26452         if(this.last){
26453             this.selectRow(this.last-1, keepExisting);
26454             this.grid.getView().focusRow(this.last);
26455         }
26456     },
26457
26458     /**
26459      * Returns the selected records
26460      * @return {Array} Array of selected records
26461      */
26462     getSelections : function(){
26463         return [].concat(this.selections.items);
26464     },
26465
26466     /**
26467      * Returns the first selected record.
26468      * @return {Record}
26469      */
26470     getSelected : function(){
26471         return this.selections.itemAt(0);
26472     },
26473
26474
26475     /**
26476      * Clears all selections.
26477      */
26478     clearSelections : function(fast)
26479     {
26480         if(this.locked) {
26481             return;
26482         }
26483         if(fast !== true){
26484                 var ds = this.grid.store;
26485             var s = this.selections;
26486             s.each(function(r){
26487                 this.deselectRow(ds.indexOfId(r.id));
26488             }, this);
26489             s.clear();
26490         }else{
26491             this.selections.clear();
26492         }
26493         this.last = false;
26494     },
26495
26496
26497     /**
26498      * Selects all rows.
26499      */
26500     selectAll : function(){
26501         if(this.locked) {
26502             return;
26503         }
26504         this.selections.clear();
26505         for(var i = 0, len = this.grid.store.getCount(); i < len; i++){
26506             this.selectRow(i, true);
26507         }
26508     },
26509
26510     /**
26511      * Returns True if there is a selection.
26512      * @return {Boolean}
26513      */
26514     hasSelection : function(){
26515         return this.selections.length > 0;
26516     },
26517
26518     /**
26519      * Returns True if the specified row is selected.
26520      * @param {Number/Record} record The record or index of the record to check
26521      * @return {Boolean}
26522      */
26523     isSelected : function(index){
26524             var r = typeof index == "number" ? this.grid.store.getAt(index) : index;
26525         return (r && this.selections.key(r.id) ? true : false);
26526     },
26527
26528     /**
26529      * Returns True if the specified record id is selected.
26530      * @param {String} id The id of record to check
26531      * @return {Boolean}
26532      */
26533     isIdSelected : function(id){
26534         return (this.selections.key(id) ? true : false);
26535     },
26536
26537
26538     // private
26539     handleMouseDBClick : function(e, t){
26540         
26541     },
26542     // private
26543     handleMouseDown : function(e, t)
26544     {
26545             var rowIndex = this.grid.headerShow  ? t.dom.rowIndex - 1 : t.dom.rowIndex ; // first row is header???
26546         if(this.isLocked() || rowIndex < 0 ){
26547             return;
26548         };
26549         if(e.shiftKey && this.last !== false){
26550             var last = this.last;
26551             this.selectRange(last, rowIndex, e.ctrlKey);
26552             this.last = last; // reset the last
26553             t.focus();
26554     
26555         }else{
26556             var isSelected = this.isSelected(rowIndex);
26557             //Roo.log("select row:" + rowIndex);
26558             if(isSelected){
26559                 this.deselectRow(rowIndex);
26560             } else {
26561                         this.selectRow(rowIndex, true);
26562             }
26563     
26564             /*
26565                 if(e.button !== 0 && isSelected){
26566                 alert('rowIndex 2: ' + rowIndex);
26567                     view.focusRow(rowIndex);
26568                 }else if(e.ctrlKey && isSelected){
26569                     this.deselectRow(rowIndex);
26570                 }else if(!isSelected){
26571                     this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
26572                     view.focusRow(rowIndex);
26573                 }
26574             */
26575         }
26576         this.fireEvent("afterselectionchange", this);
26577     },
26578     // private
26579     handleDragableRowClick :  function(grid, rowIndex, e) 
26580     {
26581         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
26582             this.selectRow(rowIndex, false);
26583             grid.view.focusRow(rowIndex);
26584              this.fireEvent("afterselectionchange", this);
26585         }
26586     },
26587     
26588     /**
26589      * Selects multiple rows.
26590      * @param {Array} rows Array of the indexes of the row to select
26591      * @param {Boolean} keepExisting (optional) True to keep existing selections
26592      */
26593     selectRows : function(rows, keepExisting){
26594         if(!keepExisting){
26595             this.clearSelections();
26596         }
26597         for(var i = 0, len = rows.length; i < len; i++){
26598             this.selectRow(rows[i], true);
26599         }
26600     },
26601
26602     /**
26603      * Selects a range of rows. All rows in between startRow and endRow are also selected.
26604      * @param {Number} startRow The index of the first row in the range
26605      * @param {Number} endRow The index of the last row in the range
26606      * @param {Boolean} keepExisting (optional) True to retain existing selections
26607      */
26608     selectRange : function(startRow, endRow, keepExisting){
26609         if(this.locked) {
26610             return;
26611         }
26612         if(!keepExisting){
26613             this.clearSelections();
26614         }
26615         if(startRow <= endRow){
26616             for(var i = startRow; i <= endRow; i++){
26617                 this.selectRow(i, true);
26618             }
26619         }else{
26620             for(var i = startRow; i >= endRow; i--){
26621                 this.selectRow(i, true);
26622             }
26623         }
26624     },
26625
26626     /**
26627      * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
26628      * @param {Number} startRow The index of the first row in the range
26629      * @param {Number} endRow The index of the last row in the range
26630      */
26631     deselectRange : function(startRow, endRow, preventViewNotify){
26632         if(this.locked) {
26633             return;
26634         }
26635         for(var i = startRow; i <= endRow; i++){
26636             this.deselectRow(i, preventViewNotify);
26637         }
26638     },
26639
26640     /**
26641      * Selects a row.
26642      * @param {Number} row The index of the row to select
26643      * @param {Boolean} keepExisting (optional) True to keep existing selections
26644      */
26645     selectRow : function(index, keepExisting, preventViewNotify)
26646     {
26647             if(this.locked || (index < 0 || index > this.grid.store.getCount())) {
26648             return;
26649         }
26650         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
26651             if(!keepExisting || this.singleSelect){
26652                 this.clearSelections();
26653             }
26654             
26655             var r = this.grid.store.getAt(index);
26656             //console.log('selectRow - record id :' + r.id);
26657             
26658             this.selections.add(r);
26659             this.last = this.lastActive = index;
26660             if(!preventViewNotify){
26661                 var proxy = new Roo.Element(
26662                                 this.grid.getRowDom(index)
26663                 );
26664                 proxy.addClass('bg-info info');
26665             }
26666             this.fireEvent("rowselect", this, index, r);
26667             this.fireEvent("selectionchange", this);
26668         }
26669     },
26670
26671     /**
26672      * Deselects a row.
26673      * @param {Number} row The index of the row to deselect
26674      */
26675     deselectRow : function(index, preventViewNotify)
26676     {
26677         if(this.locked) {
26678             return;
26679         }
26680         if(this.last == index){
26681             this.last = false;
26682         }
26683         if(this.lastActive == index){
26684             this.lastActive = false;
26685         }
26686         
26687         var r = this.grid.store.getAt(index);
26688         if (!r) {
26689             return;
26690         }
26691         
26692         this.selections.remove(r);
26693         //.console.log('deselectRow - record id :' + r.id);
26694         if(!preventViewNotify){
26695         
26696             var proxy = new Roo.Element(
26697                 this.grid.getRowDom(index)
26698             );
26699             proxy.removeClass('bg-info info');
26700         }
26701         this.fireEvent("rowdeselect", this, index);
26702         this.fireEvent("selectionchange", this);
26703     },
26704
26705     // private
26706     restoreLast : function(){
26707         if(this._last){
26708             this.last = this._last;
26709         }
26710     },
26711
26712     // private
26713     acceptsNav : function(row, col, cm){
26714         return !cm.isHidden(col) && cm.isCellEditable(col, row);
26715     },
26716
26717     // private
26718     onEditorKey : function(field, e){
26719         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
26720         if(k == e.TAB){
26721             e.stopEvent();
26722             ed.completeEdit();
26723             if(e.shiftKey){
26724                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
26725             }else{
26726                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
26727             }
26728         }else if(k == e.ENTER && !e.ctrlKey){
26729             e.stopEvent();
26730             ed.completeEdit();
26731             if(e.shiftKey){
26732                 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
26733             }else{
26734                 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
26735             }
26736         }else if(k == e.ESC){
26737             ed.cancelEdit();
26738         }
26739         if(newCell){
26740             g.startEditing(newCell[0], newCell[1]);
26741         }
26742     }
26743 });
26744 /*
26745  * Based on:
26746  * Ext JS Library 1.1.1
26747  * Copyright(c) 2006-2007, Ext JS, LLC.
26748  *
26749  * Originally Released Under LGPL - original licence link has changed is not relivant.
26750  *
26751  * Fork - LGPL
26752  * <script type="text/javascript">
26753  */
26754  
26755 /**
26756  * @class Roo.bootstrap.PagingToolbar
26757  * @extends Roo.bootstrap.NavSimplebar
26758  * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
26759  * @constructor
26760  * Create a new PagingToolbar
26761  * @param {Object} config The config object
26762  * @param {Roo.data.Store} store
26763  */
26764 Roo.bootstrap.PagingToolbar = function(config)
26765 {
26766     // old args format still supported... - xtype is prefered..
26767         // created from xtype...
26768     
26769     this.ds = config.dataSource;
26770     
26771     if (config.store && !this.ds) {
26772         this.store= Roo.factory(config.store, Roo.data);
26773         this.ds = this.store;
26774         this.ds.xmodule = this.xmodule || false;
26775     }
26776     
26777     this.toolbarItems = [];
26778     if (config.items) {
26779         this.toolbarItems = config.items;
26780     }
26781     
26782     Roo.bootstrap.PagingToolbar.superclass.constructor.call(this, config);
26783     
26784     this.cursor = 0;
26785     
26786     if (this.ds) { 
26787         this.bind(this.ds);
26788     }
26789     
26790     if (Roo.bootstrap.version == 4) {
26791         this.navgroup = new Roo.bootstrap.ButtonGroup({ cls: 'pagination' });
26792     } else {
26793         this.navgroup = new Roo.bootstrap.NavGroup({ cls: 'pagination' });
26794     }
26795     
26796 };
26797
26798 Roo.extend(Roo.bootstrap.PagingToolbar, Roo.bootstrap.NavSimplebar, {
26799     /**
26800      * @cfg {Roo.data.Store} dataSource
26801      * The underlying data store providing the paged data
26802      */
26803     /**
26804      * @cfg {String/HTMLElement/Element} container
26805      * container The id or element that will contain the toolbar
26806      */
26807     /**
26808      * @cfg {Boolean} displayInfo
26809      * True to display the displayMsg (defaults to false)
26810      */
26811     /**
26812      * @cfg {Number} pageSize
26813      * The number of records to display per page (defaults to 20)
26814      */
26815     pageSize: 20,
26816     /**
26817      * @cfg {String} displayMsg
26818      * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
26819      */
26820     displayMsg : 'Displaying {0} - {1} of {2}',
26821     /**
26822      * @cfg {String} emptyMsg
26823      * The message to display when no records are found (defaults to "No data to display")
26824      */
26825     emptyMsg : 'No data to display',
26826     /**
26827      * Customizable piece of the default paging text (defaults to "Page")
26828      * @type String
26829      */
26830     beforePageText : "Page",
26831     /**
26832      * Customizable piece of the default paging text (defaults to "of %0")
26833      * @type String
26834      */
26835     afterPageText : "of {0}",
26836     /**
26837      * Customizable piece of the default paging text (defaults to "First Page")
26838      * @type String
26839      */
26840     firstText : "First Page",
26841     /**
26842      * Customizable piece of the default paging text (defaults to "Previous Page")
26843      * @type String
26844      */
26845     prevText : "Previous Page",
26846     /**
26847      * Customizable piece of the default paging text (defaults to "Next Page")
26848      * @type String
26849      */
26850     nextText : "Next Page",
26851     /**
26852      * Customizable piece of the default paging text (defaults to "Last Page")
26853      * @type String
26854      */
26855     lastText : "Last Page",
26856     /**
26857      * Customizable piece of the default paging text (defaults to "Refresh")
26858      * @type String
26859      */
26860     refreshText : "Refresh",
26861
26862     buttons : false,
26863     // private
26864     onRender : function(ct, position) 
26865     {
26866         Roo.bootstrap.PagingToolbar.superclass.onRender.call(this, ct, position);
26867         this.navgroup.parentId = this.id;
26868         this.navgroup.onRender(this.el, null);
26869         // add the buttons to the navgroup
26870         
26871         if(this.displayInfo){
26872             this.el.select('ul.navbar-nav',true).first().createChild({cls:'x-paging-info'});
26873             this.displayEl = this.el.select('.x-paging-info', true).first();
26874 //            var navel = this.navgroup.addItem( { tagtype : 'span', html : '', cls : 'x-paging-info', preventDefault : true } );
26875 //            this.displayEl = navel.el.select('span',true).first();
26876         }
26877         
26878         var _this = this;
26879         
26880         if(this.buttons){
26881             Roo.each(_this.buttons, function(e){ // this might need to use render????
26882                Roo.factory(e).render(_this.el);
26883             });
26884         }
26885             
26886         Roo.each(_this.toolbarItems, function(e) {
26887             _this.navgroup.addItem(e);
26888         });
26889         
26890         
26891         this.first = this.navgroup.addItem({
26892             tooltip: this.firstText,
26893             cls: "prev btn-outline-secondary",
26894             html : ' <i class="fa fa-step-backward"></i>',
26895             disabled: true,
26896             preventDefault: true,
26897             listeners : { click : this.onClick.createDelegate(this, ["first"]) }
26898         });
26899         
26900         this.prev =  this.navgroup.addItem({
26901             tooltip: this.prevText,
26902             cls: "prev btn-outline-secondary",
26903             html : ' <i class="fa fa-backward"></i>',
26904             disabled: true,
26905             preventDefault: true,
26906             listeners : { click :  this.onClick.createDelegate(this, ["prev"]) }
26907         });
26908     //this.addSeparator();
26909         
26910         
26911         var field = this.navgroup.addItem( {
26912             tagtype : 'span',
26913             cls : 'x-paging-position  btn-outline-secondary',
26914              disabled: true,
26915             html : this.beforePageText  +
26916                 '<input type="text" size="3" value="1" class="x-grid-page-number">' +
26917                 '<span class="x-paging-after">' +  String.format(this.afterPageText, 1) + '</span>'
26918          } ); //?? escaped?
26919         
26920         this.field = field.el.select('input', true).first();
26921         this.field.on("keydown", this.onPagingKeydown, this);
26922         this.field.on("focus", function(){this.dom.select();});
26923     
26924     
26925         this.afterTextEl =  field.el.select('.x-paging-after',true).first();
26926         //this.field.setHeight(18);
26927         //this.addSeparator();
26928         this.next = this.navgroup.addItem({
26929             tooltip: this.nextText,
26930             cls: "next btn-outline-secondary",
26931             html : ' <i class="fa fa-forward"></i>',
26932             disabled: true,
26933             preventDefault: true,
26934             listeners : { click :  this.onClick.createDelegate(this, ["next"]) }
26935         });
26936         this.last = this.navgroup.addItem({
26937             tooltip: this.lastText,
26938             html : ' <i class="fa fa-step-forward"></i>',
26939             cls: "next btn-outline-secondary",
26940             disabled: true,
26941             preventDefault: true,
26942             listeners : { click :  this.onClick.createDelegate(this, ["last"]) }
26943         });
26944     //this.addSeparator();
26945         this.loading = this.navgroup.addItem({
26946             tooltip: this.refreshText,
26947             cls: "btn-outline-secondary",
26948             html : ' <i class="fa fa-refresh"></i>',
26949             preventDefault: true,
26950             listeners : { click : this.onClick.createDelegate(this, ["refresh"]) }
26951         });
26952         
26953     },
26954
26955     // private
26956     updateInfo : function(){
26957         if(this.displayEl){
26958             var count = (typeof(this.getCount) == 'undefined') ? this.ds.getCount() : this.getCount();
26959             var msg = count == 0 ?
26960                 this.emptyMsg :
26961                 String.format(
26962                     this.displayMsg,
26963                     this.cursor+1, this.cursor+count, this.ds.getTotalCount()    
26964                 );
26965             this.displayEl.update(msg);
26966         }
26967     },
26968
26969     // private
26970     onLoad : function(ds, r, o)
26971     {
26972         this.cursor = o.params.start ? o.params.start : 0;
26973         
26974         var d = this.getPageData(),
26975             ap = d.activePage,
26976             ps = d.pages;
26977         
26978         
26979         this.afterTextEl.dom.innerHTML = String.format(this.afterPageText, d.pages);
26980         this.field.dom.value = ap;
26981         this.first.setDisabled(ap == 1);
26982         this.prev.setDisabled(ap == 1);
26983         this.next.setDisabled(ap == ps);
26984         this.last.setDisabled(ap == ps);
26985         this.loading.enable();
26986         this.updateInfo();
26987     },
26988
26989     // private
26990     getPageData : function(){
26991         var total = this.ds.getTotalCount();
26992         return {
26993             total : total,
26994             activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
26995             pages :  total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
26996         };
26997     },
26998
26999     // private
27000     onLoadError : function(){
27001         this.loading.enable();
27002     },
27003
27004     // private
27005     onPagingKeydown : function(e){
27006         var k = e.getKey();
27007         var d = this.getPageData();
27008         if(k == e.RETURN){
27009             var v = this.field.dom.value, pageNum;
27010             if(!v || isNaN(pageNum = parseInt(v, 10))){
27011                 this.field.dom.value = d.activePage;
27012                 return;
27013             }
27014             pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
27015             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
27016             e.stopEvent();
27017         }
27018         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))
27019         {
27020           var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
27021           this.field.dom.value = pageNum;
27022           this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
27023           e.stopEvent();
27024         }
27025         else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
27026         {
27027           var v = this.field.dom.value, pageNum; 
27028           var increment = (e.shiftKey) ? 10 : 1;
27029           if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN) {
27030                 increment *= -1;
27031           }
27032           if(!v || isNaN(pageNum = parseInt(v, 10))) {
27033             this.field.dom.value = d.activePage;
27034             return;
27035           }
27036           else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
27037           {
27038             this.field.dom.value = parseInt(v, 10) + increment;
27039             pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
27040             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
27041           }
27042           e.stopEvent();
27043         }
27044     },
27045
27046     // private
27047     beforeLoad : function(){
27048         if(this.loading){
27049             this.loading.disable();
27050         }
27051     },
27052
27053     // private
27054     onClick : function(which){
27055         
27056         var ds = this.ds;
27057         if (!ds) {
27058             return;
27059         }
27060         
27061         switch(which){
27062             case "first":
27063                 ds.load({params:{start: 0, limit: this.pageSize}});
27064             break;
27065             case "prev":
27066                 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
27067             break;
27068             case "next":
27069                 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
27070             break;
27071             case "last":
27072                 var total = ds.getTotalCount();
27073                 var extra = total % this.pageSize;
27074                 var lastStart = extra ? (total - extra) : total-this.pageSize;
27075                 ds.load({params:{start: lastStart, limit: this.pageSize}});
27076             break;
27077             case "refresh":
27078                 ds.load({params:{start: this.cursor, limit: this.pageSize}});
27079             break;
27080         }
27081     },
27082
27083     /**
27084      * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
27085      * @param {Roo.data.Store} store The data store to unbind
27086      */
27087     unbind : function(ds){
27088         ds.un("beforeload", this.beforeLoad, this);
27089         ds.un("load", this.onLoad, this);
27090         ds.un("loadexception", this.onLoadError, this);
27091         ds.un("remove", this.updateInfo, this);
27092         ds.un("add", this.updateInfo, this);
27093         this.ds = undefined;
27094     },
27095
27096     /**
27097      * Binds the paging toolbar to the specified {@link Roo.data.Store}
27098      * @param {Roo.data.Store} store The data store to bind
27099      */
27100     bind : function(ds){
27101         ds.on("beforeload", this.beforeLoad, this);
27102         ds.on("load", this.onLoad, this);
27103         ds.on("loadexception", this.onLoadError, this);
27104         ds.on("remove", this.updateInfo, this);
27105         ds.on("add", this.updateInfo, this);
27106         this.ds = ds;
27107     }
27108 });/*
27109  * - LGPL
27110  *
27111  * element
27112  * 
27113  */
27114
27115 /**
27116  * @class Roo.bootstrap.MessageBar
27117  * @extends Roo.bootstrap.Component
27118  * Bootstrap MessageBar class
27119  * @cfg {String} html contents of the MessageBar
27120  * @cfg {String} weight (info | success | warning | danger) default info
27121  * @cfg {String} beforeClass insert the bar before the given class
27122  * @cfg {Boolean} closable (true | false) default false
27123  * @cfg {Boolean} fixed (true | false) default false, fix the bar at the top
27124  * 
27125  * @constructor
27126  * Create a new Element
27127  * @param {Object} config The config object
27128  */
27129
27130 Roo.bootstrap.MessageBar = function(config){
27131     Roo.bootstrap.MessageBar.superclass.constructor.call(this, config);
27132 };
27133
27134 Roo.extend(Roo.bootstrap.MessageBar, Roo.bootstrap.Component,  {
27135     
27136     html: '',
27137     weight: 'info',
27138     closable: false,
27139     fixed: false,
27140     beforeClass: 'bootstrap-sticky-wrap',
27141     
27142     getAutoCreate : function(){
27143         
27144         var cfg = {
27145             tag: 'div',
27146             cls: 'alert alert-dismissable alert-' + this.weight,
27147             cn: [
27148                 {
27149                     tag: 'span',
27150                     cls: 'message',
27151                     html: this.html || ''
27152                 }
27153             ]
27154         };
27155         
27156         if(this.fixed){
27157             cfg.cls += ' alert-messages-fixed';
27158         }
27159         
27160         if(this.closable){
27161             cfg.cn.push({
27162                 tag: 'button',
27163                 cls: 'close',
27164                 html: 'x'
27165             });
27166         }
27167         
27168         return cfg;
27169     },
27170     
27171     onRender : function(ct, position)
27172     {
27173         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
27174         
27175         if(!this.el){
27176             var cfg = Roo.apply({},  this.getAutoCreate());
27177             cfg.id = Roo.id();
27178             
27179             if (this.cls) {
27180                 cfg.cls += ' ' + this.cls;
27181             }
27182             if (this.style) {
27183                 cfg.style = this.style;
27184             }
27185             this.el = Roo.get(document.body).createChild(cfg, Roo.select('.'+this.beforeClass, true).first());
27186             
27187             this.el.setVisibilityMode(Roo.Element.DISPLAY);
27188         }
27189         
27190         this.el.select('>button.close').on('click', this.hide, this);
27191         
27192     },
27193     
27194     show : function()
27195     {
27196         if (!this.rendered) {
27197             this.render();
27198         }
27199         
27200         this.el.show();
27201         
27202         this.fireEvent('show', this);
27203         
27204     },
27205     
27206     hide : function()
27207     {
27208         if (!this.rendered) {
27209             this.render();
27210         }
27211         
27212         this.el.hide();
27213         
27214         this.fireEvent('hide', this);
27215     },
27216     
27217     update : function()
27218     {
27219 //        var e = this.el.dom.firstChild;
27220 //        
27221 //        if(this.closable){
27222 //            e = e.nextSibling;
27223 //        }
27224 //        
27225 //        e.data = this.html || '';
27226
27227         this.el.select('>.message', true).first().dom.innerHTML = this.html || '';
27228     }
27229    
27230 });
27231
27232  
27233
27234      /*
27235  * - LGPL
27236  *
27237  * Graph
27238  * 
27239  */
27240
27241
27242 /**
27243  * @class Roo.bootstrap.Graph
27244  * @extends Roo.bootstrap.Component
27245  * Bootstrap Graph class
27246 > Prameters
27247  -sm {number} sm 4
27248  -md {number} md 5
27249  @cfg {String} graphtype  bar | vbar | pie
27250  @cfg {number} g_x coodinator | centre x (pie)
27251  @cfg {number} g_y coodinator | centre y (pie)
27252  @cfg {number} g_r radius (pie)
27253  @cfg {number} g_height height of the chart (respected by all elements in the set)
27254  @cfg {number} g_width width of the chart (respected by all elements in the set)
27255  @cfg {Object} title The title of the chart
27256     
27257  -{Array}  values
27258  -opts (object) options for the chart 
27259      o {
27260      o type (string) type of endings of the bar. Default: 'square'. Other options are: 'round', 'sharp', 'soft'.
27261      o gutter (number)(string) default '20%' (WHAT DOES IT DO?)
27262      o vgutter (number)
27263      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.
27264      o stacked (boolean) whether or not to tread values as in a stacked bar chart
27265      o to
27266      o stretch (boolean)
27267      o }
27268  -opts (object) options for the pie
27269      o{
27270      o cut
27271      o startAngle (number)
27272      o endAngle (number)
27273      } 
27274  *
27275  * @constructor
27276  * Create a new Input
27277  * @param {Object} config The config object
27278  */
27279
27280 Roo.bootstrap.Graph = function(config){
27281     Roo.bootstrap.Graph.superclass.constructor.call(this, config);
27282     
27283     this.addEvents({
27284         // img events
27285         /**
27286          * @event click
27287          * The img click event for the img.
27288          * @param {Roo.EventObject} e
27289          */
27290         "click" : true
27291     });
27292 };
27293
27294 Roo.extend(Roo.bootstrap.Graph, Roo.bootstrap.Component,  {
27295     
27296     sm: 4,
27297     md: 5,
27298     graphtype: 'bar',
27299     g_height: 250,
27300     g_width: 400,
27301     g_x: 50,
27302     g_y: 50,
27303     g_r: 30,
27304     opts:{
27305         //g_colors: this.colors,
27306         g_type: 'soft',
27307         g_gutter: '20%'
27308
27309     },
27310     title : false,
27311
27312     getAutoCreate : function(){
27313         
27314         var cfg = {
27315             tag: 'div',
27316             html : null
27317         };
27318         
27319         
27320         return  cfg;
27321     },
27322
27323     onRender : function(ct,position){
27324         
27325         
27326         Roo.bootstrap.Graph.superclass.onRender.call(this,ct,position);
27327         
27328         if (typeof(Raphael) == 'undefined') {
27329             Roo.bootstrap.MessageBox.alert("Error","Raphael is not availabe");
27330             return;
27331         }
27332         
27333         this.raphael = Raphael(this.el.dom);
27334         
27335                     // data1 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
27336                     // data2 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
27337                     // data3 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
27338                     // txtattr = { font: "12px 'Fontin Sans', Fontin-Sans, sans-serif" };
27339                 /*
27340                 r.text(160, 10, "Single Series Chart").attr(txtattr);
27341                 r.text(480, 10, "Multiline Series Chart").attr(txtattr);
27342                 r.text(160, 250, "Multiple Series Stacked Chart").attr(txtattr);
27343                 r.text(480, 250, 'Multiline Series Stacked Vertical Chart. Type "round"').attr(txtattr);
27344                 
27345                 r.barchart(10, 10, 300, 220, [[55, 20, 13, 32, 5, 1, 2, 10]], 0, {type: "sharp"});
27346                 r.barchart(330, 10, 300, 220, data1);
27347                 r.barchart(10, 250, 300, 220, data2, {stacked: true});
27348                 r.barchart(330, 250, 300, 220, data3, {stacked: true, type: "round"});
27349                 */
27350                 
27351                 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
27352                 // r.barchart(30, 30, 560, 250,  xdata, {
27353                 //    labels : [55, 20, 13, 32, 5, 1, 2, 10,5 , 10],
27354                 //     axis : "0 0 1 1",
27355                 //     axisxlabels :  xdata
27356                 //     //yvalues : cols,
27357                    
27358                 // });
27359 //        var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
27360 //        
27361 //        this.load(null,xdata,{
27362 //                axis : "0 0 1 1",
27363 //                axisxlabels :  xdata
27364 //                });
27365
27366     },
27367
27368     load : function(graphtype,xdata,opts)
27369     {
27370         this.raphael.clear();
27371         if(!graphtype) {
27372             graphtype = this.graphtype;
27373         }
27374         if(!opts){
27375             opts = this.opts;
27376         }
27377         var r = this.raphael,
27378             fin = function () {
27379                 this.flag = r.popup(this.bar.x, this.bar.y, this.bar.value || "0").insertBefore(this);
27380             },
27381             fout = function () {
27382                 this.flag.animate({opacity: 0}, 300, function () {this.remove();});
27383             },
27384             pfin = function() {
27385                 this.sector.stop();
27386                 this.sector.scale(1.1, 1.1, this.cx, this.cy);
27387
27388                 if (this.label) {
27389                     this.label[0].stop();
27390                     this.label[0].attr({ r: 7.5 });
27391                     this.label[1].attr({ "font-weight": 800 });
27392                 }
27393             },
27394             pfout = function() {
27395                 this.sector.animate({ transform: 's1 1 ' + this.cx + ' ' + this.cy }, 500, "bounce");
27396
27397                 if (this.label) {
27398                     this.label[0].animate({ r: 5 }, 500, "bounce");
27399                     this.label[1].attr({ "font-weight": 400 });
27400                 }
27401             };
27402
27403         switch(graphtype){
27404             case 'bar':
27405                 this.raphael.barchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
27406                 break;
27407             case 'hbar':
27408                 this.raphael.hbarchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
27409                 break;
27410             case 'pie':
27411 //                opts = { legend: ["%% - Enterprise Users", "% - ddd","Chrome Users"], legendpos: "west", 
27412 //                href: ["http://raphaeljs.com", "http://g.raphaeljs.com"]};
27413 //            
27414                 this.raphael.piechart(this.g_x,this.g_y,this.g_r,xdata,opts).hover(pfin, pfout);
27415                 
27416                 break;
27417
27418         }
27419         
27420         if(this.title){
27421             this.raphael.text(this.title.x, this.title.y, this.title.text).attr(this.title.attr);
27422         }
27423         
27424     },
27425     
27426     setTitle: function(o)
27427     {
27428         this.title = o;
27429     },
27430     
27431     initEvents: function() {
27432         
27433         if(!this.href){
27434             this.el.on('click', this.onClick, this);
27435         }
27436     },
27437     
27438     onClick : function(e)
27439     {
27440         Roo.log('img onclick');
27441         this.fireEvent('click', this, e);
27442     }
27443    
27444 });
27445
27446  
27447 /*
27448  * - LGPL
27449  *
27450  * numberBox
27451  * 
27452  */
27453 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
27454
27455 /**
27456  * @class Roo.bootstrap.dash.NumberBox
27457  * @extends Roo.bootstrap.Component
27458  * Bootstrap NumberBox class
27459  * @cfg {String} headline Box headline
27460  * @cfg {String} content Box content
27461  * @cfg {String} icon Box icon
27462  * @cfg {String} footer Footer text
27463  * @cfg {String} fhref Footer href
27464  * 
27465  * @constructor
27466  * Create a new NumberBox
27467  * @param {Object} config The config object
27468  */
27469
27470
27471 Roo.bootstrap.dash.NumberBox = function(config){
27472     Roo.bootstrap.dash.NumberBox.superclass.constructor.call(this, config);
27473     
27474 };
27475
27476 Roo.extend(Roo.bootstrap.dash.NumberBox, Roo.bootstrap.Component,  {
27477     
27478     headline : '',
27479     content : '',
27480     icon : '',
27481     footer : '',
27482     fhref : '',
27483     ficon : '',
27484     
27485     getAutoCreate : function(){
27486         
27487         var cfg = {
27488             tag : 'div',
27489             cls : 'small-box ',
27490             cn : [
27491                 {
27492                     tag : 'div',
27493                     cls : 'inner',
27494                     cn :[
27495                         {
27496                             tag : 'h3',
27497                             cls : 'roo-headline',
27498                             html : this.headline
27499                         },
27500                         {
27501                             tag : 'p',
27502                             cls : 'roo-content',
27503                             html : this.content
27504                         }
27505                     ]
27506                 }
27507             ]
27508         };
27509         
27510         if(this.icon){
27511             cfg.cn.push({
27512                 tag : 'div',
27513                 cls : 'icon',
27514                 cn :[
27515                     {
27516                         tag : 'i',
27517                         cls : 'ion ' + this.icon
27518                     }
27519                 ]
27520             });
27521         }
27522         
27523         if(this.footer){
27524             var footer = {
27525                 tag : 'a',
27526                 cls : 'small-box-footer',
27527                 href : this.fhref || '#',
27528                 html : this.footer
27529             };
27530             
27531             cfg.cn.push(footer);
27532             
27533         }
27534         
27535         return  cfg;
27536     },
27537
27538     onRender : function(ct,position){
27539         Roo.bootstrap.dash.NumberBox.superclass.onRender.call(this,ct,position);
27540
27541
27542        
27543                 
27544     },
27545
27546     setHeadline: function (value)
27547     {
27548         this.el.select('.roo-headline',true).first().dom.innerHTML = value;
27549     },
27550     
27551     setFooter: function (value, href)
27552     {
27553         this.el.select('a.small-box-footer',true).first().dom.innerHTML = value;
27554         
27555         if(href){
27556             this.el.select('a.small-box-footer',true).first().attr('href', href);
27557         }
27558         
27559     },
27560
27561     setContent: function (value)
27562     {
27563         this.el.select('.roo-content',true).first().dom.innerHTML = value;
27564     },
27565
27566     initEvents: function() 
27567     {   
27568         
27569     }
27570     
27571 });
27572
27573  
27574 /*
27575  * - LGPL
27576  *
27577  * TabBox
27578  * 
27579  */
27580 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
27581
27582 /**
27583  * @class Roo.bootstrap.dash.TabBox
27584  * @extends Roo.bootstrap.Component
27585  * Bootstrap TabBox class
27586  * @cfg {String} title Title of the TabBox
27587  * @cfg {String} icon Icon of the TabBox
27588  * @cfg {Boolean} showtabs (true|false) show the tabs default true
27589  * @cfg {Boolean} tabScrollable (true|false) tab scrollable when mobile view default false
27590  * 
27591  * @constructor
27592  * Create a new TabBox
27593  * @param {Object} config The config object
27594  */
27595
27596
27597 Roo.bootstrap.dash.TabBox = function(config){
27598     Roo.bootstrap.dash.TabBox.superclass.constructor.call(this, config);
27599     this.addEvents({
27600         // raw events
27601         /**
27602          * @event addpane
27603          * When a pane is added
27604          * @param {Roo.bootstrap.dash.TabPane} pane
27605          */
27606         "addpane" : true,
27607         /**
27608          * @event activatepane
27609          * When a pane is activated
27610          * @param {Roo.bootstrap.dash.TabPane} pane
27611          */
27612         "activatepane" : true
27613         
27614          
27615     });
27616     
27617     this.panes = [];
27618 };
27619
27620 Roo.extend(Roo.bootstrap.dash.TabBox, Roo.bootstrap.Component,  {
27621
27622     title : '',
27623     icon : false,
27624     showtabs : true,
27625     tabScrollable : false,
27626     
27627     getChildContainer : function()
27628     {
27629         return this.el.select('.tab-content', true).first();
27630     },
27631     
27632     getAutoCreate : function(){
27633         
27634         var header = {
27635             tag: 'li',
27636             cls: 'pull-left header',
27637             html: this.title,
27638             cn : []
27639         };
27640         
27641         if(this.icon){
27642             header.cn.push({
27643                 tag: 'i',
27644                 cls: 'fa ' + this.icon
27645             });
27646         }
27647         
27648         var h = {
27649             tag: 'ul',
27650             cls: 'nav nav-tabs pull-right',
27651             cn: [
27652                 header
27653             ]
27654         };
27655         
27656         if(this.tabScrollable){
27657             h = {
27658                 tag: 'div',
27659                 cls: 'tab-header',
27660                 cn: [
27661                     {
27662                         tag: 'ul',
27663                         cls: 'nav nav-tabs pull-right',
27664                         cn: [
27665                             header
27666                         ]
27667                     }
27668                 ]
27669             };
27670         }
27671         
27672         var cfg = {
27673             tag: 'div',
27674             cls: 'nav-tabs-custom',
27675             cn: [
27676                 h,
27677                 {
27678                     tag: 'div',
27679                     cls: 'tab-content no-padding',
27680                     cn: []
27681                 }
27682             ]
27683         };
27684
27685         return  cfg;
27686     },
27687     initEvents : function()
27688     {
27689         //Roo.log('add add pane handler');
27690         this.on('addpane', this.onAddPane, this);
27691     },
27692      /**
27693      * Updates the box title
27694      * @param {String} html to set the title to.
27695      */
27696     setTitle : function(value)
27697     {
27698         this.el.select('.nav-tabs .header', true).first().dom.innerHTML = value;
27699     },
27700     onAddPane : function(pane)
27701     {
27702         this.panes.push(pane);
27703         //Roo.log('addpane');
27704         //Roo.log(pane);
27705         // tabs are rendere left to right..
27706         if(!this.showtabs){
27707             return;
27708         }
27709         
27710         var ctr = this.el.select('.nav-tabs', true).first();
27711          
27712          
27713         var existing = ctr.select('.nav-tab',true);
27714         var qty = existing.getCount();;
27715         
27716         
27717         var tab = ctr.createChild({
27718             tag : 'li',
27719             cls : 'nav-tab' + (qty ? '' : ' active'),
27720             cn : [
27721                 {
27722                     tag : 'a',
27723                     href:'#',
27724                     html : pane.title
27725                 }
27726             ]
27727         }, qty ? existing.first().dom : ctr.select('.header', true).first().dom );
27728         pane.tab = tab;
27729         
27730         tab.on('click', this.onTabClick.createDelegate(this, [pane], true));
27731         if (!qty) {
27732             pane.el.addClass('active');
27733         }
27734         
27735                 
27736     },
27737     onTabClick : function(ev,un,ob,pane)
27738     {
27739         //Roo.log('tab - prev default');
27740         ev.preventDefault();
27741         
27742         
27743         this.el.select('.nav-tabs li.nav-tab', true).removeClass('active');
27744         pane.tab.addClass('active');
27745         //Roo.log(pane.title);
27746         this.getChildContainer().select('.tab-pane',true).removeClass('active');
27747         // technically we should have a deactivate event.. but maybe add later.
27748         // and it should not de-activate the selected tab...
27749         this.fireEvent('activatepane', pane);
27750         pane.el.addClass('active');
27751         pane.fireEvent('activate');
27752         
27753         
27754     },
27755     
27756     getActivePane : function()
27757     {
27758         var r = false;
27759         Roo.each(this.panes, function(p) {
27760             if(p.el.hasClass('active')){
27761                 r = p;
27762                 return false;
27763             }
27764             
27765             return;
27766         });
27767         
27768         return r;
27769     }
27770     
27771     
27772 });
27773
27774  
27775 /*
27776  * - LGPL
27777  *
27778  * Tab pane
27779  * 
27780  */
27781 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
27782 /**
27783  * @class Roo.bootstrap.TabPane
27784  * @extends Roo.bootstrap.Component
27785  * Bootstrap TabPane class
27786  * @cfg {Boolean} active (false | true) Default false
27787  * @cfg {String} title title of panel
27788
27789  * 
27790  * @constructor
27791  * Create a new TabPane
27792  * @param {Object} config The config object
27793  */
27794
27795 Roo.bootstrap.dash.TabPane = function(config){
27796     Roo.bootstrap.dash.TabPane.superclass.constructor.call(this, config);
27797     
27798     this.addEvents({
27799         // raw events
27800         /**
27801          * @event activate
27802          * When a pane is activated
27803          * @param {Roo.bootstrap.dash.TabPane} pane
27804          */
27805         "activate" : true
27806          
27807     });
27808 };
27809
27810 Roo.extend(Roo.bootstrap.dash.TabPane, Roo.bootstrap.Component,  {
27811     
27812     active : false,
27813     title : '',
27814     
27815     // the tabBox that this is attached to.
27816     tab : false,
27817      
27818     getAutoCreate : function() 
27819     {
27820         var cfg = {
27821             tag: 'div',
27822             cls: 'tab-pane'
27823         };
27824         
27825         if(this.active){
27826             cfg.cls += ' active';
27827         }
27828         
27829         return cfg;
27830     },
27831     initEvents  : function()
27832     {
27833         //Roo.log('trigger add pane handler');
27834         this.parent().fireEvent('addpane', this)
27835     },
27836     
27837      /**
27838      * Updates the tab title 
27839      * @param {String} html to set the title to.
27840      */
27841     setTitle: function(str)
27842     {
27843         if (!this.tab) {
27844             return;
27845         }
27846         this.title = str;
27847         this.tab.select('a', true).first().dom.innerHTML = str;
27848         
27849     }
27850     
27851     
27852     
27853 });
27854
27855  
27856
27857
27858  /*
27859  * - LGPL
27860  *
27861  * menu
27862  * 
27863  */
27864 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
27865
27866 /**
27867  * @class Roo.bootstrap.menu.Menu
27868  * @extends Roo.bootstrap.Component
27869  * Bootstrap Menu class - container for Menu
27870  * @cfg {String} html Text of the menu
27871  * @cfg {String} weight (default | primary | success | info | warning | danger | inverse)
27872  * @cfg {String} icon Font awesome icon
27873  * @cfg {String} pos Menu align to (top | bottom) default bottom
27874  * 
27875  * 
27876  * @constructor
27877  * Create a new Menu
27878  * @param {Object} config The config object
27879  */
27880
27881
27882 Roo.bootstrap.menu.Menu = function(config){
27883     Roo.bootstrap.menu.Menu.superclass.constructor.call(this, config);
27884     
27885     this.addEvents({
27886         /**
27887          * @event beforeshow
27888          * Fires before this menu is displayed
27889          * @param {Roo.bootstrap.menu.Menu} this
27890          */
27891         beforeshow : true,
27892         /**
27893          * @event beforehide
27894          * Fires before this menu is hidden
27895          * @param {Roo.bootstrap.menu.Menu} this
27896          */
27897         beforehide : true,
27898         /**
27899          * @event show
27900          * Fires after this menu is displayed
27901          * @param {Roo.bootstrap.menu.Menu} this
27902          */
27903         show : true,
27904         /**
27905          * @event hide
27906          * Fires after this menu is hidden
27907          * @param {Roo.bootstrap.menu.Menu} this
27908          */
27909         hide : true,
27910         /**
27911          * @event click
27912          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
27913          * @param {Roo.bootstrap.menu.Menu} this
27914          * @param {Roo.EventObject} e
27915          */
27916         click : true
27917     });
27918     
27919 };
27920
27921 Roo.extend(Roo.bootstrap.menu.Menu, Roo.bootstrap.Component,  {
27922     
27923     submenu : false,
27924     html : '',
27925     weight : 'default',
27926     icon : false,
27927     pos : 'bottom',
27928     
27929     
27930     getChildContainer : function() {
27931         if(this.isSubMenu){
27932             return this.el;
27933         }
27934         
27935         return this.el.select('ul.dropdown-menu', true).first();  
27936     },
27937     
27938     getAutoCreate : function()
27939     {
27940         var text = [
27941             {
27942                 tag : 'span',
27943                 cls : 'roo-menu-text',
27944                 html : this.html
27945             }
27946         ];
27947         
27948         if(this.icon){
27949             text.unshift({
27950                 tag : 'i',
27951                 cls : 'fa ' + this.icon
27952             })
27953         }
27954         
27955         
27956         var cfg = {
27957             tag : 'div',
27958             cls : 'btn-group',
27959             cn : [
27960                 {
27961                     tag : 'button',
27962                     cls : 'dropdown-button btn btn-' + this.weight,
27963                     cn : text
27964                 },
27965                 {
27966                     tag : 'button',
27967                     cls : 'dropdown-toggle btn btn-' + this.weight,
27968                     cn : [
27969                         {
27970                             tag : 'span',
27971                             cls : 'caret'
27972                         }
27973                     ]
27974                 },
27975                 {
27976                     tag : 'ul',
27977                     cls : 'dropdown-menu'
27978                 }
27979             ]
27980             
27981         };
27982         
27983         if(this.pos == 'top'){
27984             cfg.cls += ' dropup';
27985         }
27986         
27987         if(this.isSubMenu){
27988             cfg = {
27989                 tag : 'ul',
27990                 cls : 'dropdown-menu'
27991             }
27992         }
27993         
27994         return cfg;
27995     },
27996     
27997     onRender : function(ct, position)
27998     {
27999         this.isSubMenu = ct.hasClass('dropdown-submenu');
28000         
28001         Roo.bootstrap.menu.Menu.superclass.onRender.call(this, ct, position);
28002     },
28003     
28004     initEvents : function() 
28005     {
28006         if(this.isSubMenu){
28007             return;
28008         }
28009         
28010         this.hidden = true;
28011         
28012         this.triggerEl = this.el.select('button.dropdown-toggle', true).first();
28013         this.triggerEl.on('click', this.onTriggerPress, this);
28014         
28015         this.buttonEl = this.el.select('button.dropdown-button', true).first();
28016         this.buttonEl.on('click', this.onClick, this);
28017         
28018     },
28019     
28020     list : function()
28021     {
28022         if(this.isSubMenu){
28023             return this.el;
28024         }
28025         
28026         return this.el.select('ul.dropdown-menu', true).first();
28027     },
28028     
28029     onClick : function(e)
28030     {
28031         this.fireEvent("click", this, e);
28032     },
28033     
28034     onTriggerPress  : function(e)
28035     {   
28036         if (this.isVisible()) {
28037             this.hide();
28038         } else {
28039             this.show();
28040         }
28041     },
28042     
28043     isVisible : function(){
28044         return !this.hidden;
28045     },
28046     
28047     show : function()
28048     {
28049         this.fireEvent("beforeshow", this);
28050         
28051         this.hidden = false;
28052         this.el.addClass('open');
28053         
28054         Roo.get(document).on("mouseup", this.onMouseUp, this);
28055         
28056         this.fireEvent("show", this);
28057         
28058         
28059     },
28060     
28061     hide : function()
28062     {
28063         this.fireEvent("beforehide", this);
28064         
28065         this.hidden = true;
28066         this.el.removeClass('open');
28067         
28068         Roo.get(document).un("mouseup", this.onMouseUp);
28069         
28070         this.fireEvent("hide", this);
28071     },
28072     
28073     onMouseUp : function()
28074     {
28075         this.hide();
28076     }
28077     
28078 });
28079
28080  
28081  /*
28082  * - LGPL
28083  *
28084  * menu item
28085  * 
28086  */
28087 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
28088
28089 /**
28090  * @class Roo.bootstrap.menu.Item
28091  * @extends Roo.bootstrap.Component
28092  * Bootstrap MenuItem class
28093  * @cfg {Boolean} submenu (true | false) default false
28094  * @cfg {String} html text of the item
28095  * @cfg {String} href the link
28096  * @cfg {Boolean} disable (true | false) default false
28097  * @cfg {Boolean} preventDefault (true | false) default true
28098  * @cfg {String} icon Font awesome icon
28099  * @cfg {String} pos Submenu align to (left | right) default right 
28100  * 
28101  * 
28102  * @constructor
28103  * Create a new Item
28104  * @param {Object} config The config object
28105  */
28106
28107
28108 Roo.bootstrap.menu.Item = function(config){
28109     Roo.bootstrap.menu.Item.superclass.constructor.call(this, config);
28110     this.addEvents({
28111         /**
28112          * @event mouseover
28113          * Fires when the mouse is hovering over this menu
28114          * @param {Roo.bootstrap.menu.Item} this
28115          * @param {Roo.EventObject} e
28116          */
28117         mouseover : true,
28118         /**
28119          * @event mouseout
28120          * Fires when the mouse exits this menu
28121          * @param {Roo.bootstrap.menu.Item} this
28122          * @param {Roo.EventObject} e
28123          */
28124         mouseout : true,
28125         // raw events
28126         /**
28127          * @event click
28128          * The raw click event for the entire grid.
28129          * @param {Roo.EventObject} e
28130          */
28131         click : true
28132     });
28133 };
28134
28135 Roo.extend(Roo.bootstrap.menu.Item, Roo.bootstrap.Component,  {
28136     
28137     submenu : false,
28138     href : '',
28139     html : '',
28140     preventDefault: true,
28141     disable : false,
28142     icon : false,
28143     pos : 'right',
28144     
28145     getAutoCreate : function()
28146     {
28147         var text = [
28148             {
28149                 tag : 'span',
28150                 cls : 'roo-menu-item-text',
28151                 html : this.html
28152             }
28153         ];
28154         
28155         if(this.icon){
28156             text.unshift({
28157                 tag : 'i',
28158                 cls : 'fa ' + this.icon
28159             })
28160         }
28161         
28162         var cfg = {
28163             tag : 'li',
28164             cn : [
28165                 {
28166                     tag : 'a',
28167                     href : this.href || '#',
28168                     cn : text
28169                 }
28170             ]
28171         };
28172         
28173         if(this.disable){
28174             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'disabled' : (cfg.cls + ' disabled');
28175         }
28176         
28177         if(this.submenu){
28178             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'dropdown-submenu' : (cfg.cls + ' dropdown-submenu');
28179             
28180             if(this.pos == 'left'){
28181                 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'pull-left' : (cfg.cls + ' pull-left');
28182             }
28183         }
28184         
28185         return cfg;
28186     },
28187     
28188     initEvents : function() 
28189     {
28190         this.el.on('mouseover', this.onMouseOver, this);
28191         this.el.on('mouseout', this.onMouseOut, this);
28192         
28193         this.el.select('a', true).first().on('click', this.onClick, this);
28194         
28195     },
28196     
28197     onClick : function(e)
28198     {
28199         if(this.preventDefault){
28200             e.preventDefault();
28201         }
28202         
28203         this.fireEvent("click", this, e);
28204     },
28205     
28206     onMouseOver : function(e)
28207     {
28208         if(this.submenu && this.pos == 'left'){
28209             this.el.select('ul.dropdown-menu', true).first().setLeft(this.el.select('ul.dropdown-menu', true).first().getWidth() * -1);
28210         }
28211         
28212         this.fireEvent("mouseover", this, e);
28213     },
28214     
28215     onMouseOut : function(e)
28216     {
28217         this.fireEvent("mouseout", this, e);
28218     }
28219 });
28220
28221  
28222
28223  /*
28224  * - LGPL
28225  *
28226  * menu separator
28227  * 
28228  */
28229 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
28230
28231 /**
28232  * @class Roo.bootstrap.menu.Separator
28233  * @extends Roo.bootstrap.Component
28234  * Bootstrap Separator class
28235  * 
28236  * @constructor
28237  * Create a new Separator
28238  * @param {Object} config The config object
28239  */
28240
28241
28242 Roo.bootstrap.menu.Separator = function(config){
28243     Roo.bootstrap.menu.Separator.superclass.constructor.call(this, config);
28244 };
28245
28246 Roo.extend(Roo.bootstrap.menu.Separator, Roo.bootstrap.Component,  {
28247     
28248     getAutoCreate : function(){
28249         var cfg = {
28250             tag : 'li',
28251             cls: 'divider'
28252         };
28253         
28254         return cfg;
28255     }
28256    
28257 });
28258
28259  
28260
28261  /*
28262  * - LGPL
28263  *
28264  * Tooltip
28265  * 
28266  */
28267
28268 /**
28269  * @class Roo.bootstrap.Tooltip
28270  * Bootstrap Tooltip class
28271  * This is basic at present - all componets support it by default, however they should add tooltipEl() method
28272  * to determine which dom element triggers the tooltip.
28273  * 
28274  * It needs to add support for additional attributes like tooltip-position
28275  * 
28276  * @constructor
28277  * Create a new Toolti
28278  * @param {Object} config The config object
28279  */
28280
28281 Roo.bootstrap.Tooltip = function(config){
28282     Roo.bootstrap.Tooltip.superclass.constructor.call(this, config);
28283     
28284     this.alignment = Roo.bootstrap.Tooltip.alignment;
28285     
28286     if(typeof(config) != 'undefined' && typeof(config.alignment) != 'undefined'){
28287         this.alignment = config.alignment;
28288     }
28289     
28290 };
28291
28292 Roo.apply(Roo.bootstrap.Tooltip, {
28293     /**
28294      * @function init initialize tooltip monitoring.
28295      * @static
28296      */
28297     currentEl : false,
28298     currentTip : false,
28299     currentRegion : false,
28300     
28301     //  init : delay?
28302     
28303     init : function()
28304     {
28305         Roo.get(document).on('mouseover', this.enter ,this);
28306         Roo.get(document).on('mouseout', this.leave, this);
28307          
28308         
28309         this.currentTip = new Roo.bootstrap.Tooltip();
28310     },
28311     
28312     enter : function(ev)
28313     {
28314         var dom = ev.getTarget();
28315         
28316         //Roo.log(['enter',dom]);
28317         var el = Roo.fly(dom);
28318         if (this.currentEl) {
28319             //Roo.log(dom);
28320             //Roo.log(this.currentEl);
28321             //Roo.log(this.currentEl.contains(dom));
28322             if (this.currentEl == el) {
28323                 return;
28324             }
28325             if (dom != this.currentEl.dom && this.currentEl.contains(dom)) {
28326                 return;
28327             }
28328
28329         }
28330         
28331         if (this.currentTip.el) {
28332             this.currentTip.el.setVisibilityMode(Roo.Element.DISPLAY).hide(); // force hiding...
28333         }    
28334         //Roo.log(ev);
28335         
28336         if(!el || el.dom == document){
28337             return;
28338         }
28339         
28340         var bindEl = el;
28341         
28342         // you can not look for children, as if el is the body.. then everythign is the child..
28343         if (!el.attr('tooltip')) { //
28344             if (!el.select("[tooltip]").elements.length) {
28345                 return;
28346             }
28347             // is the mouse over this child...?
28348             bindEl = el.select("[tooltip]").first();
28349             var xy = ev.getXY();
28350             if (!bindEl.getRegion().contains( { top : xy[1] ,right : xy[0] , bottom : xy[1], left : xy[0]})) {
28351                 //Roo.log("not in region.");
28352                 return;
28353             }
28354             //Roo.log("child element over..");
28355             
28356         }
28357         this.currentEl = bindEl;
28358         this.currentTip.bind(bindEl);
28359         this.currentRegion = Roo.lib.Region.getRegion(dom);
28360         this.currentTip.enter();
28361         
28362     },
28363     leave : function(ev)
28364     {
28365         var dom = ev.getTarget();
28366         //Roo.log(['leave',dom]);
28367         if (!this.currentEl) {
28368             return;
28369         }
28370         
28371         
28372         if (dom != this.currentEl.dom) {
28373             return;
28374         }
28375         var xy = ev.getXY();
28376         if (this.currentRegion.contains( new Roo.lib.Region( xy[1], xy[0] ,xy[1], xy[0]  ))) {
28377             return;
28378         }
28379         // only activate leave if mouse cursor is outside... bounding box..
28380         
28381         
28382         
28383         
28384         if (this.currentTip) {
28385             this.currentTip.leave();
28386         }
28387         //Roo.log('clear currentEl');
28388         this.currentEl = false;
28389         
28390         
28391     },
28392     alignment : {
28393         'left' : ['r-l', [-2,0], 'right'],
28394         'right' : ['l-r', [2,0], 'left'],
28395         'bottom' : ['t-b', [0,2], 'top'],
28396         'top' : [ 'b-t', [0,-2], 'bottom']
28397     }
28398     
28399 });
28400
28401
28402 Roo.extend(Roo.bootstrap.Tooltip, Roo.bootstrap.Component,  {
28403     
28404     
28405     bindEl : false,
28406     
28407     delay : null, // can be { show : 300 , hide: 500}
28408     
28409     timeout : null,
28410     
28411     hoverState : null, //???
28412     
28413     placement : 'bottom', 
28414     
28415     alignment : false,
28416     
28417     getAutoCreate : function(){
28418     
28419         var cfg = {
28420            cls : 'tooltip',   
28421            role : 'tooltip',
28422            cn : [
28423                 {
28424                     cls : 'tooltip-arrow arrow'
28425                 },
28426                 {
28427                     cls : 'tooltip-inner'
28428                 }
28429            ]
28430         };
28431         
28432         return cfg;
28433     },
28434     bind : function(el)
28435     {
28436         this.bindEl = el;
28437     },
28438     
28439     initEvents : function()
28440     {
28441         this.arrowEl = this.el.select('.arrow', true).first();
28442         this.innerEl = this.el.select('.tooltip-inner', true).first();
28443     },
28444     
28445     enter : function () {
28446        
28447         if (this.timeout != null) {
28448             clearTimeout(this.timeout);
28449         }
28450         
28451         this.hoverState = 'in';
28452          //Roo.log("enter - show");
28453         if (!this.delay || !this.delay.show) {
28454             this.show();
28455             return;
28456         }
28457         var _t = this;
28458         this.timeout = setTimeout(function () {
28459             if (_t.hoverState == 'in') {
28460                 _t.show();
28461             }
28462         }, this.delay.show);
28463     },
28464     leave : function()
28465     {
28466         clearTimeout(this.timeout);
28467     
28468         this.hoverState = 'out';
28469          if (!this.delay || !this.delay.hide) {
28470             this.hide();
28471             return;
28472         }
28473        
28474         var _t = this;
28475         this.timeout = setTimeout(function () {
28476             //Roo.log("leave - timeout");
28477             
28478             if (_t.hoverState == 'out') {
28479                 _t.hide();
28480                 Roo.bootstrap.Tooltip.currentEl = false;
28481             }
28482         }, delay);
28483     },
28484     
28485     show : function (msg)
28486     {
28487         if (!this.el) {
28488             this.render(document.body);
28489         }
28490         // set content.
28491         //Roo.log([this.bindEl, this.bindEl.attr('tooltip')]);
28492         
28493         var tip = msg || this.bindEl.attr('tooltip') || this.bindEl.select("[tooltip]").first().attr('tooltip');
28494         
28495         this.el.select('.tooltip-inner',true).first().dom.innerHTML = tip;
28496         
28497         this.el.removeClass(['fade','top','bottom', 'left', 'right','in',
28498                              'bs-tooltip-top','bs-tooltip-bottom', 'bs-tooltip-left', 'bs-tooltip-right']);
28499         
28500         var placement = typeof this.placement == 'function' ?
28501             this.placement.call(this, this.el, on_el) :
28502             this.placement;
28503             
28504         var autoToken = /\s?auto?\s?/i;
28505         var autoPlace = autoToken.test(placement);
28506         if (autoPlace) {
28507             placement = placement.replace(autoToken, '') || 'top';
28508         }
28509         
28510         //this.el.detach()
28511         //this.el.setXY([0,0]);
28512         this.el.show();
28513         //this.el.dom.style.display='block';
28514         
28515         //this.el.appendTo(on_el);
28516         
28517         var p = this.getPosition();
28518         var box = this.el.getBox();
28519         
28520         if (autoPlace) {
28521             // fixme..
28522         }
28523         
28524         var align = this.alignment[placement];
28525         
28526         var xy = this.el.getAlignToXY(this.bindEl, align[0], align[1]);
28527         
28528         if(placement == 'top' || placement == 'bottom'){
28529             if(xy[0] < 0){
28530                 placement = 'right';
28531             }
28532             
28533             if(xy[0] + this.el.getWidth() > Roo.lib.Dom.getViewWidth()){
28534                 placement = 'left';
28535             }
28536             
28537             var scroll = Roo.select('body', true).first().getScroll();
28538             
28539             if(xy[1] > Roo.lib.Dom.getViewHeight() + scroll.top - this.el.getHeight()){
28540                 placement = 'top';
28541             }
28542             
28543             align = this.alignment[placement];
28544             
28545             this.arrowEl.setLeft((this.innerEl.getWidth()/2) - 5);
28546             
28547         }
28548         
28549         this.el.alignTo(this.bindEl, align[0],align[1]);
28550         //var arrow = this.el.select('.arrow',true).first();
28551         //arrow.set(align[2], 
28552         
28553         this.el.addClass(placement);
28554         this.el.addClass("bs-tooltip-"+ placement);
28555         
28556         this.el.addClass('in fade show');
28557         
28558         this.hoverState = null;
28559         
28560         if (this.el.hasClass('fade')) {
28561             // fade it?
28562         }
28563         
28564         
28565         
28566         
28567         
28568     },
28569     hide : function()
28570     {
28571          
28572         if (!this.el) {
28573             return;
28574         }
28575         //this.el.setXY([0,0]);
28576         this.el.removeClass(['show', 'in']);
28577         //this.el.hide();
28578         
28579     }
28580     
28581 });
28582  
28583
28584  /*
28585  * - LGPL
28586  *
28587  * Location Picker
28588  * 
28589  */
28590
28591 /**
28592  * @class Roo.bootstrap.LocationPicker
28593  * @extends Roo.bootstrap.Component
28594  * Bootstrap LocationPicker class
28595  * @cfg {Number} latitude Position when init default 0
28596  * @cfg {Number} longitude Position when init default 0
28597  * @cfg {Number} zoom default 15
28598  * @cfg {String} mapTypeId default google.maps.MapTypeId.ROADMAP
28599  * @cfg {Boolean} mapTypeControl default false
28600  * @cfg {Boolean} disableDoubleClickZoom default false
28601  * @cfg {Boolean} scrollwheel default true
28602  * @cfg {Boolean} streetViewControl default false
28603  * @cfg {Number} radius default 0
28604  * @cfg {String} locationName
28605  * @cfg {Boolean} draggable default true
28606  * @cfg {Boolean} enableAutocomplete default false
28607  * @cfg {Boolean} enableReverseGeocode default true
28608  * @cfg {String} markerTitle
28609  * 
28610  * @constructor
28611  * Create a new LocationPicker
28612  * @param {Object} config The config object
28613  */
28614
28615
28616 Roo.bootstrap.LocationPicker = function(config){
28617     
28618     Roo.bootstrap.LocationPicker.superclass.constructor.call(this, config);
28619     
28620     this.addEvents({
28621         /**
28622          * @event initial
28623          * Fires when the picker initialized.
28624          * @param {Roo.bootstrap.LocationPicker} this
28625          * @param {Google Location} location
28626          */
28627         initial : true,
28628         /**
28629          * @event positionchanged
28630          * Fires when the picker position changed.
28631          * @param {Roo.bootstrap.LocationPicker} this
28632          * @param {Google Location} location
28633          */
28634         positionchanged : true,
28635         /**
28636          * @event resize
28637          * Fires when the map resize.
28638          * @param {Roo.bootstrap.LocationPicker} this
28639          */
28640         resize : true,
28641         /**
28642          * @event show
28643          * Fires when the map show.
28644          * @param {Roo.bootstrap.LocationPicker} this
28645          */
28646         show : true,
28647         /**
28648          * @event hide
28649          * Fires when the map hide.
28650          * @param {Roo.bootstrap.LocationPicker} this
28651          */
28652         hide : true,
28653         /**
28654          * @event mapClick
28655          * Fires when click the map.
28656          * @param {Roo.bootstrap.LocationPicker} this
28657          * @param {Map event} e
28658          */
28659         mapClick : true,
28660         /**
28661          * @event mapRightClick
28662          * Fires when right click the map.
28663          * @param {Roo.bootstrap.LocationPicker} this
28664          * @param {Map event} e
28665          */
28666         mapRightClick : true,
28667         /**
28668          * @event markerClick
28669          * Fires when click the marker.
28670          * @param {Roo.bootstrap.LocationPicker} this
28671          * @param {Map event} e
28672          */
28673         markerClick : true,
28674         /**
28675          * @event markerRightClick
28676          * Fires when right click the marker.
28677          * @param {Roo.bootstrap.LocationPicker} this
28678          * @param {Map event} e
28679          */
28680         markerRightClick : true,
28681         /**
28682          * @event OverlayViewDraw
28683          * Fires when OverlayView Draw
28684          * @param {Roo.bootstrap.LocationPicker} this
28685          */
28686         OverlayViewDraw : true,
28687         /**
28688          * @event OverlayViewOnAdd
28689          * Fires when OverlayView Draw
28690          * @param {Roo.bootstrap.LocationPicker} this
28691          */
28692         OverlayViewOnAdd : true,
28693         /**
28694          * @event OverlayViewOnRemove
28695          * Fires when OverlayView Draw
28696          * @param {Roo.bootstrap.LocationPicker} this
28697          */
28698         OverlayViewOnRemove : true,
28699         /**
28700          * @event OverlayViewShow
28701          * Fires when OverlayView Draw
28702          * @param {Roo.bootstrap.LocationPicker} this
28703          * @param {Pixel} cpx
28704          */
28705         OverlayViewShow : true,
28706         /**
28707          * @event OverlayViewHide
28708          * Fires when OverlayView Draw
28709          * @param {Roo.bootstrap.LocationPicker} this
28710          */
28711         OverlayViewHide : true,
28712         /**
28713          * @event loadexception
28714          * Fires when load google lib failed.
28715          * @param {Roo.bootstrap.LocationPicker} this
28716          */
28717         loadexception : true
28718     });
28719         
28720 };
28721
28722 Roo.extend(Roo.bootstrap.LocationPicker, Roo.bootstrap.Component,  {
28723     
28724     gMapContext: false,
28725     
28726     latitude: 0,
28727     longitude: 0,
28728     zoom: 15,
28729     mapTypeId: false,
28730     mapTypeControl: false,
28731     disableDoubleClickZoom: false,
28732     scrollwheel: true,
28733     streetViewControl: false,
28734     radius: 0,
28735     locationName: '',
28736     draggable: true,
28737     enableAutocomplete: false,
28738     enableReverseGeocode: true,
28739     markerTitle: '',
28740     
28741     getAutoCreate: function()
28742     {
28743
28744         var cfg = {
28745             tag: 'div',
28746             cls: 'roo-location-picker'
28747         };
28748         
28749         return cfg
28750     },
28751     
28752     initEvents: function(ct, position)
28753     {       
28754         if(!this.el.getWidth() || this.isApplied()){
28755             return;
28756         }
28757         
28758         this.el.setVisibilityMode(Roo.Element.DISPLAY);
28759         
28760         this.initial();
28761     },
28762     
28763     initial: function()
28764     {
28765         if(typeof(google) == 'undefined' || typeof(google.maps) == 'undefined'){
28766             this.fireEvent('loadexception', this);
28767             return;
28768         }
28769         
28770         if(!this.mapTypeId){
28771             this.mapTypeId = google.maps.MapTypeId.ROADMAP;
28772         }
28773         
28774         this.gMapContext = this.GMapContext();
28775         
28776         this.initOverlayView();
28777         
28778         this.OverlayView = new Roo.bootstrap.LocationPicker.OverlayView(this.gMapContext.map);
28779         
28780         var _this = this;
28781                 
28782         google.maps.event.addListener(this.gMapContext.marker, "dragend", function(event) {
28783             _this.setPosition(_this.gMapContext.marker.position);
28784         });
28785         
28786         google.maps.event.addListener(this.gMapContext.map, 'click', function(event){
28787             _this.fireEvent('mapClick', this, event);
28788             
28789         });
28790
28791         google.maps.event.addListener(this.gMapContext.map, 'rightclick', function(event){
28792             _this.fireEvent('mapRightClick', this, event);
28793             
28794         });
28795         
28796         google.maps.event.addListener(this.gMapContext.marker, 'click', function(event){
28797             _this.fireEvent('markerClick', this, event);
28798             
28799         });
28800
28801         google.maps.event.addListener(this.gMapContext.marker, 'rightclick', function(event){
28802             _this.fireEvent('markerRightClick', this, event);
28803             
28804         });
28805         
28806         this.setPosition(this.gMapContext.location);
28807         
28808         this.fireEvent('initial', this, this.gMapContext.location);
28809     },
28810     
28811     initOverlayView: function()
28812     {
28813         var _this = this;
28814         
28815         Roo.bootstrap.LocationPicker.OverlayView.prototype = Roo.apply(new google.maps.OverlayView(), {
28816             
28817             draw: function()
28818             {
28819                 _this.fireEvent('OverlayViewDraw', _this);
28820             },
28821             
28822             onAdd: function()
28823             {
28824                 _this.fireEvent('OverlayViewOnAdd', _this);
28825             },
28826             
28827             onRemove: function()
28828             {
28829                 _this.fireEvent('OverlayViewOnRemove', _this);
28830             },
28831             
28832             show: function(cpx)
28833             {
28834                 _this.fireEvent('OverlayViewShow', _this, cpx);
28835             },
28836             
28837             hide: function()
28838             {
28839                 _this.fireEvent('OverlayViewHide', _this);
28840             }
28841             
28842         });
28843     },
28844     
28845     fromLatLngToContainerPixel: function(event)
28846     {
28847         return this.OverlayView.getProjection().fromLatLngToContainerPixel(event.latLng);
28848     },
28849     
28850     isApplied: function() 
28851     {
28852         return this.getGmapContext() == false ? false : true;
28853     },
28854     
28855     getGmapContext: function() 
28856     {
28857         return (typeof(this.gMapContext) == 'undefined') ? false : this.gMapContext;
28858     },
28859     
28860     GMapContext: function() 
28861     {
28862         var position = new google.maps.LatLng(this.latitude, this.longitude);
28863         
28864         var _map = new google.maps.Map(this.el.dom, {
28865             center: position,
28866             zoom: this.zoom,
28867             mapTypeId: this.mapTypeId,
28868             mapTypeControl: this.mapTypeControl,
28869             disableDoubleClickZoom: this.disableDoubleClickZoom,
28870             scrollwheel: this.scrollwheel,
28871             streetViewControl: this.streetViewControl,
28872             locationName: this.locationName,
28873             draggable: this.draggable,
28874             enableAutocomplete: this.enableAutocomplete,
28875             enableReverseGeocode: this.enableReverseGeocode
28876         });
28877         
28878         var _marker = new google.maps.Marker({
28879             position: position,
28880             map: _map,
28881             title: this.markerTitle,
28882             draggable: this.draggable
28883         });
28884         
28885         return {
28886             map: _map,
28887             marker: _marker,
28888             circle: null,
28889             location: position,
28890             radius: this.radius,
28891             locationName: this.locationName,
28892             addressComponents: {
28893                 formatted_address: null,
28894                 addressLine1: null,
28895                 addressLine2: null,
28896                 streetName: null,
28897                 streetNumber: null,
28898                 city: null,
28899                 district: null,
28900                 state: null,
28901                 stateOrProvince: null
28902             },
28903             settings: this,
28904             domContainer: this.el.dom,
28905             geodecoder: new google.maps.Geocoder()
28906         };
28907     },
28908     
28909     drawCircle: function(center, radius, options) 
28910     {
28911         if (this.gMapContext.circle != null) {
28912             this.gMapContext.circle.setMap(null);
28913         }
28914         if (radius > 0) {
28915             radius *= 1;
28916             options = Roo.apply({}, options, {
28917                 strokeColor: "#0000FF",
28918                 strokeOpacity: .35,
28919                 strokeWeight: 2,
28920                 fillColor: "#0000FF",
28921                 fillOpacity: .2
28922             });
28923             
28924             options.map = this.gMapContext.map;
28925             options.radius = radius;
28926             options.center = center;
28927             this.gMapContext.circle = new google.maps.Circle(options);
28928             return this.gMapContext.circle;
28929         }
28930         
28931         return null;
28932     },
28933     
28934     setPosition: function(location) 
28935     {
28936         this.gMapContext.location = location;
28937         this.gMapContext.marker.setPosition(location);
28938         this.gMapContext.map.panTo(location);
28939         this.drawCircle(location, this.gMapContext.radius, {});
28940         
28941         var _this = this;
28942         
28943         if (this.gMapContext.settings.enableReverseGeocode) {
28944             this.gMapContext.geodecoder.geocode({
28945                 latLng: this.gMapContext.location
28946             }, function(results, status) {
28947                 
28948                 if (status == google.maps.GeocoderStatus.OK && results.length > 0) {
28949                     _this.gMapContext.locationName = results[0].formatted_address;
28950                     _this.gMapContext.addressComponents = _this.address_component_from_google_geocode(results[0].address_components);
28951                     
28952                     _this.fireEvent('positionchanged', this, location);
28953                 }
28954             });
28955             
28956             return;
28957         }
28958         
28959         this.fireEvent('positionchanged', this, location);
28960     },
28961     
28962     resize: function()
28963     {
28964         google.maps.event.trigger(this.gMapContext.map, "resize");
28965         
28966         this.gMapContext.map.setCenter(this.gMapContext.marker.position);
28967         
28968         this.fireEvent('resize', this);
28969     },
28970     
28971     setPositionByLatLng: function(latitude, longitude)
28972     {
28973         this.setPosition(new google.maps.LatLng(latitude, longitude));
28974     },
28975     
28976     getCurrentPosition: function() 
28977     {
28978         return {
28979             latitude: this.gMapContext.location.lat(),
28980             longitude: this.gMapContext.location.lng()
28981         };
28982     },
28983     
28984     getAddressName: function() 
28985     {
28986         return this.gMapContext.locationName;
28987     },
28988     
28989     getAddressComponents: function() 
28990     {
28991         return this.gMapContext.addressComponents;
28992     },
28993     
28994     address_component_from_google_geocode: function(address_components) 
28995     {
28996         var result = {};
28997         
28998         for (var i = 0; i < address_components.length; i++) {
28999             var component = address_components[i];
29000             if (component.types.indexOf("postal_code") >= 0) {
29001                 result.postalCode = component.short_name;
29002             } else if (component.types.indexOf("street_number") >= 0) {
29003                 result.streetNumber = component.short_name;
29004             } else if (component.types.indexOf("route") >= 0) {
29005                 result.streetName = component.short_name;
29006             } else if (component.types.indexOf("neighborhood") >= 0) {
29007                 result.city = component.short_name;
29008             } else if (component.types.indexOf("locality") >= 0) {
29009                 result.city = component.short_name;
29010             } else if (component.types.indexOf("sublocality") >= 0) {
29011                 result.district = component.short_name;
29012             } else if (component.types.indexOf("administrative_area_level_1") >= 0) {
29013                 result.stateOrProvince = component.short_name;
29014             } else if (component.types.indexOf("country") >= 0) {
29015                 result.country = component.short_name;
29016             }
29017         }
29018         
29019         result.addressLine1 = [ result.streetNumber, result.streetName ].join(" ").trim();
29020         result.addressLine2 = "";
29021         return result;
29022     },
29023     
29024     setZoomLevel: function(zoom)
29025     {
29026         this.gMapContext.map.setZoom(zoom);
29027     },
29028     
29029     show: function()
29030     {
29031         if(!this.el){
29032             return;
29033         }
29034         
29035         this.el.show();
29036         
29037         this.resize();
29038         
29039         this.fireEvent('show', this);
29040     },
29041     
29042     hide: function()
29043     {
29044         if(!this.el){
29045             return;
29046         }
29047         
29048         this.el.hide();
29049         
29050         this.fireEvent('hide', this);
29051     }
29052     
29053 });
29054
29055 Roo.apply(Roo.bootstrap.LocationPicker, {
29056     
29057     OverlayView : function(map, options)
29058     {
29059         options = options || {};
29060         
29061         this.setMap(map);
29062     }
29063     
29064     
29065 });/**
29066  * @class Roo.bootstrap.Alert
29067  * @extends Roo.bootstrap.Component
29068  * Bootstrap Alert class - shows an alert area box
29069  * eg
29070  * <div class="alert alert-danger" role="alert"><span class="fa fa-exclamation-triangle"></span><span class="sr-only">Error:</span>
29071   Enter a valid email address
29072 </div>
29073  * @licence LGPL
29074  * @cfg {String} title The title of alert
29075  * @cfg {String} html The content of alert
29076  * @cfg {String} weight (  success | info | warning | danger )
29077  * @cfg {String} faicon font-awesomeicon
29078  * 
29079  * @constructor
29080  * Create a new alert
29081  * @param {Object} config The config object
29082  */
29083
29084
29085 Roo.bootstrap.Alert = function(config){
29086     Roo.bootstrap.Alert.superclass.constructor.call(this, config);
29087     
29088 };
29089
29090 Roo.extend(Roo.bootstrap.Alert, Roo.bootstrap.Component,  {
29091     
29092     title: '',
29093     html: '',
29094     weight: false,
29095     faicon: false,
29096     
29097     getAutoCreate : function()
29098     {
29099         
29100         var cfg = {
29101             tag : 'div',
29102             cls : 'alert',
29103             cn : [
29104                 {
29105                     tag : 'i',
29106                     cls : 'roo-alert-icon'
29107                     
29108                 },
29109                 {
29110                     tag : 'b',
29111                     cls : 'roo-alert-title',
29112                     html : this.title
29113                 },
29114                 {
29115                     tag : 'span',
29116                     cls : 'roo-alert-text',
29117                     html : this.html
29118                 }
29119             ]
29120         };
29121         
29122         if(this.faicon){
29123             cfg.cn[0].cls += ' fa ' + this.faicon;
29124         }
29125         
29126         if(this.weight){
29127             cfg.cls += ' alert-' + this.weight;
29128         }
29129         
29130         return cfg;
29131     },
29132     
29133     initEvents: function() 
29134     {
29135         this.el.setVisibilityMode(Roo.Element.DISPLAY);
29136     },
29137     
29138     setTitle : function(str)
29139     {
29140         this.el.select('.roo-alert-title',true).first().dom.innerHTML = str;
29141     },
29142     
29143     setText : function(str)
29144     {
29145         this.el.select('.roo-alert-text',true).first().dom.innerHTML = str;
29146     },
29147     
29148     setWeight : function(weight)
29149     {
29150         if(this.weight){
29151             this.el.select('.alert',true).first().removeClass('alert-' + this.weight);
29152         }
29153         
29154         this.weight = weight;
29155         
29156         this.el.select('.alert',true).first().addClass('alert-' + this.weight);
29157     },
29158     
29159     setIcon : function(icon)
29160     {
29161         if(this.faicon){
29162             this.el.select('.roo-alert-icon',true).first().removeClass(['fa', 'fa-' + this.faicon]);
29163         }
29164         
29165         this.faicon = icon;
29166         
29167         this.el.select('.roo-alert-icon',true).first().addClass(['fa', 'fa-' + this.faicon]);
29168     },
29169     
29170     hide: function() 
29171     {
29172         this.el.hide();   
29173     },
29174     
29175     show: function() 
29176     {  
29177         this.el.show();   
29178     }
29179     
29180 });
29181
29182  
29183 /*
29184 * Licence: LGPL
29185 */
29186
29187 /**
29188  * @class Roo.bootstrap.UploadCropbox
29189  * @extends Roo.bootstrap.Component
29190  * Bootstrap UploadCropbox class
29191  * @cfg {String} emptyText show when image has been loaded
29192  * @cfg {String} rotateNotify show when image too small to rotate
29193  * @cfg {Number} errorTimeout default 3000
29194  * @cfg {Number} minWidth default 300
29195  * @cfg {Number} minHeight default 300
29196  * @cfg {Array} buttons default ['rotateLeft', 'pictureBtn', 'rotateRight']
29197  * @cfg {Boolean} isDocument (true|false) default false
29198  * @cfg {String} url action url
29199  * @cfg {String} paramName default 'imageUpload'
29200  * @cfg {String} method default POST
29201  * @cfg {Boolean} loadMask (true|false) default true
29202  * @cfg {Boolean} loadingText default 'Loading...'
29203  * 
29204  * @constructor
29205  * Create a new UploadCropbox
29206  * @param {Object} config The config object
29207  */
29208
29209 Roo.bootstrap.UploadCropbox = function(config){
29210     Roo.bootstrap.UploadCropbox.superclass.constructor.call(this, config);
29211     
29212     this.addEvents({
29213         /**
29214          * @event beforeselectfile
29215          * Fire before select file
29216          * @param {Roo.bootstrap.UploadCropbox} this
29217          */
29218         "beforeselectfile" : true,
29219         /**
29220          * @event initial
29221          * Fire after initEvent
29222          * @param {Roo.bootstrap.UploadCropbox} this
29223          */
29224         "initial" : true,
29225         /**
29226          * @event crop
29227          * Fire after initEvent
29228          * @param {Roo.bootstrap.UploadCropbox} this
29229          * @param {String} data
29230          */
29231         "crop" : true,
29232         /**
29233          * @event prepare
29234          * Fire when preparing the file data
29235          * @param {Roo.bootstrap.UploadCropbox} this
29236          * @param {Object} file
29237          */
29238         "prepare" : true,
29239         /**
29240          * @event exception
29241          * Fire when get exception
29242          * @param {Roo.bootstrap.UploadCropbox} this
29243          * @param {XMLHttpRequest} xhr
29244          */
29245         "exception" : true,
29246         /**
29247          * @event beforeloadcanvas
29248          * Fire before load the canvas
29249          * @param {Roo.bootstrap.UploadCropbox} this
29250          * @param {String} src
29251          */
29252         "beforeloadcanvas" : true,
29253         /**
29254          * @event trash
29255          * Fire when trash image
29256          * @param {Roo.bootstrap.UploadCropbox} this
29257          */
29258         "trash" : true,
29259         /**
29260          * @event download
29261          * Fire when download the image
29262          * @param {Roo.bootstrap.UploadCropbox} this
29263          */
29264         "download" : true,
29265         /**
29266          * @event footerbuttonclick
29267          * Fire when footerbuttonclick
29268          * @param {Roo.bootstrap.UploadCropbox} this
29269          * @param {String} type
29270          */
29271         "footerbuttonclick" : true,
29272         /**
29273          * @event resize
29274          * Fire when resize
29275          * @param {Roo.bootstrap.UploadCropbox} this
29276          */
29277         "resize" : true,
29278         /**
29279          * @event rotate
29280          * Fire when rotate the image
29281          * @param {Roo.bootstrap.UploadCropbox} this
29282          * @param {String} pos
29283          */
29284         "rotate" : true,
29285         /**
29286          * @event inspect
29287          * Fire when inspect the file
29288          * @param {Roo.bootstrap.UploadCropbox} this
29289          * @param {Object} file
29290          */
29291         "inspect" : true,
29292         /**
29293          * @event upload
29294          * Fire when xhr upload the file
29295          * @param {Roo.bootstrap.UploadCropbox} this
29296          * @param {Object} data
29297          */
29298         "upload" : true,
29299         /**
29300          * @event arrange
29301          * Fire when arrange the file data
29302          * @param {Roo.bootstrap.UploadCropbox} this
29303          * @param {Object} formData
29304          */
29305         "arrange" : true
29306     });
29307     
29308     this.buttons = this.buttons || Roo.bootstrap.UploadCropbox.footer.STANDARD;
29309 };
29310
29311 Roo.extend(Roo.bootstrap.UploadCropbox, Roo.bootstrap.Component,  {
29312     
29313     emptyText : 'Click to upload image',
29314     rotateNotify : 'Image is too small to rotate',
29315     errorTimeout : 3000,
29316     scale : 0,
29317     baseScale : 1,
29318     rotate : 0,
29319     dragable : false,
29320     pinching : false,
29321     mouseX : 0,
29322     mouseY : 0,
29323     cropData : false,
29324     minWidth : 300,
29325     minHeight : 300,
29326     file : false,
29327     exif : {},
29328     baseRotate : 1,
29329     cropType : 'image/jpeg',
29330     buttons : false,
29331     canvasLoaded : false,
29332     isDocument : false,
29333     method : 'POST',
29334     paramName : 'imageUpload',
29335     loadMask : true,
29336     loadingText : 'Loading...',
29337     maskEl : false,
29338     
29339     getAutoCreate : function()
29340     {
29341         var cfg = {
29342             tag : 'div',
29343             cls : 'roo-upload-cropbox',
29344             cn : [
29345                 {
29346                     tag : 'input',
29347                     cls : 'roo-upload-cropbox-selector',
29348                     type : 'file'
29349                 },
29350                 {
29351                     tag : 'div',
29352                     cls : 'roo-upload-cropbox-body',
29353                     style : 'cursor:pointer',
29354                     cn : [
29355                         {
29356                             tag : 'div',
29357                             cls : 'roo-upload-cropbox-preview'
29358                         },
29359                         {
29360                             tag : 'div',
29361                             cls : 'roo-upload-cropbox-thumb'
29362                         },
29363                         {
29364                             tag : 'div',
29365                             cls : 'roo-upload-cropbox-empty-notify',
29366                             html : this.emptyText
29367                         },
29368                         {
29369                             tag : 'div',
29370                             cls : 'roo-upload-cropbox-error-notify alert alert-danger',
29371                             html : this.rotateNotify
29372                         }
29373                     ]
29374                 },
29375                 {
29376                     tag : 'div',
29377                     cls : 'roo-upload-cropbox-footer',
29378                     cn : {
29379                         tag : 'div',
29380                         cls : 'btn-group btn-group-justified roo-upload-cropbox-btn-group',
29381                         cn : []
29382                     }
29383                 }
29384             ]
29385         };
29386         
29387         return cfg;
29388     },
29389     
29390     onRender : function(ct, position)
29391     {
29392         Roo.bootstrap.UploadCropbox.superclass.onRender.call(this, ct, position);
29393         
29394         if (this.buttons.length) {
29395             
29396             Roo.each(this.buttons, function(bb) {
29397                 
29398                 var btn = this.el.select('.roo-upload-cropbox-footer div.roo-upload-cropbox-btn-group').first().createChild(bb);
29399                 
29400                 btn.on('click', this.onFooterButtonClick.createDelegate(this, [bb.action], true));
29401                 
29402             }, this);
29403         }
29404         
29405         if(this.loadMask){
29406             this.maskEl = this.el;
29407         }
29408     },
29409     
29410     initEvents : function()
29411     {
29412         this.urlAPI = (window.createObjectURL && window) || 
29413                                 (window.URL && URL.revokeObjectURL && URL) || 
29414                                 (window.webkitURL && webkitURL);
29415                         
29416         this.bodyEl = this.el.select('.roo-upload-cropbox-body', true).first();
29417         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29418         
29419         this.selectorEl = this.el.select('.roo-upload-cropbox-selector', true).first();
29420         this.selectorEl.hide();
29421         
29422         this.previewEl = this.el.select('.roo-upload-cropbox-preview', true).first();
29423         this.previewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29424         
29425         this.thumbEl = this.el.select('.roo-upload-cropbox-thumb', true).first();
29426         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29427         this.thumbEl.hide();
29428         
29429         this.notifyEl = this.el.select('.roo-upload-cropbox-empty-notify', true).first();
29430         this.notifyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29431         
29432         this.errorEl = this.el.select('.roo-upload-cropbox-error-notify', true).first();
29433         this.errorEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29434         this.errorEl.hide();
29435         
29436         this.footerEl = this.el.select('.roo-upload-cropbox-footer', true).first();
29437         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29438         this.footerEl.hide();
29439         
29440         this.setThumbBoxSize();
29441         
29442         this.bind();
29443         
29444         this.resize();
29445         
29446         this.fireEvent('initial', this);
29447     },
29448
29449     bind : function()
29450     {
29451         var _this = this;
29452         
29453         window.addEventListener("resize", function() { _this.resize(); } );
29454         
29455         this.bodyEl.on('click', this.beforeSelectFile, this);
29456         
29457         if(Roo.isTouch){
29458             this.bodyEl.on('touchstart', this.onTouchStart, this);
29459             this.bodyEl.on('touchmove', this.onTouchMove, this);
29460             this.bodyEl.on('touchend', this.onTouchEnd, this);
29461         }
29462         
29463         if(!Roo.isTouch){
29464             this.bodyEl.on('mousedown', this.onMouseDown, this);
29465             this.bodyEl.on('mousemove', this.onMouseMove, this);
29466             var mousewheel = (/Firefox/i.test(navigator.userAgent))? 'DOMMouseScroll' : 'mousewheel';
29467             this.bodyEl.on(mousewheel, this.onMouseWheel, this);
29468             Roo.get(document).on('mouseup', this.onMouseUp, this);
29469         }
29470         
29471         this.selectorEl.on('change', this.onFileSelected, this);
29472     },
29473     
29474     reset : function()
29475     {    
29476         this.scale = 0;
29477         this.baseScale = 1;
29478         this.rotate = 0;
29479         this.baseRotate = 1;
29480         this.dragable = false;
29481         this.pinching = false;
29482         this.mouseX = 0;
29483         this.mouseY = 0;
29484         this.cropData = false;
29485         this.notifyEl.dom.innerHTML = this.emptyText;
29486         
29487         this.selectorEl.dom.value = '';
29488         
29489     },
29490     
29491     resize : function()
29492     {
29493         if(this.fireEvent('resize', this) != false){
29494             this.setThumbBoxPosition();
29495             this.setCanvasPosition();
29496         }
29497     },
29498     
29499     onFooterButtonClick : function(e, el, o, type)
29500     {
29501         switch (type) {
29502             case 'rotate-left' :
29503                 this.onRotateLeft(e);
29504                 break;
29505             case 'rotate-right' :
29506                 this.onRotateRight(e);
29507                 break;
29508             case 'picture' :
29509                 this.beforeSelectFile(e);
29510                 break;
29511             case 'trash' :
29512                 this.trash(e);
29513                 break;
29514             case 'crop' :
29515                 this.crop(e);
29516                 break;
29517             case 'download' :
29518                 this.download(e);
29519                 break;
29520             default :
29521                 break;
29522         }
29523         
29524         this.fireEvent('footerbuttonclick', this, type);
29525     },
29526     
29527     beforeSelectFile : function(e)
29528     {
29529         e.preventDefault();
29530         
29531         if(this.fireEvent('beforeselectfile', this) != false){
29532             this.selectorEl.dom.click();
29533         }
29534     },
29535     
29536     onFileSelected : function(e)
29537     {
29538         e.preventDefault();
29539         
29540         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
29541             return;
29542         }
29543         
29544         var file = this.selectorEl.dom.files[0];
29545         
29546         if(this.fireEvent('inspect', this, file) != false){
29547             this.prepare(file);
29548         }
29549         
29550     },
29551     
29552     trash : function(e)
29553     {
29554         this.fireEvent('trash', this);
29555     },
29556     
29557     download : function(e)
29558     {
29559         this.fireEvent('download', this);
29560     },
29561     
29562     loadCanvas : function(src)
29563     {   
29564         if(this.fireEvent('beforeloadcanvas', this, src) != false){
29565             
29566             this.reset();
29567             
29568             this.imageEl = document.createElement('img');
29569             
29570             var _this = this;
29571             
29572             this.imageEl.addEventListener("load", function(){ _this.onLoadCanvas(); });
29573             
29574             this.imageEl.src = src;
29575         }
29576     },
29577     
29578     onLoadCanvas : function()
29579     {   
29580         this.imageEl.OriginWidth = this.imageEl.naturalWidth || this.imageEl.width;
29581         this.imageEl.OriginHeight = this.imageEl.naturalHeight || this.imageEl.height;
29582         
29583         this.bodyEl.un('click', this.beforeSelectFile, this);
29584         
29585         this.notifyEl.hide();
29586         this.thumbEl.show();
29587         this.footerEl.show();
29588         
29589         this.baseRotateLevel();
29590         
29591         if(this.isDocument){
29592             this.setThumbBoxSize();
29593         }
29594         
29595         this.setThumbBoxPosition();
29596         
29597         this.baseScaleLevel();
29598         
29599         this.draw();
29600         
29601         this.resize();
29602         
29603         this.canvasLoaded = true;
29604         
29605         if(this.loadMask){
29606             this.maskEl.unmask();
29607         }
29608         
29609     },
29610     
29611     setCanvasPosition : function()
29612     {   
29613         if(!this.canvasEl){
29614             return;
29615         }
29616         
29617         var pw = Math.ceil((this.bodyEl.getWidth() - this.canvasEl.width) / 2);
29618         var ph = Math.ceil((this.bodyEl.getHeight() - this.canvasEl.height) / 2);
29619         
29620         this.previewEl.setLeft(pw);
29621         this.previewEl.setTop(ph);
29622         
29623     },
29624     
29625     onMouseDown : function(e)
29626     {   
29627         e.stopEvent();
29628         
29629         this.dragable = true;
29630         this.pinching = false;
29631         
29632         if(this.isDocument && (this.canvasEl.width < this.thumbEl.getWidth() || this.canvasEl.height < this.thumbEl.getHeight())){
29633             this.dragable = false;
29634             return;
29635         }
29636         
29637         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
29638         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
29639         
29640     },
29641     
29642     onMouseMove : function(e)
29643     {   
29644         e.stopEvent();
29645         
29646         if(!this.canvasLoaded){
29647             return;
29648         }
29649         
29650         if (!this.dragable){
29651             return;
29652         }
29653         
29654         var minX = Math.ceil(this.thumbEl.getLeft(true));
29655         var minY = Math.ceil(this.thumbEl.getTop(true));
29656         
29657         var maxX = Math.ceil(minX + this.thumbEl.getWidth() - this.canvasEl.width);
29658         var maxY = Math.ceil(minY + this.thumbEl.getHeight() - this.canvasEl.height);
29659         
29660         var x = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
29661         var y = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
29662         
29663         x = x - this.mouseX;
29664         y = y - this.mouseY;
29665         
29666         var bgX = Math.ceil(x + this.previewEl.getLeft(true));
29667         var bgY = Math.ceil(y + this.previewEl.getTop(true));
29668         
29669         bgX = (minX < bgX) ? minX : ((maxX > bgX) ? maxX : bgX);
29670         bgY = (minY < bgY) ? minY : ((maxY > bgY) ? maxY : bgY);
29671         
29672         this.previewEl.setLeft(bgX);
29673         this.previewEl.setTop(bgY);
29674         
29675         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
29676         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
29677     },
29678     
29679     onMouseUp : function(e)
29680     {   
29681         e.stopEvent();
29682         
29683         this.dragable = false;
29684     },
29685     
29686     onMouseWheel : function(e)
29687     {   
29688         e.stopEvent();
29689         
29690         this.startScale = this.scale;
29691         
29692         this.scale = (e.getWheelDelta() == 1) ? (this.scale + 1) : (this.scale - 1);
29693         
29694         if(!this.zoomable()){
29695             this.scale = this.startScale;
29696             return;
29697         }
29698         
29699         this.draw();
29700         
29701         return;
29702     },
29703     
29704     zoomable : function()
29705     {
29706         var minScale = this.thumbEl.getWidth() / this.minWidth;
29707         
29708         if(this.minWidth < this.minHeight){
29709             minScale = this.thumbEl.getHeight() / this.minHeight;
29710         }
29711         
29712         var width = Math.ceil(this.imageEl.OriginWidth * this.getScaleLevel() / minScale);
29713         var height = Math.ceil(this.imageEl.OriginHeight * this.getScaleLevel() / minScale);
29714         
29715         if(
29716                 this.isDocument &&
29717                 (this.rotate == 0 || this.rotate == 180) && 
29718                 (
29719                     width > this.imageEl.OriginWidth || 
29720                     height > this.imageEl.OriginHeight ||
29721                     (width < this.minWidth && height < this.minHeight)
29722                 )
29723         ){
29724             return false;
29725         }
29726         
29727         if(
29728                 this.isDocument &&
29729                 (this.rotate == 90 || this.rotate == 270) && 
29730                 (
29731                     width > this.imageEl.OriginWidth || 
29732                     height > this.imageEl.OriginHeight ||
29733                     (width < this.minHeight && height < this.minWidth)
29734                 )
29735         ){
29736             return false;
29737         }
29738         
29739         if(
29740                 !this.isDocument &&
29741                 (this.rotate == 0 || this.rotate == 180) && 
29742                 (
29743                     width < this.minWidth || 
29744                     width > this.imageEl.OriginWidth || 
29745                     height < this.minHeight || 
29746                     height > this.imageEl.OriginHeight
29747                 )
29748         ){
29749             return false;
29750         }
29751         
29752         if(
29753                 !this.isDocument &&
29754                 (this.rotate == 90 || this.rotate == 270) && 
29755                 (
29756                     width < this.minHeight || 
29757                     width > this.imageEl.OriginWidth || 
29758                     height < this.minWidth || 
29759                     height > this.imageEl.OriginHeight
29760                 )
29761         ){
29762             return false;
29763         }
29764         
29765         return true;
29766         
29767     },
29768     
29769     onRotateLeft : function(e)
29770     {   
29771         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
29772             
29773             var minScale = this.thumbEl.getWidth() / this.minWidth;
29774             
29775             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
29776             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
29777             
29778             this.startScale = this.scale;
29779             
29780             while (this.getScaleLevel() < minScale){
29781             
29782                 this.scale = this.scale + 1;
29783                 
29784                 if(!this.zoomable()){
29785                     break;
29786                 }
29787                 
29788                 if(
29789                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
29790                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
29791                 ){
29792                     continue;
29793                 }
29794                 
29795                 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
29796
29797                 this.draw();
29798                 
29799                 return;
29800             }
29801             
29802             this.scale = this.startScale;
29803             
29804             this.onRotateFail();
29805             
29806             return false;
29807         }
29808         
29809         this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
29810
29811         if(this.isDocument){
29812             this.setThumbBoxSize();
29813             this.setThumbBoxPosition();
29814             this.setCanvasPosition();
29815         }
29816         
29817         this.draw();
29818         
29819         this.fireEvent('rotate', this, 'left');
29820         
29821     },
29822     
29823     onRotateRight : function(e)
29824     {
29825         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
29826             
29827             var minScale = this.thumbEl.getWidth() / this.minWidth;
29828         
29829             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
29830             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
29831             
29832             this.startScale = this.scale;
29833             
29834             while (this.getScaleLevel() < minScale){
29835             
29836                 this.scale = this.scale + 1;
29837                 
29838                 if(!this.zoomable()){
29839                     break;
29840                 }
29841                 
29842                 if(
29843                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
29844                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
29845                 ){
29846                     continue;
29847                 }
29848                 
29849                 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
29850
29851                 this.draw();
29852                 
29853                 return;
29854             }
29855             
29856             this.scale = this.startScale;
29857             
29858             this.onRotateFail();
29859             
29860             return false;
29861         }
29862         
29863         this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
29864
29865         if(this.isDocument){
29866             this.setThumbBoxSize();
29867             this.setThumbBoxPosition();
29868             this.setCanvasPosition();
29869         }
29870         
29871         this.draw();
29872         
29873         this.fireEvent('rotate', this, 'right');
29874     },
29875     
29876     onRotateFail : function()
29877     {
29878         this.errorEl.show(true);
29879         
29880         var _this = this;
29881         
29882         (function() { _this.errorEl.hide(true); }).defer(this.errorTimeout);
29883     },
29884     
29885     draw : function()
29886     {
29887         this.previewEl.dom.innerHTML = '';
29888         
29889         var canvasEl = document.createElement("canvas");
29890         
29891         var contextEl = canvasEl.getContext("2d");
29892         
29893         canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
29894         canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
29895         var center = this.imageEl.OriginWidth / 2;
29896         
29897         if(this.imageEl.OriginWidth < this.imageEl.OriginHeight){
29898             canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
29899             canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
29900             center = this.imageEl.OriginHeight / 2;
29901         }
29902         
29903         contextEl.scale(this.getScaleLevel(), this.getScaleLevel());
29904         
29905         contextEl.translate(center, center);
29906         contextEl.rotate(this.rotate * Math.PI / 180);
29907
29908         contextEl.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
29909         
29910         this.canvasEl = document.createElement("canvas");
29911         
29912         this.contextEl = this.canvasEl.getContext("2d");
29913         
29914         switch (this.rotate) {
29915             case 0 :
29916                 
29917                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
29918                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
29919                 
29920                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
29921                 
29922                 break;
29923             case 90 : 
29924                 
29925                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
29926                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
29927                 
29928                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
29929                     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);
29930                     break;
29931                 }
29932                 
29933                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
29934                 
29935                 break;
29936             case 180 :
29937                 
29938                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
29939                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
29940                 
29941                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
29942                     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);
29943                     break;
29944                 }
29945                 
29946                 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);
29947                 
29948                 break;
29949             case 270 :
29950                 
29951                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
29952                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
29953         
29954                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
29955                     this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
29956                     break;
29957                 }
29958                 
29959                 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);
29960                 
29961                 break;
29962             default : 
29963                 break;
29964         }
29965         
29966         this.previewEl.appendChild(this.canvasEl);
29967         
29968         this.setCanvasPosition();
29969     },
29970     
29971     crop : function()
29972     {
29973         if(!this.canvasLoaded){
29974             return;
29975         }
29976         
29977         var imageCanvas = document.createElement("canvas");
29978         
29979         var imageContext = imageCanvas.getContext("2d");
29980         
29981         imageCanvas.width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
29982         imageCanvas.height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
29983         
29984         var center = imageCanvas.width / 2;
29985         
29986         imageContext.translate(center, center);
29987         
29988         imageContext.rotate(this.rotate * Math.PI / 180);
29989         
29990         imageContext.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
29991         
29992         var canvas = document.createElement("canvas");
29993         
29994         var context = canvas.getContext("2d");
29995                 
29996         canvas.width = this.minWidth;
29997         canvas.height = this.minHeight;
29998
29999         switch (this.rotate) {
30000             case 0 :
30001                 
30002                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
30003                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
30004                 
30005                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
30006                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
30007                 
30008                 var targetWidth = this.minWidth - 2 * x;
30009                 var targetHeight = this.minHeight - 2 * y;
30010                 
30011                 var scale = 1;
30012                 
30013                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
30014                     scale = targetWidth / width;
30015                 }
30016                 
30017                 if(x > 0 && y == 0){
30018                     scale = targetHeight / height;
30019                 }
30020                 
30021                 if(x > 0 && y > 0){
30022                     scale = targetWidth / width;
30023                     
30024                     if(width < height){
30025                         scale = targetHeight / height;
30026                     }
30027                 }
30028                 
30029                 context.scale(scale, scale);
30030                 
30031                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
30032                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
30033
30034                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
30035                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
30036
30037                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
30038                 
30039                 break;
30040             case 90 : 
30041                 
30042                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
30043                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
30044                 
30045                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
30046                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
30047                 
30048                 var targetWidth = this.minWidth - 2 * x;
30049                 var targetHeight = this.minHeight - 2 * y;
30050                 
30051                 var scale = 1;
30052                 
30053                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
30054                     scale = targetWidth / width;
30055                 }
30056                 
30057                 if(x > 0 && y == 0){
30058                     scale = targetHeight / height;
30059                 }
30060                 
30061                 if(x > 0 && y > 0){
30062                     scale = targetWidth / width;
30063                     
30064                     if(width < height){
30065                         scale = targetHeight / height;
30066                     }
30067                 }
30068                 
30069                 context.scale(scale, scale);
30070                 
30071                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
30072                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
30073
30074                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
30075                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
30076                 
30077                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
30078                 
30079                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
30080                 
30081                 break;
30082             case 180 :
30083                 
30084                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
30085                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
30086                 
30087                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
30088                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
30089                 
30090                 var targetWidth = this.minWidth - 2 * x;
30091                 var targetHeight = this.minHeight - 2 * y;
30092                 
30093                 var scale = 1;
30094                 
30095                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
30096                     scale = targetWidth / width;
30097                 }
30098                 
30099                 if(x > 0 && y == 0){
30100                     scale = targetHeight / height;
30101                 }
30102                 
30103                 if(x > 0 && y > 0){
30104                     scale = targetWidth / width;
30105                     
30106                     if(width < height){
30107                         scale = targetHeight / height;
30108                     }
30109                 }
30110                 
30111                 context.scale(scale, scale);
30112                 
30113                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
30114                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
30115
30116                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
30117                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
30118
30119                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
30120                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
30121                 
30122                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
30123                 
30124                 break;
30125             case 270 :
30126                 
30127                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
30128                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
30129                 
30130                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
30131                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
30132                 
30133                 var targetWidth = this.minWidth - 2 * x;
30134                 var targetHeight = this.minHeight - 2 * y;
30135                 
30136                 var scale = 1;
30137                 
30138                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
30139                     scale = targetWidth / width;
30140                 }
30141                 
30142                 if(x > 0 && y == 0){
30143                     scale = targetHeight / height;
30144                 }
30145                 
30146                 if(x > 0 && y > 0){
30147                     scale = targetWidth / width;
30148                     
30149                     if(width < height){
30150                         scale = targetHeight / height;
30151                     }
30152                 }
30153                 
30154                 context.scale(scale, scale);
30155                 
30156                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
30157                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
30158
30159                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
30160                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
30161                 
30162                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
30163                 
30164                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
30165                 
30166                 break;
30167             default : 
30168                 break;
30169         }
30170         
30171         this.cropData = canvas.toDataURL(this.cropType);
30172         
30173         if(this.fireEvent('crop', this, this.cropData) !== false){
30174             this.process(this.file, this.cropData);
30175         }
30176         
30177         return;
30178         
30179     },
30180     
30181     setThumbBoxSize : function()
30182     {
30183         var width, height;
30184         
30185         if(this.isDocument && typeof(this.imageEl) != 'undefined'){
30186             width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.max(this.minWidth, this.minHeight) : Math.min(this.minWidth, this.minHeight);
30187             height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.min(this.minWidth, this.minHeight) : Math.max(this.minWidth, this.minHeight);
30188             
30189             this.minWidth = width;
30190             this.minHeight = height;
30191             
30192             if(this.rotate == 90 || this.rotate == 270){
30193                 this.minWidth = height;
30194                 this.minHeight = width;
30195             }
30196         }
30197         
30198         height = 300;
30199         width = Math.ceil(this.minWidth * height / this.minHeight);
30200         
30201         if(this.minWidth > this.minHeight){
30202             width = 300;
30203             height = Math.ceil(this.minHeight * width / this.minWidth);
30204         }
30205         
30206         this.thumbEl.setStyle({
30207             width : width + 'px',
30208             height : height + 'px'
30209         });
30210
30211         return;
30212             
30213     },
30214     
30215     setThumbBoxPosition : function()
30216     {
30217         var x = Math.ceil((this.bodyEl.getWidth() - this.thumbEl.getWidth()) / 2 );
30218         var y = Math.ceil((this.bodyEl.getHeight() - this.thumbEl.getHeight()) / 2);
30219         
30220         this.thumbEl.setLeft(x);
30221         this.thumbEl.setTop(y);
30222         
30223     },
30224     
30225     baseRotateLevel : function()
30226     {
30227         this.baseRotate = 1;
30228         
30229         if(
30230                 typeof(this.exif) != 'undefined' &&
30231                 typeof(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != 'undefined' &&
30232                 [1, 3, 6, 8].indexOf(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != -1
30233         ){
30234             this.baseRotate = this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']];
30235         }
30236         
30237         this.rotate = Roo.bootstrap.UploadCropbox['Orientation'][this.baseRotate];
30238         
30239     },
30240     
30241     baseScaleLevel : function()
30242     {
30243         var width, height;
30244         
30245         if(this.isDocument){
30246             
30247             if(this.baseRotate == 6 || this.baseRotate == 8){
30248             
30249                 height = this.thumbEl.getHeight();
30250                 this.baseScale = height / this.imageEl.OriginWidth;
30251
30252                 if(this.imageEl.OriginHeight * this.baseScale > this.thumbEl.getWidth()){
30253                     width = this.thumbEl.getWidth();
30254                     this.baseScale = width / this.imageEl.OriginHeight;
30255                 }
30256
30257                 return;
30258             }
30259
30260             height = this.thumbEl.getHeight();
30261             this.baseScale = height / this.imageEl.OriginHeight;
30262
30263             if(this.imageEl.OriginWidth * this.baseScale > this.thumbEl.getWidth()){
30264                 width = this.thumbEl.getWidth();
30265                 this.baseScale = width / this.imageEl.OriginWidth;
30266             }
30267
30268             return;
30269         }
30270         
30271         if(this.baseRotate == 6 || this.baseRotate == 8){
30272             
30273             width = this.thumbEl.getHeight();
30274             this.baseScale = width / this.imageEl.OriginHeight;
30275             
30276             if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getWidth()){
30277                 height = this.thumbEl.getWidth();
30278                 this.baseScale = height / this.imageEl.OriginHeight;
30279             }
30280             
30281             if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
30282                 height = this.thumbEl.getWidth();
30283                 this.baseScale = height / this.imageEl.OriginHeight;
30284                 
30285                 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getHeight()){
30286                     width = this.thumbEl.getHeight();
30287                     this.baseScale = width / this.imageEl.OriginWidth;
30288                 }
30289             }
30290             
30291             return;
30292         }
30293         
30294         width = this.thumbEl.getWidth();
30295         this.baseScale = width / this.imageEl.OriginWidth;
30296         
30297         if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getHeight()){
30298             height = this.thumbEl.getHeight();
30299             this.baseScale = height / this.imageEl.OriginHeight;
30300         }
30301         
30302         if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
30303             
30304             height = this.thumbEl.getHeight();
30305             this.baseScale = height / this.imageEl.OriginHeight;
30306             
30307             if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getWidth()){
30308                 width = this.thumbEl.getWidth();
30309                 this.baseScale = width / this.imageEl.OriginWidth;
30310             }
30311             
30312         }
30313         
30314         return;
30315     },
30316     
30317     getScaleLevel : function()
30318     {
30319         return this.baseScale * Math.pow(1.1, this.scale);
30320     },
30321     
30322     onTouchStart : function(e)
30323     {
30324         if(!this.canvasLoaded){
30325             this.beforeSelectFile(e);
30326             return;
30327         }
30328         
30329         var touches = e.browserEvent.touches;
30330         
30331         if(!touches){
30332             return;
30333         }
30334         
30335         if(touches.length == 1){
30336             this.onMouseDown(e);
30337             return;
30338         }
30339         
30340         if(touches.length != 2){
30341             return;
30342         }
30343         
30344         var coords = [];
30345         
30346         for(var i = 0, finger; finger = touches[i]; i++){
30347             coords.push(finger.pageX, finger.pageY);
30348         }
30349         
30350         var x = Math.pow(coords[0] - coords[2], 2);
30351         var y = Math.pow(coords[1] - coords[3], 2);
30352         
30353         this.startDistance = Math.sqrt(x + y);
30354         
30355         this.startScale = this.scale;
30356         
30357         this.pinching = true;
30358         this.dragable = false;
30359         
30360     },
30361     
30362     onTouchMove : function(e)
30363     {
30364         if(!this.pinching && !this.dragable){
30365             return;
30366         }
30367         
30368         var touches = e.browserEvent.touches;
30369         
30370         if(!touches){
30371             return;
30372         }
30373         
30374         if(this.dragable){
30375             this.onMouseMove(e);
30376             return;
30377         }
30378         
30379         var coords = [];
30380         
30381         for(var i = 0, finger; finger = touches[i]; i++){
30382             coords.push(finger.pageX, finger.pageY);
30383         }
30384         
30385         var x = Math.pow(coords[0] - coords[2], 2);
30386         var y = Math.pow(coords[1] - coords[3], 2);
30387         
30388         this.endDistance = Math.sqrt(x + y);
30389         
30390         this.scale = this.startScale + Math.floor(Math.log(this.endDistance / this.startDistance) / Math.log(1.1));
30391         
30392         if(!this.zoomable()){
30393             this.scale = this.startScale;
30394             return;
30395         }
30396         
30397         this.draw();
30398         
30399     },
30400     
30401     onTouchEnd : function(e)
30402     {
30403         this.pinching = false;
30404         this.dragable = false;
30405         
30406     },
30407     
30408     process : function(file, crop)
30409     {
30410         if(this.loadMask){
30411             this.maskEl.mask(this.loadingText);
30412         }
30413         
30414         this.xhr = new XMLHttpRequest();
30415         
30416         file.xhr = this.xhr;
30417
30418         this.xhr.open(this.method, this.url, true);
30419         
30420         var headers = {
30421             "Accept": "application/json",
30422             "Cache-Control": "no-cache",
30423             "X-Requested-With": "XMLHttpRequest"
30424         };
30425         
30426         for (var headerName in headers) {
30427             var headerValue = headers[headerName];
30428             if (headerValue) {
30429                 this.xhr.setRequestHeader(headerName, headerValue);
30430             }
30431         }
30432         
30433         var _this = this;
30434         
30435         this.xhr.onload = function()
30436         {
30437             _this.xhrOnLoad(_this.xhr);
30438         }
30439         
30440         this.xhr.onerror = function()
30441         {
30442             _this.xhrOnError(_this.xhr);
30443         }
30444         
30445         var formData = new FormData();
30446
30447         formData.append('returnHTML', 'NO');
30448         
30449         if(crop){
30450             formData.append('crop', crop);
30451         }
30452         
30453         if(typeof(file) != 'undefined' && (typeof(file.id) == 'undefined' || file.id * 1 < 1)){
30454             formData.append(this.paramName, file, file.name);
30455         }
30456         
30457         if(typeof(file.filename) != 'undefined'){
30458             formData.append('filename', file.filename);
30459         }
30460         
30461         if(typeof(file.mimetype) != 'undefined'){
30462             formData.append('mimetype', file.mimetype);
30463         }
30464         
30465         if(this.fireEvent('arrange', this, formData) != false){
30466             this.xhr.send(formData);
30467         };
30468     },
30469     
30470     xhrOnLoad : function(xhr)
30471     {
30472         if(this.loadMask){
30473             this.maskEl.unmask();
30474         }
30475         
30476         if (xhr.readyState !== 4) {
30477             this.fireEvent('exception', this, xhr);
30478             return;
30479         }
30480
30481         var response = Roo.decode(xhr.responseText);
30482         
30483         if(!response.success){
30484             this.fireEvent('exception', this, xhr);
30485             return;
30486         }
30487         
30488         var response = Roo.decode(xhr.responseText);
30489         
30490         this.fireEvent('upload', this, response);
30491         
30492     },
30493     
30494     xhrOnError : function()
30495     {
30496         if(this.loadMask){
30497             this.maskEl.unmask();
30498         }
30499         
30500         Roo.log('xhr on error');
30501         
30502         var response = Roo.decode(xhr.responseText);
30503           
30504         Roo.log(response);
30505         
30506     },
30507     
30508     prepare : function(file)
30509     {   
30510         if(this.loadMask){
30511             this.maskEl.mask(this.loadingText);
30512         }
30513         
30514         this.file = false;
30515         this.exif = {};
30516         
30517         if(typeof(file) === 'string'){
30518             this.loadCanvas(file);
30519             return;
30520         }
30521         
30522         if(!file || !this.urlAPI){
30523             return;
30524         }
30525         
30526         this.file = file;
30527         this.cropType = file.type;
30528         
30529         var _this = this;
30530         
30531         if(this.fireEvent('prepare', this, this.file) != false){
30532             
30533             var reader = new FileReader();
30534             
30535             reader.onload = function (e) {
30536                 if (e.target.error) {
30537                     Roo.log(e.target.error);
30538                     return;
30539                 }
30540                 
30541                 var buffer = e.target.result,
30542                     dataView = new DataView(buffer),
30543                     offset = 2,
30544                     maxOffset = dataView.byteLength - 4,
30545                     markerBytes,
30546                     markerLength;
30547                 
30548                 if (dataView.getUint16(0) === 0xffd8) {
30549                     while (offset < maxOffset) {
30550                         markerBytes = dataView.getUint16(offset);
30551                         
30552                         if ((markerBytes >= 0xffe0 && markerBytes <= 0xffef) || markerBytes === 0xfffe) {
30553                             markerLength = dataView.getUint16(offset + 2) + 2;
30554                             if (offset + markerLength > dataView.byteLength) {
30555                                 Roo.log('Invalid meta data: Invalid segment size.');
30556                                 break;
30557                             }
30558                             
30559                             if(markerBytes == 0xffe1){
30560                                 _this.parseExifData(
30561                                     dataView,
30562                                     offset,
30563                                     markerLength
30564                                 );
30565                             }
30566                             
30567                             offset += markerLength;
30568                             
30569                             continue;
30570                         }
30571                         
30572                         break;
30573                     }
30574                     
30575                 }
30576                 
30577                 var url = _this.urlAPI.createObjectURL(_this.file);
30578                 
30579                 _this.loadCanvas(url);
30580                 
30581                 return;
30582             }
30583             
30584             reader.readAsArrayBuffer(this.file);
30585             
30586         }
30587         
30588     },
30589     
30590     parseExifData : function(dataView, offset, length)
30591     {
30592         var tiffOffset = offset + 10,
30593             littleEndian,
30594             dirOffset;
30595     
30596         if (dataView.getUint32(offset + 4) !== 0x45786966) {
30597             // No Exif data, might be XMP data instead
30598             return;
30599         }
30600         
30601         // Check for the ASCII code for "Exif" (0x45786966):
30602         if (dataView.getUint32(offset + 4) !== 0x45786966) {
30603             // No Exif data, might be XMP data instead
30604             return;
30605         }
30606         if (tiffOffset + 8 > dataView.byteLength) {
30607             Roo.log('Invalid Exif data: Invalid segment size.');
30608             return;
30609         }
30610         // Check for the two null bytes:
30611         if (dataView.getUint16(offset + 8) !== 0x0000) {
30612             Roo.log('Invalid Exif data: Missing byte alignment offset.');
30613             return;
30614         }
30615         // Check the byte alignment:
30616         switch (dataView.getUint16(tiffOffset)) {
30617         case 0x4949:
30618             littleEndian = true;
30619             break;
30620         case 0x4D4D:
30621             littleEndian = false;
30622             break;
30623         default:
30624             Roo.log('Invalid Exif data: Invalid byte alignment marker.');
30625             return;
30626         }
30627         // Check for the TIFF tag marker (0x002A):
30628         if (dataView.getUint16(tiffOffset + 2, littleEndian) !== 0x002A) {
30629             Roo.log('Invalid Exif data: Missing TIFF marker.');
30630             return;
30631         }
30632         // Retrieve the directory offset bytes, usually 0x00000008 or 8 decimal:
30633         dirOffset = dataView.getUint32(tiffOffset + 4, littleEndian);
30634         
30635         this.parseExifTags(
30636             dataView,
30637             tiffOffset,
30638             tiffOffset + dirOffset,
30639             littleEndian
30640         );
30641     },
30642     
30643     parseExifTags : function(dataView, tiffOffset, dirOffset, littleEndian)
30644     {
30645         var tagsNumber,
30646             dirEndOffset,
30647             i;
30648         if (dirOffset + 6 > dataView.byteLength) {
30649             Roo.log('Invalid Exif data: Invalid directory offset.');
30650             return;
30651         }
30652         tagsNumber = dataView.getUint16(dirOffset, littleEndian);
30653         dirEndOffset = dirOffset + 2 + 12 * tagsNumber;
30654         if (dirEndOffset + 4 > dataView.byteLength) {
30655             Roo.log('Invalid Exif data: Invalid directory size.');
30656             return;
30657         }
30658         for (i = 0; i < tagsNumber; i += 1) {
30659             this.parseExifTag(
30660                 dataView,
30661                 tiffOffset,
30662                 dirOffset + 2 + 12 * i, // tag offset
30663                 littleEndian
30664             );
30665         }
30666         // Return the offset to the next directory:
30667         return dataView.getUint32(dirEndOffset, littleEndian);
30668     },
30669     
30670     parseExifTag : function (dataView, tiffOffset, offset, littleEndian) 
30671     {
30672         var tag = dataView.getUint16(offset, littleEndian);
30673         
30674         this.exif[tag] = this.getExifValue(
30675             dataView,
30676             tiffOffset,
30677             offset,
30678             dataView.getUint16(offset + 2, littleEndian), // tag type
30679             dataView.getUint32(offset + 4, littleEndian), // tag length
30680             littleEndian
30681         );
30682     },
30683     
30684     getExifValue : function (dataView, tiffOffset, offset, type, length, littleEndian)
30685     {
30686         var tagType = Roo.bootstrap.UploadCropbox.exifTagTypes[type],
30687             tagSize,
30688             dataOffset,
30689             values,
30690             i,
30691             str,
30692             c;
30693     
30694         if (!tagType) {
30695             Roo.log('Invalid Exif data: Invalid tag type.');
30696             return;
30697         }
30698         
30699         tagSize = tagType.size * length;
30700         // Determine if the value is contained in the dataOffset bytes,
30701         // or if the value at the dataOffset is a pointer to the actual data:
30702         dataOffset = tagSize > 4 ?
30703                 tiffOffset + dataView.getUint32(offset + 8, littleEndian) : (offset + 8);
30704         if (dataOffset + tagSize > dataView.byteLength) {
30705             Roo.log('Invalid Exif data: Invalid data offset.');
30706             return;
30707         }
30708         if (length === 1) {
30709             return tagType.getValue(dataView, dataOffset, littleEndian);
30710         }
30711         values = [];
30712         for (i = 0; i < length; i += 1) {
30713             values[i] = tagType.getValue(dataView, dataOffset + i * tagType.size, littleEndian);
30714         }
30715         
30716         if (tagType.ascii) {
30717             str = '';
30718             // Concatenate the chars:
30719             for (i = 0; i < values.length; i += 1) {
30720                 c = values[i];
30721                 // Ignore the terminating NULL byte(s):
30722                 if (c === '\u0000') {
30723                     break;
30724                 }
30725                 str += c;
30726             }
30727             return str;
30728         }
30729         return values;
30730     }
30731     
30732 });
30733
30734 Roo.apply(Roo.bootstrap.UploadCropbox, {
30735     tags : {
30736         'Orientation': 0x0112
30737     },
30738     
30739     Orientation: {
30740             1: 0, //'top-left',
30741 //            2: 'top-right',
30742             3: 180, //'bottom-right',
30743 //            4: 'bottom-left',
30744 //            5: 'left-top',
30745             6: 90, //'right-top',
30746 //            7: 'right-bottom',
30747             8: 270 //'left-bottom'
30748     },
30749     
30750     exifTagTypes : {
30751         // byte, 8-bit unsigned int:
30752         1: {
30753             getValue: function (dataView, dataOffset) {
30754                 return dataView.getUint8(dataOffset);
30755             },
30756             size: 1
30757         },
30758         // ascii, 8-bit byte:
30759         2: {
30760             getValue: function (dataView, dataOffset) {
30761                 return String.fromCharCode(dataView.getUint8(dataOffset));
30762             },
30763             size: 1,
30764             ascii: true
30765         },
30766         // short, 16 bit int:
30767         3: {
30768             getValue: function (dataView, dataOffset, littleEndian) {
30769                 return dataView.getUint16(dataOffset, littleEndian);
30770             },
30771             size: 2
30772         },
30773         // long, 32 bit int:
30774         4: {
30775             getValue: function (dataView, dataOffset, littleEndian) {
30776                 return dataView.getUint32(dataOffset, littleEndian);
30777             },
30778             size: 4
30779         },
30780         // rational = two long values, first is numerator, second is denominator:
30781         5: {
30782             getValue: function (dataView, dataOffset, littleEndian) {
30783                 return dataView.getUint32(dataOffset, littleEndian) /
30784                     dataView.getUint32(dataOffset + 4, littleEndian);
30785             },
30786             size: 8
30787         },
30788         // slong, 32 bit signed int:
30789         9: {
30790             getValue: function (dataView, dataOffset, littleEndian) {
30791                 return dataView.getInt32(dataOffset, littleEndian);
30792             },
30793             size: 4
30794         },
30795         // srational, two slongs, first is numerator, second is denominator:
30796         10: {
30797             getValue: function (dataView, dataOffset, littleEndian) {
30798                 return dataView.getInt32(dataOffset, littleEndian) /
30799                     dataView.getInt32(dataOffset + 4, littleEndian);
30800             },
30801             size: 8
30802         }
30803     },
30804     
30805     footer : {
30806         STANDARD : [
30807             {
30808                 tag : 'div',
30809                 cls : 'btn-group roo-upload-cropbox-rotate-left',
30810                 action : 'rotate-left',
30811                 cn : [
30812                     {
30813                         tag : 'button',
30814                         cls : 'btn btn-default',
30815                         html : '<i class="fa fa-undo"></i>'
30816                     }
30817                 ]
30818             },
30819             {
30820                 tag : 'div',
30821                 cls : 'btn-group roo-upload-cropbox-picture',
30822                 action : 'picture',
30823                 cn : [
30824                     {
30825                         tag : 'button',
30826                         cls : 'btn btn-default',
30827                         html : '<i class="fa fa-picture-o"></i>'
30828                     }
30829                 ]
30830             },
30831             {
30832                 tag : 'div',
30833                 cls : 'btn-group roo-upload-cropbox-rotate-right',
30834                 action : 'rotate-right',
30835                 cn : [
30836                     {
30837                         tag : 'button',
30838                         cls : 'btn btn-default',
30839                         html : '<i class="fa fa-repeat"></i>'
30840                     }
30841                 ]
30842             }
30843         ],
30844         DOCUMENT : [
30845             {
30846                 tag : 'div',
30847                 cls : 'btn-group roo-upload-cropbox-rotate-left',
30848                 action : 'rotate-left',
30849                 cn : [
30850                     {
30851                         tag : 'button',
30852                         cls : 'btn btn-default',
30853                         html : '<i class="fa fa-undo"></i>'
30854                     }
30855                 ]
30856             },
30857             {
30858                 tag : 'div',
30859                 cls : 'btn-group roo-upload-cropbox-download',
30860                 action : 'download',
30861                 cn : [
30862                     {
30863                         tag : 'button',
30864                         cls : 'btn btn-default',
30865                         html : '<i class="fa fa-download"></i>'
30866                     }
30867                 ]
30868             },
30869             {
30870                 tag : 'div',
30871                 cls : 'btn-group roo-upload-cropbox-crop',
30872                 action : 'crop',
30873                 cn : [
30874                     {
30875                         tag : 'button',
30876                         cls : 'btn btn-default',
30877                         html : '<i class="fa fa-crop"></i>'
30878                     }
30879                 ]
30880             },
30881             {
30882                 tag : 'div',
30883                 cls : 'btn-group roo-upload-cropbox-trash',
30884                 action : 'trash',
30885                 cn : [
30886                     {
30887                         tag : 'button',
30888                         cls : 'btn btn-default',
30889                         html : '<i class="fa fa-trash"></i>'
30890                     }
30891                 ]
30892             },
30893             {
30894                 tag : 'div',
30895                 cls : 'btn-group roo-upload-cropbox-rotate-right',
30896                 action : 'rotate-right',
30897                 cn : [
30898                     {
30899                         tag : 'button',
30900                         cls : 'btn btn-default',
30901                         html : '<i class="fa fa-repeat"></i>'
30902                     }
30903                 ]
30904             }
30905         ],
30906         ROTATOR : [
30907             {
30908                 tag : 'div',
30909                 cls : 'btn-group roo-upload-cropbox-rotate-left',
30910                 action : 'rotate-left',
30911                 cn : [
30912                     {
30913                         tag : 'button',
30914                         cls : 'btn btn-default',
30915                         html : '<i class="fa fa-undo"></i>'
30916                     }
30917                 ]
30918             },
30919             {
30920                 tag : 'div',
30921                 cls : 'btn-group roo-upload-cropbox-rotate-right',
30922                 action : 'rotate-right',
30923                 cn : [
30924                     {
30925                         tag : 'button',
30926                         cls : 'btn btn-default',
30927                         html : '<i class="fa fa-repeat"></i>'
30928                     }
30929                 ]
30930             }
30931         ]
30932     }
30933 });
30934
30935 /*
30936 * Licence: LGPL
30937 */
30938
30939 /**
30940  * @class Roo.bootstrap.DocumentManager
30941  * @extends Roo.bootstrap.Component
30942  * Bootstrap DocumentManager class
30943  * @cfg {String} paramName default 'imageUpload'
30944  * @cfg {String} toolTipName default 'filename'
30945  * @cfg {String} method default POST
30946  * @cfg {String} url action url
30947  * @cfg {Number} boxes number of boxes, 0 is no limit.. default 0
30948  * @cfg {Boolean} multiple multiple upload default true
30949  * @cfg {Number} thumbSize default 300
30950  * @cfg {String} fieldLabel
30951  * @cfg {Number} labelWidth default 4
30952  * @cfg {String} labelAlign (left|top) default left
30953  * @cfg {Boolean} editable (true|false) allow edit when upload a image default true
30954 * @cfg {Number} labellg set the width of label (1-12)
30955  * @cfg {Number} labelmd set the width of label (1-12)
30956  * @cfg {Number} labelsm set the width of label (1-12)
30957  * @cfg {Number} labelxs set the width of label (1-12)
30958  * 
30959  * @constructor
30960  * Create a new DocumentManager
30961  * @param {Object} config The config object
30962  */
30963
30964 Roo.bootstrap.DocumentManager = function(config){
30965     Roo.bootstrap.DocumentManager.superclass.constructor.call(this, config);
30966     
30967     this.files = [];
30968     this.delegates = [];
30969     
30970     this.addEvents({
30971         /**
30972          * @event initial
30973          * Fire when initial the DocumentManager
30974          * @param {Roo.bootstrap.DocumentManager} this
30975          */
30976         "initial" : true,
30977         /**
30978          * @event inspect
30979          * inspect selected file
30980          * @param {Roo.bootstrap.DocumentManager} this
30981          * @param {File} file
30982          */
30983         "inspect" : true,
30984         /**
30985          * @event exception
30986          * Fire when xhr load exception
30987          * @param {Roo.bootstrap.DocumentManager} this
30988          * @param {XMLHttpRequest} xhr
30989          */
30990         "exception" : true,
30991         /**
30992          * @event afterupload
30993          * Fire when xhr load exception
30994          * @param {Roo.bootstrap.DocumentManager} this
30995          * @param {XMLHttpRequest} xhr
30996          */
30997         "afterupload" : true,
30998         /**
30999          * @event prepare
31000          * prepare the form data
31001          * @param {Roo.bootstrap.DocumentManager} this
31002          * @param {Object} formData
31003          */
31004         "prepare" : true,
31005         /**
31006          * @event remove
31007          * Fire when remove the file
31008          * @param {Roo.bootstrap.DocumentManager} this
31009          * @param {Object} file
31010          */
31011         "remove" : true,
31012         /**
31013          * @event refresh
31014          * Fire after refresh the file
31015          * @param {Roo.bootstrap.DocumentManager} this
31016          */
31017         "refresh" : true,
31018         /**
31019          * @event click
31020          * Fire after click the image
31021          * @param {Roo.bootstrap.DocumentManager} this
31022          * @param {Object} file
31023          */
31024         "click" : true,
31025         /**
31026          * @event edit
31027          * Fire when upload a image and editable set to true
31028          * @param {Roo.bootstrap.DocumentManager} this
31029          * @param {Object} file
31030          */
31031         "edit" : true,
31032         /**
31033          * @event beforeselectfile
31034          * Fire before select file
31035          * @param {Roo.bootstrap.DocumentManager} this
31036          */
31037         "beforeselectfile" : true,
31038         /**
31039          * @event process
31040          * Fire before process file
31041          * @param {Roo.bootstrap.DocumentManager} this
31042          * @param {Object} file
31043          */
31044         "process" : true,
31045         /**
31046          * @event previewrendered
31047          * Fire when preview rendered
31048          * @param {Roo.bootstrap.DocumentManager} this
31049          * @param {Object} file
31050          */
31051         "previewrendered" : true,
31052         /**
31053          */
31054         "previewResize" : true
31055         
31056     });
31057 };
31058
31059 Roo.extend(Roo.bootstrap.DocumentManager, Roo.bootstrap.Component,  {
31060     
31061     boxes : 0,
31062     inputName : '',
31063     thumbSize : 300,
31064     multiple : true,
31065     files : false,
31066     method : 'POST',
31067     url : '',
31068     paramName : 'imageUpload',
31069     toolTipName : 'filename',
31070     fieldLabel : '',
31071     labelWidth : 4,
31072     labelAlign : 'left',
31073     editable : true,
31074     delegates : false,
31075     xhr : false, 
31076     
31077     labellg : 0,
31078     labelmd : 0,
31079     labelsm : 0,
31080     labelxs : 0,
31081     
31082     getAutoCreate : function()
31083     {   
31084         var managerWidget = {
31085             tag : 'div',
31086             cls : 'roo-document-manager',
31087             cn : [
31088                 {
31089                     tag : 'input',
31090                     cls : 'roo-document-manager-selector',
31091                     type : 'file'
31092                 },
31093                 {
31094                     tag : 'div',
31095                     cls : 'roo-document-manager-uploader',
31096                     cn : [
31097                         {
31098                             tag : 'div',
31099                             cls : 'roo-document-manager-upload-btn',
31100                             html : '<i class="fa fa-plus"></i>'
31101                         }
31102                     ]
31103                     
31104                 }
31105             ]
31106         };
31107         
31108         var content = [
31109             {
31110                 tag : 'div',
31111                 cls : 'column col-md-12',
31112                 cn : managerWidget
31113             }
31114         ];
31115         
31116         if(this.fieldLabel.length){
31117             
31118             content = [
31119                 {
31120                     tag : 'div',
31121                     cls : 'column col-md-12',
31122                     html : this.fieldLabel
31123                 },
31124                 {
31125                     tag : 'div',
31126                     cls : 'column col-md-12',
31127                     cn : managerWidget
31128                 }
31129             ];
31130
31131             if(this.labelAlign == 'left'){
31132                 content = [
31133                     {
31134                         tag : 'div',
31135                         cls : 'column',
31136                         html : this.fieldLabel
31137                     },
31138                     {
31139                         tag : 'div',
31140                         cls : 'column',
31141                         cn : managerWidget
31142                     }
31143                 ];
31144                 
31145                 if(this.labelWidth > 12){
31146                     content[0].style = "width: " + this.labelWidth + 'px';
31147                 }
31148
31149                 if(this.labelWidth < 13 && this.labelmd == 0){
31150                     this.labelmd = this.labelWidth;
31151                 }
31152
31153                 if(this.labellg > 0){
31154                     content[0].cls += ' col-lg-' + this.labellg;
31155                     content[1].cls += ' col-lg-' + (12 - this.labellg);
31156                 }
31157
31158                 if(this.labelmd > 0){
31159                     content[0].cls += ' col-md-' + this.labelmd;
31160                     content[1].cls += ' col-md-' + (12 - this.labelmd);
31161                 }
31162
31163                 if(this.labelsm > 0){
31164                     content[0].cls += ' col-sm-' + this.labelsm;
31165                     content[1].cls += ' col-sm-' + (12 - this.labelsm);
31166                 }
31167
31168                 if(this.labelxs > 0){
31169                     content[0].cls += ' col-xs-' + this.labelxs;
31170                     content[1].cls += ' col-xs-' + (12 - this.labelxs);
31171                 }
31172                 
31173             }
31174         }
31175         
31176         var cfg = {
31177             tag : 'div',
31178             cls : 'row clearfix',
31179             cn : content
31180         };
31181         
31182         return cfg;
31183         
31184     },
31185     
31186     initEvents : function()
31187     {
31188         this.managerEl = this.el.select('.roo-document-manager', true).first();
31189         this.managerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
31190         
31191         this.selectorEl = this.el.select('.roo-document-manager-selector', true).first();
31192         this.selectorEl.hide();
31193         
31194         if(this.multiple){
31195             this.selectorEl.attr('multiple', 'multiple');
31196         }
31197         
31198         this.selectorEl.on('change', this.onFileSelected, this);
31199         
31200         this.uploader = this.el.select('.roo-document-manager-uploader', true).first();
31201         this.uploader.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
31202         
31203         this.uploader.on('click', this.onUploaderClick, this);
31204         
31205         this.renderProgressDialog();
31206         
31207         var _this = this;
31208         
31209         window.addEventListener("resize", function() { _this.refresh(); } );
31210         
31211         this.fireEvent('initial', this);
31212     },
31213     
31214     renderProgressDialog : function()
31215     {
31216         var _this = this;
31217         
31218         this.progressDialog = new Roo.bootstrap.Modal({
31219             cls : 'roo-document-manager-progress-dialog',
31220             allow_close : false,
31221             animate : false,
31222             title : '',
31223             buttons : [
31224                 {
31225                     name  :'cancel',
31226                     weight : 'danger',
31227                     html : 'Cancel'
31228                 }
31229             ], 
31230             listeners : { 
31231                 btnclick : function() {
31232                     _this.uploadCancel();
31233                     this.hide();
31234                 }
31235             }
31236         });
31237          
31238         this.progressDialog.render(Roo.get(document.body));
31239          
31240         this.progress = new Roo.bootstrap.Progress({
31241             cls : 'roo-document-manager-progress',
31242             active : true,
31243             striped : true
31244         });
31245         
31246         this.progress.render(this.progressDialog.getChildContainer());
31247         
31248         this.progressBar = new Roo.bootstrap.ProgressBar({
31249             cls : 'roo-document-manager-progress-bar',
31250             aria_valuenow : 0,
31251             aria_valuemin : 0,
31252             aria_valuemax : 12,
31253             panel : 'success'
31254         });
31255         
31256         this.progressBar.render(this.progress.getChildContainer());
31257     },
31258     
31259     onUploaderClick : function(e)
31260     {
31261         e.preventDefault();
31262      
31263         if(this.fireEvent('beforeselectfile', this) != false){
31264             this.selectorEl.dom.click();
31265         }
31266         
31267     },
31268     
31269     onFileSelected : function(e)
31270     {
31271         e.preventDefault();
31272         
31273         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
31274             return;
31275         }
31276         
31277         Roo.each(this.selectorEl.dom.files, function(file){
31278             if(this.fireEvent('inspect', this, file) != false){
31279                 this.files.push(file);
31280             }
31281         }, this);
31282         
31283         this.queue();
31284         
31285     },
31286     
31287     queue : function()
31288     {
31289         this.selectorEl.dom.value = '';
31290         
31291         if(!this.files || !this.files.length){
31292             return;
31293         }
31294         
31295         if(this.boxes > 0 && this.files.length > this.boxes){
31296             this.files = this.files.slice(0, this.boxes);
31297         }
31298         
31299         this.uploader.show();
31300         
31301         if(this.boxes > 0 && this.files.length > this.boxes - 1){
31302             this.uploader.hide();
31303         }
31304         
31305         var _this = this;
31306         
31307         var files = [];
31308         
31309         var docs = [];
31310         
31311         Roo.each(this.files, function(file){
31312             
31313             if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
31314                 var f = this.renderPreview(file);
31315                 files.push(f);
31316                 return;
31317             }
31318             
31319             if(file.type.indexOf('image') != -1){
31320                 this.delegates.push(
31321                     (function(){
31322                         _this.process(file);
31323                     }).createDelegate(this)
31324                 );
31325         
31326                 return;
31327             }
31328             
31329             docs.push(
31330                 (function(){
31331                     _this.process(file);
31332                 }).createDelegate(this)
31333             );
31334             
31335         }, this);
31336         
31337         this.files = files;
31338         
31339         this.delegates = this.delegates.concat(docs);
31340         
31341         if(!this.delegates.length){
31342             this.refresh();
31343             return;
31344         }
31345         
31346         this.progressBar.aria_valuemax = this.delegates.length;
31347         
31348         this.arrange();
31349         
31350         return;
31351     },
31352     
31353     arrange : function()
31354     {
31355         if(!this.delegates.length){
31356             this.progressDialog.hide();
31357             this.refresh();
31358             return;
31359         }
31360         
31361         var delegate = this.delegates.shift();
31362         
31363         this.progressDialog.show();
31364         
31365         this.progressDialog.setTitle((this.progressBar.aria_valuemax - this.delegates.length) + ' / ' + this.progressBar.aria_valuemax);
31366         
31367         this.progressBar.update(this.progressBar.aria_valuemax - this.delegates.length);
31368         
31369         delegate();
31370     },
31371     
31372     refresh : function()
31373     {
31374         this.uploader.show();
31375         
31376         if(this.boxes > 0 && this.files.length > this.boxes - 1){
31377             this.uploader.hide();
31378         }
31379         
31380         Roo.isTouch ? this.closable(false) : this.closable(true);
31381         
31382         this.fireEvent('refresh', this);
31383     },
31384     
31385     onRemove : function(e, el, o)
31386     {
31387         e.preventDefault();
31388         
31389         this.fireEvent('remove', this, o);
31390         
31391     },
31392     
31393     remove : function(o)
31394     {
31395         var files = [];
31396         
31397         Roo.each(this.files, function(file){
31398             if(typeof(file.id) == 'undefined' || file.id * 1 < 1 || file.id != o.id){
31399                 files.push(file);
31400                 return;
31401             }
31402
31403             o.target.remove();
31404
31405         }, this);
31406         
31407         this.files = files;
31408         
31409         this.refresh();
31410     },
31411     
31412     clear : function()
31413     {
31414         Roo.each(this.files, function(file){
31415             if(!file.target){
31416                 return;
31417             }
31418             
31419             file.target.remove();
31420
31421         }, this);
31422         
31423         this.files = [];
31424         
31425         this.refresh();
31426     },
31427     
31428     onClick : function(e, el, o)
31429     {
31430         e.preventDefault();
31431         
31432         this.fireEvent('click', this, o);
31433         
31434     },
31435     
31436     closable : function(closable)
31437     {
31438         Roo.each(this.managerEl.select('.roo-document-manager-preview > button.close', true).elements, function(el){
31439             
31440             el.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
31441             
31442             if(closable){
31443                 el.show();
31444                 return;
31445             }
31446             
31447             el.hide();
31448             
31449         }, this);
31450     },
31451     
31452     xhrOnLoad : function(xhr)
31453     {
31454         Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
31455             el.remove();
31456         }, this);
31457         
31458         if (xhr.readyState !== 4) {
31459             this.arrange();
31460             this.fireEvent('exception', this, xhr);
31461             return;
31462         }
31463
31464         var response = Roo.decode(xhr.responseText);
31465         
31466         if(!response.success){
31467             this.arrange();
31468             this.fireEvent('exception', this, xhr);
31469             return;
31470         }
31471         
31472         var file = this.renderPreview(response.data);
31473         
31474         this.files.push(file);
31475         
31476         this.arrange();
31477         
31478         this.fireEvent('afterupload', this, xhr);
31479         
31480     },
31481     
31482     xhrOnError : function(xhr)
31483     {
31484         Roo.log('xhr on error');
31485         
31486         var response = Roo.decode(xhr.responseText);
31487           
31488         Roo.log(response);
31489         
31490         this.arrange();
31491     },
31492     
31493     process : function(file)
31494     {
31495         if(this.fireEvent('process', this, file) !== false){
31496             if(this.editable && file.type.indexOf('image') != -1){
31497                 this.fireEvent('edit', this, file);
31498                 return;
31499             }
31500
31501             this.uploadStart(file, false);
31502
31503             return;
31504         }
31505         
31506     },
31507     
31508     uploadStart : function(file, crop)
31509     {
31510         this.xhr = new XMLHttpRequest();
31511         
31512         if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
31513             this.arrange();
31514             return;
31515         }
31516         
31517         file.xhr = this.xhr;
31518             
31519         this.managerEl.createChild({
31520             tag : 'div',
31521             cls : 'roo-document-manager-loading',
31522             cn : [
31523                 {
31524                     tag : 'div',
31525                     tooltip : file.name,
31526                     cls : 'roo-document-manager-thumb',
31527                     html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
31528                 }
31529             ]
31530
31531         });
31532
31533         this.xhr.open(this.method, this.url, true);
31534         
31535         var headers = {
31536             "Accept": "application/json",
31537             "Cache-Control": "no-cache",
31538             "X-Requested-With": "XMLHttpRequest"
31539         };
31540         
31541         for (var headerName in headers) {
31542             var headerValue = headers[headerName];
31543             if (headerValue) {
31544                 this.xhr.setRequestHeader(headerName, headerValue);
31545             }
31546         }
31547         
31548         var _this = this;
31549         
31550         this.xhr.onload = function()
31551         {
31552             _this.xhrOnLoad(_this.xhr);
31553         }
31554         
31555         this.xhr.onerror = function()
31556         {
31557             _this.xhrOnError(_this.xhr);
31558         }
31559         
31560         var formData = new FormData();
31561
31562         formData.append('returnHTML', 'NO');
31563         
31564         if(crop){
31565             formData.append('crop', crop);
31566         }
31567         
31568         formData.append(this.paramName, file, file.name);
31569         
31570         var options = {
31571             file : file, 
31572             manually : false
31573         };
31574         
31575         if(this.fireEvent('prepare', this, formData, options) != false){
31576             
31577             if(options.manually){
31578                 return;
31579             }
31580             
31581             this.xhr.send(formData);
31582             return;
31583         };
31584         
31585         this.uploadCancel();
31586     },
31587     
31588     uploadCancel : function()
31589     {
31590         if (this.xhr) {
31591             this.xhr.abort();
31592         }
31593         
31594         this.delegates = [];
31595         
31596         Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
31597             el.remove();
31598         }, this);
31599         
31600         this.arrange();
31601     },
31602     
31603     renderPreview : function(file)
31604     {
31605         if(typeof(file.target) != 'undefined' && file.target){
31606             return file;
31607         }
31608         
31609         var img_src = encodeURI(baseURL +'/Images/Thumb/' + this.thumbSize + '/' + file.id + '/' + file.filename);
31610         
31611         var previewEl = this.managerEl.createChild({
31612             tag : 'div',
31613             cls : 'roo-document-manager-preview',
31614             cn : [
31615                 {
31616                     tag : 'div',
31617                     tooltip : file[this.toolTipName],
31618                     cls : 'roo-document-manager-thumb',
31619                     html : '<img tooltip="' + file[this.toolTipName] + '" src="' + img_src + '">'
31620                 },
31621                 {
31622                     tag : 'button',
31623                     cls : 'close',
31624                     html : '<i class="fa fa-times-circle"></i>'
31625                 }
31626             ]
31627         });
31628
31629         var close = previewEl.select('button.close', true).first();
31630
31631         close.on('click', this.onRemove, this, file);
31632
31633         file.target = previewEl;
31634
31635         var image = previewEl.select('img', true).first();
31636         
31637         var _this = this;
31638         
31639         image.dom.addEventListener("load", function(){ _this.onPreviewLoad(file, image); });
31640         
31641         image.on('click', this.onClick, this, file);
31642         
31643         this.fireEvent('previewrendered', this, file);
31644         
31645         return file;
31646         
31647     },
31648     
31649     onPreviewLoad : function(file, image)
31650     {
31651         if(typeof(file.target) == 'undefined' || !file.target){
31652             return;
31653         }
31654         
31655         var width = image.dom.naturalWidth || image.dom.width;
31656         var height = image.dom.naturalHeight || image.dom.height;
31657         
31658         if(!this.previewResize) {
31659             return;
31660         }
31661         
31662         if(width > height){
31663             file.target.addClass('wide');
31664             return;
31665         }
31666         
31667         file.target.addClass('tall');
31668         return;
31669         
31670     },
31671     
31672     uploadFromSource : function(file, crop)
31673     {
31674         this.xhr = new XMLHttpRequest();
31675         
31676         this.managerEl.createChild({
31677             tag : 'div',
31678             cls : 'roo-document-manager-loading',
31679             cn : [
31680                 {
31681                     tag : 'div',
31682                     tooltip : file.name,
31683                     cls : 'roo-document-manager-thumb',
31684                     html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
31685                 }
31686             ]
31687
31688         });
31689
31690         this.xhr.open(this.method, this.url, true);
31691         
31692         var headers = {
31693             "Accept": "application/json",
31694             "Cache-Control": "no-cache",
31695             "X-Requested-With": "XMLHttpRequest"
31696         };
31697         
31698         for (var headerName in headers) {
31699             var headerValue = headers[headerName];
31700             if (headerValue) {
31701                 this.xhr.setRequestHeader(headerName, headerValue);
31702             }
31703         }
31704         
31705         var _this = this;
31706         
31707         this.xhr.onload = function()
31708         {
31709             _this.xhrOnLoad(_this.xhr);
31710         }
31711         
31712         this.xhr.onerror = function()
31713         {
31714             _this.xhrOnError(_this.xhr);
31715         }
31716         
31717         var formData = new FormData();
31718
31719         formData.append('returnHTML', 'NO');
31720         
31721         formData.append('crop', crop);
31722         
31723         if(typeof(file.filename) != 'undefined'){
31724             formData.append('filename', file.filename);
31725         }
31726         
31727         if(typeof(file.mimetype) != 'undefined'){
31728             formData.append('mimetype', file.mimetype);
31729         }
31730         
31731         Roo.log(formData);
31732         
31733         if(this.fireEvent('prepare', this, formData) != false){
31734             this.xhr.send(formData);
31735         };
31736     }
31737 });
31738
31739 /*
31740 * Licence: LGPL
31741 */
31742
31743 /**
31744  * @class Roo.bootstrap.DocumentViewer
31745  * @extends Roo.bootstrap.Component
31746  * Bootstrap DocumentViewer class
31747  * @cfg {Boolean} showDownload (true|false) show download button (default true)
31748  * @cfg {Boolean} showTrash (true|false) show trash button (default true)
31749  * 
31750  * @constructor
31751  * Create a new DocumentViewer
31752  * @param {Object} config The config object
31753  */
31754
31755 Roo.bootstrap.DocumentViewer = function(config){
31756     Roo.bootstrap.DocumentViewer.superclass.constructor.call(this, config);
31757     
31758     this.addEvents({
31759         /**
31760          * @event initial
31761          * Fire after initEvent
31762          * @param {Roo.bootstrap.DocumentViewer} this
31763          */
31764         "initial" : true,
31765         /**
31766          * @event click
31767          * Fire after click
31768          * @param {Roo.bootstrap.DocumentViewer} this
31769          */
31770         "click" : true,
31771         /**
31772          * @event download
31773          * Fire after download button
31774          * @param {Roo.bootstrap.DocumentViewer} this
31775          */
31776         "download" : true,
31777         /**
31778          * @event trash
31779          * Fire after trash button
31780          * @param {Roo.bootstrap.DocumentViewer} this
31781          */
31782         "trash" : true
31783         
31784     });
31785 };
31786
31787 Roo.extend(Roo.bootstrap.DocumentViewer, Roo.bootstrap.Component,  {
31788     
31789     showDownload : true,
31790     
31791     showTrash : true,
31792     
31793     getAutoCreate : function()
31794     {
31795         var cfg = {
31796             tag : 'div',
31797             cls : 'roo-document-viewer',
31798             cn : [
31799                 {
31800                     tag : 'div',
31801                     cls : 'roo-document-viewer-body',
31802                     cn : [
31803                         {
31804                             tag : 'div',
31805                             cls : 'roo-document-viewer-thumb',
31806                             cn : [
31807                                 {
31808                                     tag : 'img',
31809                                     cls : 'roo-document-viewer-image'
31810                                 }
31811                             ]
31812                         }
31813                     ]
31814                 },
31815                 {
31816                     tag : 'div',
31817                     cls : 'roo-document-viewer-footer',
31818                     cn : {
31819                         tag : 'div',
31820                         cls : 'btn-group btn-group-justified roo-document-viewer-btn-group',
31821                         cn : [
31822                             {
31823                                 tag : 'div',
31824                                 cls : 'btn-group roo-document-viewer-download',
31825                                 cn : [
31826                                     {
31827                                         tag : 'button',
31828                                         cls : 'btn btn-default',
31829                                         html : '<i class="fa fa-download"></i>'
31830                                     }
31831                                 ]
31832                             },
31833                             {
31834                                 tag : 'div',
31835                                 cls : 'btn-group roo-document-viewer-trash',
31836                                 cn : [
31837                                     {
31838                                         tag : 'button',
31839                                         cls : 'btn btn-default',
31840                                         html : '<i class="fa fa-trash"></i>'
31841                                     }
31842                                 ]
31843                             }
31844                         ]
31845                     }
31846                 }
31847             ]
31848         };
31849         
31850         return cfg;
31851     },
31852     
31853     initEvents : function()
31854     {
31855         this.bodyEl = this.el.select('.roo-document-viewer-body', true).first();
31856         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
31857         
31858         this.thumbEl = this.el.select('.roo-document-viewer-thumb', true).first();
31859         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
31860         
31861         this.imageEl = this.el.select('.roo-document-viewer-image', true).first();
31862         this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
31863         
31864         this.footerEl = this.el.select('.roo-document-viewer-footer', true).first();
31865         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY);
31866         
31867         this.downloadBtn = this.el.select('.roo-document-viewer-download', true).first();
31868         this.downloadBtn.setVisibilityMode(Roo.Element.DISPLAY);
31869         
31870         this.trashBtn = this.el.select('.roo-document-viewer-trash', true).first();
31871         this.trashBtn.setVisibilityMode(Roo.Element.DISPLAY);
31872         
31873         this.bodyEl.on('click', this.onClick, this);
31874         this.downloadBtn.on('click', this.onDownload, this);
31875         this.trashBtn.on('click', this.onTrash, this);
31876         
31877         this.downloadBtn.hide();
31878         this.trashBtn.hide();
31879         
31880         if(this.showDownload){
31881             this.downloadBtn.show();
31882         }
31883         
31884         if(this.showTrash){
31885             this.trashBtn.show();
31886         }
31887         
31888         if(!this.showDownload && !this.showTrash) {
31889             this.footerEl.hide();
31890         }
31891         
31892     },
31893     
31894     initial : function()
31895     {
31896         this.fireEvent('initial', this);
31897         
31898     },
31899     
31900     onClick : function(e)
31901     {
31902         e.preventDefault();
31903         
31904         this.fireEvent('click', this);
31905     },
31906     
31907     onDownload : function(e)
31908     {
31909         e.preventDefault();
31910         
31911         this.fireEvent('download', this);
31912     },
31913     
31914     onTrash : function(e)
31915     {
31916         e.preventDefault();
31917         
31918         this.fireEvent('trash', this);
31919     }
31920     
31921 });
31922 /*
31923  * - LGPL
31924  *
31925  * nav progress bar
31926  * 
31927  */
31928
31929 /**
31930  * @class Roo.bootstrap.NavProgressBar
31931  * @extends Roo.bootstrap.Component
31932  * Bootstrap NavProgressBar class
31933  * 
31934  * @constructor
31935  * Create a new nav progress bar
31936  * @param {Object} config The config object
31937  */
31938
31939 Roo.bootstrap.NavProgressBar = function(config){
31940     Roo.bootstrap.NavProgressBar.superclass.constructor.call(this, config);
31941
31942     this.bullets = this.bullets || [];
31943    
31944 //    Roo.bootstrap.NavProgressBar.register(this);
31945      this.addEvents({
31946         /**
31947              * @event changed
31948              * Fires when the active item changes
31949              * @param {Roo.bootstrap.NavProgressBar} this
31950              * @param {Roo.bootstrap.NavProgressItem} selected The item selected
31951              * @param {Roo.bootstrap.NavProgressItem} prev The previously selected item 
31952          */
31953         'changed': true
31954      });
31955     
31956 };
31957
31958 Roo.extend(Roo.bootstrap.NavProgressBar, Roo.bootstrap.Component,  {
31959     
31960     bullets : [],
31961     barItems : [],
31962     
31963     getAutoCreate : function()
31964     {
31965         var cfg = Roo.apply({}, Roo.bootstrap.NavProgressBar.superclass.getAutoCreate.call(this));
31966         
31967         cfg = {
31968             tag : 'div',
31969             cls : 'roo-navigation-bar-group',
31970             cn : [
31971                 {
31972                     tag : 'div',
31973                     cls : 'roo-navigation-top-bar'
31974                 },
31975                 {
31976                     tag : 'div',
31977                     cls : 'roo-navigation-bullets-bar',
31978                     cn : [
31979                         {
31980                             tag : 'ul',
31981                             cls : 'roo-navigation-bar'
31982                         }
31983                     ]
31984                 },
31985                 
31986                 {
31987                     tag : 'div',
31988                     cls : 'roo-navigation-bottom-bar'
31989                 }
31990             ]
31991             
31992         };
31993         
31994         return cfg;
31995         
31996     },
31997     
31998     initEvents: function() 
31999     {
32000         
32001     },
32002     
32003     onRender : function(ct, position) 
32004     {
32005         Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
32006         
32007         if(this.bullets.length){
32008             Roo.each(this.bullets, function(b){
32009                this.addItem(b);
32010             }, this);
32011         }
32012         
32013         this.format();
32014         
32015     },
32016     
32017     addItem : function(cfg)
32018     {
32019         var item = new Roo.bootstrap.NavProgressItem(cfg);
32020         
32021         item.parentId = this.id;
32022         item.render(this.el.select('.roo-navigation-bar', true).first(), null);
32023         
32024         if(cfg.html){
32025             var top = new Roo.bootstrap.Element({
32026                 tag : 'div',
32027                 cls : 'roo-navigation-bar-text'
32028             });
32029             
32030             var bottom = new Roo.bootstrap.Element({
32031                 tag : 'div',
32032                 cls : 'roo-navigation-bar-text'
32033             });
32034             
32035             top.onRender(this.el.select('.roo-navigation-top-bar', true).first(), null);
32036             bottom.onRender(this.el.select('.roo-navigation-bottom-bar', true).first(), null);
32037             
32038             var topText = new Roo.bootstrap.Element({
32039                 tag : 'span',
32040                 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? cfg.html : ''
32041             });
32042             
32043             var bottomText = new Roo.bootstrap.Element({
32044                 tag : 'span',
32045                 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? '' : cfg.html
32046             });
32047             
32048             topText.onRender(top.el, null);
32049             bottomText.onRender(bottom.el, null);
32050             
32051             item.topEl = top;
32052             item.bottomEl = bottom;
32053         }
32054         
32055         this.barItems.push(item);
32056         
32057         return item;
32058     },
32059     
32060     getActive : function()
32061     {
32062         var active = false;
32063         
32064         Roo.each(this.barItems, function(v){
32065             
32066             if (!v.isActive()) {
32067                 return;
32068             }
32069             
32070             active = v;
32071             return false;
32072             
32073         });
32074         
32075         return active;
32076     },
32077     
32078     setActiveItem : function(item)
32079     {
32080         var prev = false;
32081         
32082         Roo.each(this.barItems, function(v){
32083             if (v.rid == item.rid) {
32084                 return ;
32085             }
32086             
32087             if (v.isActive()) {
32088                 v.setActive(false);
32089                 prev = v;
32090             }
32091         });
32092
32093         item.setActive(true);
32094         
32095         this.fireEvent('changed', this, item, prev);
32096     },
32097     
32098     getBarItem: function(rid)
32099     {
32100         var ret = false;
32101         
32102         Roo.each(this.barItems, function(e) {
32103             if (e.rid != rid) {
32104                 return;
32105             }
32106             
32107             ret =  e;
32108             return false;
32109         });
32110         
32111         return ret;
32112     },
32113     
32114     indexOfItem : function(item)
32115     {
32116         var index = false;
32117         
32118         Roo.each(this.barItems, function(v, i){
32119             
32120             if (v.rid != item.rid) {
32121                 return;
32122             }
32123             
32124             index = i;
32125             return false
32126         });
32127         
32128         return index;
32129     },
32130     
32131     setActiveNext : function()
32132     {
32133         var i = this.indexOfItem(this.getActive());
32134         
32135         if (i > this.barItems.length) {
32136             return;
32137         }
32138         
32139         this.setActiveItem(this.barItems[i+1]);
32140     },
32141     
32142     setActivePrev : function()
32143     {
32144         var i = this.indexOfItem(this.getActive());
32145         
32146         if (i  < 1) {
32147             return;
32148         }
32149         
32150         this.setActiveItem(this.barItems[i-1]);
32151     },
32152     
32153     format : function()
32154     {
32155         if(!this.barItems.length){
32156             return;
32157         }
32158      
32159         var width = 100 / this.barItems.length;
32160         
32161         Roo.each(this.barItems, function(i){
32162             i.el.setStyle('width', width + '%');
32163             i.topEl.el.setStyle('width', width + '%');
32164             i.bottomEl.el.setStyle('width', width + '%');
32165         }, this);
32166         
32167     }
32168     
32169 });
32170 /*
32171  * - LGPL
32172  *
32173  * Nav Progress Item
32174  * 
32175  */
32176
32177 /**
32178  * @class Roo.bootstrap.NavProgressItem
32179  * @extends Roo.bootstrap.Component
32180  * Bootstrap NavProgressItem class
32181  * @cfg {String} rid the reference id
32182  * @cfg {Boolean} active (true|false) Is item active default false
32183  * @cfg {Boolean} disabled (true|false) Is item active default false
32184  * @cfg {String} html
32185  * @cfg {String} position (top|bottom) text position default bottom
32186  * @cfg {String} icon show icon instead of number
32187  * 
32188  * @constructor
32189  * Create a new NavProgressItem
32190  * @param {Object} config The config object
32191  */
32192 Roo.bootstrap.NavProgressItem = function(config){
32193     Roo.bootstrap.NavProgressItem.superclass.constructor.call(this, config);
32194     this.addEvents({
32195         // raw events
32196         /**
32197          * @event click
32198          * The raw click event for the entire grid.
32199          * @param {Roo.bootstrap.NavProgressItem} this
32200          * @param {Roo.EventObject} e
32201          */
32202         "click" : true
32203     });
32204    
32205 };
32206
32207 Roo.extend(Roo.bootstrap.NavProgressItem, Roo.bootstrap.Component,  {
32208     
32209     rid : '',
32210     active : false,
32211     disabled : false,
32212     html : '',
32213     position : 'bottom',
32214     icon : false,
32215     
32216     getAutoCreate : function()
32217     {
32218         var iconCls = 'roo-navigation-bar-item-icon';
32219         
32220         iconCls += ((this.icon) ? (' ' + this.icon) : (' step-number')) ;
32221         
32222         var cfg = {
32223             tag: 'li',
32224             cls: 'roo-navigation-bar-item',
32225             cn : [
32226                 {
32227                     tag : 'i',
32228                     cls : iconCls
32229                 }
32230             ]
32231         };
32232         
32233         if(this.active){
32234             cfg.cls += ' active';
32235         }
32236         if(this.disabled){
32237             cfg.cls += ' disabled';
32238         }
32239         
32240         return cfg;
32241     },
32242     
32243     disable : function()
32244     {
32245         this.setDisabled(true);
32246     },
32247     
32248     enable : function()
32249     {
32250         this.setDisabled(false);
32251     },
32252     
32253     initEvents: function() 
32254     {
32255         this.iconEl = this.el.select('.roo-navigation-bar-item-icon', true).first();
32256         
32257         this.iconEl.on('click', this.onClick, this);
32258     },
32259     
32260     onClick : function(e)
32261     {
32262         e.preventDefault();
32263         
32264         if(this.disabled){
32265             return;
32266         }
32267         
32268         if(this.fireEvent('click', this, e) === false){
32269             return;
32270         };
32271         
32272         this.parent().setActiveItem(this);
32273     },
32274     
32275     isActive: function () 
32276     {
32277         return this.active;
32278     },
32279     
32280     setActive : function(state)
32281     {
32282         if(this.active == state){
32283             return;
32284         }
32285         
32286         this.active = state;
32287         
32288         if (state) {
32289             this.el.addClass('active');
32290             return;
32291         }
32292         
32293         this.el.removeClass('active');
32294         
32295         return;
32296     },
32297     
32298     setDisabled : function(state)
32299     {
32300         if(this.disabled == state){
32301             return;
32302         }
32303         
32304         this.disabled = state;
32305         
32306         if (state) {
32307             this.el.addClass('disabled');
32308             return;
32309         }
32310         
32311         this.el.removeClass('disabled');
32312     },
32313     
32314     tooltipEl : function()
32315     {
32316         return this.el.select('.roo-navigation-bar-item-icon', true).first();;
32317     }
32318 });
32319  
32320
32321  /*
32322  * - LGPL
32323  *
32324  * FieldLabel
32325  * 
32326  */
32327
32328 /**
32329  * @class Roo.bootstrap.FieldLabel
32330  * @extends Roo.bootstrap.Component
32331  * Bootstrap FieldLabel class
32332  * @cfg {String} html contents of the element
32333  * @cfg {String} tag tag of the element default label
32334  * @cfg {String} cls class of the element
32335  * @cfg {String} target label target 
32336  * @cfg {Boolean} allowBlank (true|false) target allowBlank default true
32337  * @cfg {String} invalidClass DEPRICATED - BS4 uses is-invalid
32338  * @cfg {String} validClass DEPRICATED - BS4 uses is-valid
32339  * @cfg {String} iconTooltip default "This field is required"
32340  * @cfg {String} indicatorpos (left|right) default left
32341  * 
32342  * @constructor
32343  * Create a new FieldLabel
32344  * @param {Object} config The config object
32345  */
32346
32347 Roo.bootstrap.FieldLabel = function(config){
32348     Roo.bootstrap.Element.superclass.constructor.call(this, config);
32349     
32350     this.addEvents({
32351             /**
32352              * @event invalid
32353              * Fires after the field has been marked as invalid.
32354              * @param {Roo.form.FieldLabel} this
32355              * @param {String} msg The validation message
32356              */
32357             invalid : true,
32358             /**
32359              * @event valid
32360              * Fires after the field has been validated with no errors.
32361              * @param {Roo.form.FieldLabel} this
32362              */
32363             valid : true
32364         });
32365 };
32366
32367 Roo.extend(Roo.bootstrap.FieldLabel, Roo.bootstrap.Component,  {
32368     
32369     tag: 'label',
32370     cls: '',
32371     html: '',
32372     target: '',
32373     allowBlank : true,
32374     invalidClass : 'has-warning',
32375     validClass : 'has-success',
32376     iconTooltip : 'This field is required',
32377     indicatorpos : 'left',
32378     
32379     getAutoCreate : function(){
32380         
32381         var cls = "";
32382         if (!this.allowBlank) {
32383             cls  = "visible";
32384         }
32385         
32386         var cfg = {
32387             tag : this.tag,
32388             cls : 'roo-bootstrap-field-label ' + this.cls,
32389             for : this.target,
32390             cn : [
32391                 {
32392                     tag : 'i',
32393                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star ' + cls,
32394                     tooltip : this.iconTooltip
32395                 },
32396                 {
32397                     tag : 'span',
32398                     html : this.html
32399                 }
32400             ] 
32401         };
32402         
32403         if(this.indicatorpos == 'right'){
32404             var cfg = {
32405                 tag : this.tag,
32406                 cls : 'roo-bootstrap-field-label ' + this.cls,
32407                 for : this.target,
32408                 cn : [
32409                     {
32410                         tag : 'span',
32411                         html : this.html
32412                     },
32413                     {
32414                         tag : 'i',
32415                         cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star '+ cls,
32416                         tooltip : this.iconTooltip
32417                     }
32418                 ] 
32419             };
32420         }
32421         
32422         return cfg;
32423     },
32424     
32425     initEvents: function() 
32426     {
32427         Roo.bootstrap.Element.superclass.initEvents.call(this);
32428         
32429         this.indicator = this.indicatorEl();
32430         
32431         if(this.indicator){
32432             this.indicator.removeClass('visible');
32433             this.indicator.addClass('invisible');
32434         }
32435         
32436         Roo.bootstrap.FieldLabel.register(this);
32437     },
32438     
32439     indicatorEl : function()
32440     {
32441         var indicator = this.el.select('i.roo-required-indicator',true).first();
32442         
32443         if(!indicator){
32444             return false;
32445         }
32446         
32447         return indicator;
32448         
32449     },
32450     
32451     /**
32452      * Mark this field as valid
32453      */
32454     markValid : function()
32455     {
32456         if(this.indicator){
32457             this.indicator.removeClass('visible');
32458             this.indicator.addClass('invisible');
32459         }
32460         if (Roo.bootstrap.version == 3) {
32461             this.el.removeClass(this.invalidClass);
32462             this.el.addClass(this.validClass);
32463         } else {
32464             this.el.removeClass('is-invalid');
32465             this.el.addClass('is-valid');
32466         }
32467         
32468         
32469         this.fireEvent('valid', this);
32470     },
32471     
32472     /**
32473      * Mark this field as invalid
32474      * @param {String} msg The validation message
32475      */
32476     markInvalid : function(msg)
32477     {
32478         if(this.indicator){
32479             this.indicator.removeClass('invisible');
32480             this.indicator.addClass('visible');
32481         }
32482           if (Roo.bootstrap.version == 3) {
32483             this.el.removeClass(this.validClass);
32484             this.el.addClass(this.invalidClass);
32485         } else {
32486             this.el.removeClass('is-valid');
32487             this.el.addClass('is-invalid');
32488         }
32489         
32490         
32491         this.fireEvent('invalid', this, msg);
32492     }
32493     
32494    
32495 });
32496
32497 Roo.apply(Roo.bootstrap.FieldLabel, {
32498     
32499     groups: {},
32500     
32501      /**
32502     * register a FieldLabel Group
32503     * @param {Roo.bootstrap.FieldLabel} the FieldLabel to add
32504     */
32505     register : function(label)
32506     {
32507         if(this.groups.hasOwnProperty(label.target)){
32508             return;
32509         }
32510      
32511         this.groups[label.target] = label;
32512         
32513     },
32514     /**
32515     * fetch a FieldLabel Group based on the target
32516     * @param {string} target
32517     * @returns {Roo.bootstrap.FieldLabel} the CheckBox group
32518     */
32519     get: function(target) {
32520         if (typeof(this.groups[target]) == 'undefined') {
32521             return false;
32522         }
32523         
32524         return this.groups[target] ;
32525     }
32526 });
32527
32528  
32529
32530  /*
32531  * - LGPL
32532  *
32533  * page DateSplitField.
32534  * 
32535  */
32536
32537
32538 /**
32539  * @class Roo.bootstrap.DateSplitField
32540  * @extends Roo.bootstrap.Component
32541  * Bootstrap DateSplitField class
32542  * @cfg {string} fieldLabel - the label associated
32543  * @cfg {Number} labelWidth set the width of label (0-12)
32544  * @cfg {String} labelAlign (top|left)
32545  * @cfg {Boolean} dayAllowBlank (true|false) default false
32546  * @cfg {Boolean} monthAllowBlank (true|false) default false
32547  * @cfg {Boolean} yearAllowBlank (true|false) default false
32548  * @cfg {string} dayPlaceholder 
32549  * @cfg {string} monthPlaceholder
32550  * @cfg {string} yearPlaceholder
32551  * @cfg {string} dayFormat default 'd'
32552  * @cfg {string} monthFormat default 'm'
32553  * @cfg {string} yearFormat default 'Y'
32554  * @cfg {Number} labellg set the width of label (1-12)
32555  * @cfg {Number} labelmd set the width of label (1-12)
32556  * @cfg {Number} labelsm set the width of label (1-12)
32557  * @cfg {Number} labelxs set the width of label (1-12)
32558
32559  *     
32560  * @constructor
32561  * Create a new DateSplitField
32562  * @param {Object} config The config object
32563  */
32564
32565 Roo.bootstrap.DateSplitField = function(config){
32566     Roo.bootstrap.DateSplitField.superclass.constructor.call(this, config);
32567     
32568     this.addEvents({
32569         // raw events
32570          /**
32571          * @event years
32572          * getting the data of years
32573          * @param {Roo.bootstrap.DateSplitField} this
32574          * @param {Object} years
32575          */
32576         "years" : true,
32577         /**
32578          * @event days
32579          * getting the data of days
32580          * @param {Roo.bootstrap.DateSplitField} this
32581          * @param {Object} days
32582          */
32583         "days" : true,
32584         /**
32585          * @event invalid
32586          * Fires after the field has been marked as invalid.
32587          * @param {Roo.form.Field} this
32588          * @param {String} msg The validation message
32589          */
32590         invalid : true,
32591        /**
32592          * @event valid
32593          * Fires after the field has been validated with no errors.
32594          * @param {Roo.form.Field} this
32595          */
32596         valid : true
32597     });
32598 };
32599
32600 Roo.extend(Roo.bootstrap.DateSplitField, Roo.bootstrap.Component,  {
32601     
32602     fieldLabel : '',
32603     labelAlign : 'top',
32604     labelWidth : 3,
32605     dayAllowBlank : false,
32606     monthAllowBlank : false,
32607     yearAllowBlank : false,
32608     dayPlaceholder : '',
32609     monthPlaceholder : '',
32610     yearPlaceholder : '',
32611     dayFormat : 'd',
32612     monthFormat : 'm',
32613     yearFormat : 'Y',
32614     isFormField : true,
32615     labellg : 0,
32616     labelmd : 0,
32617     labelsm : 0,
32618     labelxs : 0,
32619     
32620     getAutoCreate : function()
32621     {
32622         var cfg = {
32623             tag : 'div',
32624             cls : 'row roo-date-split-field-group',
32625             cn : [
32626                 {
32627                     tag : 'input',
32628                     type : 'hidden',
32629                     cls : 'form-hidden-field roo-date-split-field-group-value',
32630                     name : this.name
32631                 }
32632             ]
32633         };
32634         
32635         var labelCls = 'col-md-12';
32636         var contentCls = 'col-md-4';
32637         
32638         if(this.fieldLabel){
32639             
32640             var label = {
32641                 tag : 'div',
32642                 cls : 'column roo-date-split-field-label col-md-' + ((this.labelAlign == 'top') ? '12' : this.labelWidth),
32643                 cn : [
32644                     {
32645                         tag : 'label',
32646                         html : this.fieldLabel
32647                     }
32648                 ]
32649             };
32650             
32651             if(this.labelAlign == 'left'){
32652             
32653                 if(this.labelWidth > 12){
32654                     label.style = "width: " + this.labelWidth + 'px';
32655                 }
32656
32657                 if(this.labelWidth < 13 && this.labelmd == 0){
32658                     this.labelmd = this.labelWidth;
32659                 }
32660
32661                 if(this.labellg > 0){
32662                     labelCls = ' col-lg-' + this.labellg;
32663                     contentCls = ' col-lg-' + ((12 - this.labellg) / 3);
32664                 }
32665
32666                 if(this.labelmd > 0){
32667                     labelCls = ' col-md-' + this.labelmd;
32668                     contentCls = ' col-md-' + ((12 - this.labelmd) / 3);
32669                 }
32670
32671                 if(this.labelsm > 0){
32672                     labelCls = ' col-sm-' + this.labelsm;
32673                     contentCls = ' col-sm-' + ((12 - this.labelsm) / 3);
32674                 }
32675
32676                 if(this.labelxs > 0){
32677                     labelCls = ' col-xs-' + this.labelxs;
32678                     contentCls = ' col-xs-' + ((12 - this.labelxs) / 3);
32679                 }
32680             }
32681             
32682             label.cls += ' ' + labelCls;
32683             
32684             cfg.cn.push(label);
32685         }
32686         
32687         Roo.each(['day', 'month', 'year'], function(t){
32688             cfg.cn.push({
32689                 tag : 'div',
32690                 cls : 'column roo-date-split-field-' + t + ' ' + contentCls
32691             });
32692         }, this);
32693         
32694         return cfg;
32695     },
32696     
32697     inputEl: function ()
32698     {
32699         return this.el.select('.roo-date-split-field-group-value', true).first();
32700     },
32701     
32702     onRender : function(ct, position) 
32703     {
32704         var _this = this;
32705         
32706         Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
32707         
32708         this.inputEl = this.el.select('.roo-date-split-field-group-value', true).first();
32709         
32710         this.dayField = new Roo.bootstrap.ComboBox({
32711             allowBlank : this.dayAllowBlank,
32712             alwaysQuery : true,
32713             displayField : 'value',
32714             editable : false,
32715             fieldLabel : '',
32716             forceSelection : true,
32717             mode : 'local',
32718             placeholder : this.dayPlaceholder,
32719             selectOnFocus : true,
32720             tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
32721             triggerAction : 'all',
32722             typeAhead : true,
32723             valueField : 'value',
32724             store : new Roo.data.SimpleStore({
32725                 data : (function() {    
32726                     var days = [];
32727                     _this.fireEvent('days', _this, days);
32728                     return days;
32729                 })(),
32730                 fields : [ 'value' ]
32731             }),
32732             listeners : {
32733                 select : function (_self, record, index)
32734                 {
32735                     _this.setValue(_this.getValue());
32736                 }
32737             }
32738         });
32739
32740         this.dayField.render(this.el.select('.roo-date-split-field-day', true).first(), null);
32741         
32742         this.monthField = new Roo.bootstrap.MonthField({
32743             after : '<i class=\"fa fa-calendar\"></i>',
32744             allowBlank : this.monthAllowBlank,
32745             placeholder : this.monthPlaceholder,
32746             readOnly : true,
32747             listeners : {
32748                 render : function (_self)
32749                 {
32750                     this.el.select('span.input-group-addon', true).first().on('click', function(e){
32751                         e.preventDefault();
32752                         _self.focus();
32753                     });
32754                 },
32755                 select : function (_self, oldvalue, newvalue)
32756                 {
32757                     _this.setValue(_this.getValue());
32758                 }
32759             }
32760         });
32761         
32762         this.monthField.render(this.el.select('.roo-date-split-field-month', true).first(), null);
32763         
32764         this.yearField = new Roo.bootstrap.ComboBox({
32765             allowBlank : this.yearAllowBlank,
32766             alwaysQuery : true,
32767             displayField : 'value',
32768             editable : false,
32769             fieldLabel : '',
32770             forceSelection : true,
32771             mode : 'local',
32772             placeholder : this.yearPlaceholder,
32773             selectOnFocus : true,
32774             tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
32775             triggerAction : 'all',
32776             typeAhead : true,
32777             valueField : 'value',
32778             store : new Roo.data.SimpleStore({
32779                 data : (function() {
32780                     var years = [];
32781                     _this.fireEvent('years', _this, years);
32782                     return years;
32783                 })(),
32784                 fields : [ 'value' ]
32785             }),
32786             listeners : {
32787                 select : function (_self, record, index)
32788                 {
32789                     _this.setValue(_this.getValue());
32790                 }
32791             }
32792         });
32793
32794         this.yearField.render(this.el.select('.roo-date-split-field-year', true).first(), null);
32795     },
32796     
32797     setValue : function(v, format)
32798     {
32799         this.inputEl.dom.value = v;
32800         
32801         var f = format || (this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat);
32802         
32803         var d = Date.parseDate(v, f);
32804         
32805         if(!d){
32806             this.validate();
32807             return;
32808         }
32809         
32810         this.setDay(d.format(this.dayFormat));
32811         this.setMonth(d.format(this.monthFormat));
32812         this.setYear(d.format(this.yearFormat));
32813         
32814         this.validate();
32815         
32816         return;
32817     },
32818     
32819     setDay : function(v)
32820     {
32821         this.dayField.setValue(v);
32822         this.inputEl.dom.value = this.getValue();
32823         this.validate();
32824         return;
32825     },
32826     
32827     setMonth : function(v)
32828     {
32829         this.monthField.setValue(v, true);
32830         this.inputEl.dom.value = this.getValue();
32831         this.validate();
32832         return;
32833     },
32834     
32835     setYear : function(v)
32836     {
32837         this.yearField.setValue(v);
32838         this.inputEl.dom.value = this.getValue();
32839         this.validate();
32840         return;
32841     },
32842     
32843     getDay : function()
32844     {
32845         return this.dayField.getValue();
32846     },
32847     
32848     getMonth : function()
32849     {
32850         return this.monthField.getValue();
32851     },
32852     
32853     getYear : function()
32854     {
32855         return this.yearField.getValue();
32856     },
32857     
32858     getValue : function()
32859     {
32860         var f = this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat;
32861         
32862         var date = this.yearField.getValue() + '-' + this.monthField.getValue() + '-' + this.dayField.getValue();
32863         
32864         return date;
32865     },
32866     
32867     reset : function()
32868     {
32869         this.setDay('');
32870         this.setMonth('');
32871         this.setYear('');
32872         this.inputEl.dom.value = '';
32873         this.validate();
32874         return;
32875     },
32876     
32877     validate : function()
32878     {
32879         var d = this.dayField.validate();
32880         var m = this.monthField.validate();
32881         var y = this.yearField.validate();
32882         
32883         var valid = true;
32884         
32885         if(
32886                 (!this.dayAllowBlank && !d) ||
32887                 (!this.monthAllowBlank && !m) ||
32888                 (!this.yearAllowBlank && !y)
32889         ){
32890             valid = false;
32891         }
32892         
32893         if(this.dayAllowBlank && this.monthAllowBlank && this.yearAllowBlank){
32894             return valid;
32895         }
32896         
32897         if(valid){
32898             this.markValid();
32899             return valid;
32900         }
32901         
32902         this.markInvalid();
32903         
32904         return valid;
32905     },
32906     
32907     markValid : function()
32908     {
32909         
32910         var label = this.el.select('label', true).first();
32911         var icon = this.el.select('i.fa-star', true).first();
32912
32913         if(label && icon){
32914             icon.remove();
32915         }
32916         
32917         this.fireEvent('valid', this);
32918     },
32919     
32920      /**
32921      * Mark this field as invalid
32922      * @param {String} msg The validation message
32923      */
32924     markInvalid : function(msg)
32925     {
32926         
32927         var label = this.el.select('label', true).first();
32928         var icon = this.el.select('i.fa-star', true).first();
32929
32930         if(label && !icon){
32931             this.el.select('.roo-date-split-field-label', true).createChild({
32932                 tag : 'i',
32933                 cls : 'text-danger fa fa-lg fa-star',
32934                 tooltip : 'This field is required',
32935                 style : 'margin-right:5px;'
32936             }, label, true);
32937         }
32938         
32939         this.fireEvent('invalid', this, msg);
32940     },
32941     
32942     clearInvalid : function()
32943     {
32944         var label = this.el.select('label', true).first();
32945         var icon = this.el.select('i.fa-star', true).first();
32946
32947         if(label && icon){
32948             icon.remove();
32949         }
32950         
32951         this.fireEvent('valid', this);
32952     },
32953     
32954     getName: function()
32955     {
32956         return this.name;
32957     }
32958     
32959 });
32960
32961  /**
32962  *
32963  * This is based on 
32964  * http://masonry.desandro.com
32965  *
32966  * The idea is to render all the bricks based on vertical width...
32967  *
32968  * The original code extends 'outlayer' - we might need to use that....
32969  * 
32970  */
32971
32972
32973 /**
32974  * @class Roo.bootstrap.LayoutMasonry
32975  * @extends Roo.bootstrap.Component
32976  * Bootstrap Layout Masonry class
32977  * 
32978  * @constructor
32979  * Create a new Element
32980  * @param {Object} config The config object
32981  */
32982
32983 Roo.bootstrap.LayoutMasonry = function(config){
32984     
32985     Roo.bootstrap.LayoutMasonry.superclass.constructor.call(this, config);
32986     
32987     this.bricks = [];
32988     
32989     Roo.bootstrap.LayoutMasonry.register(this);
32990     
32991     this.addEvents({
32992         // raw events
32993         /**
32994          * @event layout
32995          * Fire after layout the items
32996          * @param {Roo.bootstrap.LayoutMasonry} this
32997          * @param {Roo.EventObject} e
32998          */
32999         "layout" : true
33000     });
33001     
33002 };
33003
33004 Roo.extend(Roo.bootstrap.LayoutMasonry, Roo.bootstrap.Component,  {
33005     
33006     /**
33007      * @cfg {Boolean} isLayoutInstant = no animation?
33008      */   
33009     isLayoutInstant : false, // needed?
33010    
33011     /**
33012      * @cfg {Number} boxWidth  width of the columns
33013      */   
33014     boxWidth : 450,
33015     
33016       /**
33017      * @cfg {Number} boxHeight  - 0 for square, or fix it at a certian height
33018      */   
33019     boxHeight : 0,
33020     
33021     /**
33022      * @cfg {Number} padWidth padding below box..
33023      */   
33024     padWidth : 10, 
33025     
33026     /**
33027      * @cfg {Number} gutter gutter width..
33028      */   
33029     gutter : 10,
33030     
33031      /**
33032      * @cfg {Number} maxCols maximum number of columns
33033      */   
33034     
33035     maxCols: 0,
33036     
33037     /**
33038      * @cfg {Boolean} isAutoInitial defalut true
33039      */   
33040     isAutoInitial : true, 
33041     
33042     containerWidth: 0,
33043     
33044     /**
33045      * @cfg {Boolean} isHorizontal defalut false
33046      */   
33047     isHorizontal : false, 
33048
33049     currentSize : null,
33050     
33051     tag: 'div',
33052     
33053     cls: '',
33054     
33055     bricks: null, //CompositeElement
33056     
33057     cols : 1,
33058     
33059     _isLayoutInited : false,
33060     
33061 //    isAlternative : false, // only use for vertical layout...
33062     
33063     /**
33064      * @cfg {Number} alternativePadWidth padding below box..
33065      */   
33066     alternativePadWidth : 50,
33067     
33068     selectedBrick : [],
33069     
33070     getAutoCreate : function(){
33071         
33072         var cfg = Roo.apply({}, Roo.bootstrap.LayoutMasonry.superclass.getAutoCreate.call(this));
33073         
33074         var cfg = {
33075             tag: this.tag,
33076             cls: 'blog-masonary-wrapper ' + this.cls,
33077             cn : {
33078                 cls : 'mas-boxes masonary'
33079             }
33080         };
33081         
33082         return cfg;
33083     },
33084     
33085     getChildContainer: function( )
33086     {
33087         if (this.boxesEl) {
33088             return this.boxesEl;
33089         }
33090         
33091         this.boxesEl = this.el.select('.mas-boxes').first();
33092         
33093         return this.boxesEl;
33094     },
33095     
33096     
33097     initEvents : function()
33098     {
33099         var _this = this;
33100         
33101         if(this.isAutoInitial){
33102             Roo.log('hook children rendered');
33103             this.on('childrenrendered', function() {
33104                 Roo.log('children rendered');
33105                 _this.initial();
33106             } ,this);
33107         }
33108     },
33109     
33110     initial : function()
33111     {
33112         this.selectedBrick = [];
33113         
33114         this.currentSize = this.el.getBox(true);
33115         
33116         Roo.EventManager.onWindowResize(this.resize, this); 
33117
33118         if(!this.isAutoInitial){
33119             this.layout();
33120             return;
33121         }
33122         
33123         this.layout();
33124         
33125         return;
33126         //this.layout.defer(500,this);
33127         
33128     },
33129     
33130     resize : function()
33131     {
33132         var cs = this.el.getBox(true);
33133         
33134         if (
33135                 this.currentSize.width == cs.width && 
33136                 this.currentSize.x == cs.x && 
33137                 this.currentSize.height == cs.height && 
33138                 this.currentSize.y == cs.y 
33139         ) {
33140             Roo.log("no change in with or X or Y");
33141             return;
33142         }
33143         
33144         this.currentSize = cs;
33145         
33146         this.layout();
33147         
33148     },
33149     
33150     layout : function()
33151     {   
33152         this._resetLayout();
33153         
33154         var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
33155         
33156         this.layoutItems( isInstant );
33157       
33158         this._isLayoutInited = true;
33159         
33160         this.fireEvent('layout', this);
33161         
33162     },
33163     
33164     _resetLayout : function()
33165     {
33166         if(this.isHorizontal){
33167             this.horizontalMeasureColumns();
33168             return;
33169         }
33170         
33171         this.verticalMeasureColumns();
33172         
33173     },
33174     
33175     verticalMeasureColumns : function()
33176     {
33177         this.getContainerWidth();
33178         
33179 //        if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
33180 //            this.colWidth = Math.floor(this.containerWidth * 0.8);
33181 //            return;
33182 //        }
33183         
33184         var boxWidth = this.boxWidth + this.padWidth;
33185         
33186         if(this.containerWidth < this.boxWidth){
33187             boxWidth = this.containerWidth
33188         }
33189         
33190         var containerWidth = this.containerWidth;
33191         
33192         var cols = Math.floor(containerWidth / boxWidth);
33193         
33194         this.cols = Math.max( cols, 1 );
33195         
33196         this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
33197         
33198         var totalBoxWidth = this.cols * boxWidth - this.padWidth;
33199         
33200         var avail = Math.floor((containerWidth - totalBoxWidth) / this.cols);
33201         
33202         this.colWidth = boxWidth + avail - this.padWidth;
33203         
33204         this.unitWidth = Math.round((this.colWidth - (this.gutter * 2)) / 3);
33205         this.unitHeight = this.boxHeight > 0 ? this.boxHeight  : this.unitWidth;
33206     },
33207     
33208     horizontalMeasureColumns : function()
33209     {
33210         this.getContainerWidth();
33211         
33212         var boxWidth = this.boxWidth;
33213         
33214         if(this.containerWidth < boxWidth){
33215             boxWidth = this.containerWidth;
33216         }
33217         
33218         this.unitWidth = Math.floor((boxWidth - (this.gutter * 2)) / 3);
33219         
33220         this.el.setHeight(boxWidth);
33221         
33222     },
33223     
33224     getContainerWidth : function()
33225     {
33226         this.containerWidth = this.el.getBox(true).width;  //maybe use getComputedWidth
33227     },
33228     
33229     layoutItems : function( isInstant )
33230     {
33231         Roo.log(this.bricks);
33232         
33233         var items = Roo.apply([], this.bricks);
33234         
33235         if(this.isHorizontal){
33236             this._horizontalLayoutItems( items , isInstant );
33237             return;
33238         }
33239         
33240 //        if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
33241 //            this._verticalAlternativeLayoutItems( items , isInstant );
33242 //            return;
33243 //        }
33244         
33245         this._verticalLayoutItems( items , isInstant );
33246         
33247     },
33248     
33249     _verticalLayoutItems : function ( items , isInstant)
33250     {
33251         if ( !items || !items.length ) {
33252             return;
33253         }
33254         
33255         var standard = [
33256             ['xs', 'xs', 'xs', 'tall'],
33257             ['xs', 'xs', 'tall'],
33258             ['xs', 'xs', 'sm'],
33259             ['xs', 'xs', 'xs'],
33260             ['xs', 'tall'],
33261             ['xs', 'sm'],
33262             ['xs', 'xs'],
33263             ['xs'],
33264             
33265             ['sm', 'xs', 'xs'],
33266             ['sm', 'xs'],
33267             ['sm'],
33268             
33269             ['tall', 'xs', 'xs', 'xs'],
33270             ['tall', 'xs', 'xs'],
33271             ['tall', 'xs'],
33272             ['tall']
33273             
33274         ];
33275         
33276         var queue = [];
33277         
33278         var boxes = [];
33279         
33280         var box = [];
33281         
33282         Roo.each(items, function(item, k){
33283             
33284             switch (item.size) {
33285                 // these layouts take up a full box,
33286                 case 'md' :
33287                 case 'md-left' :
33288                 case 'md-right' :
33289                 case 'wide' :
33290                     
33291                     if(box.length){
33292                         boxes.push(box);
33293                         box = [];
33294                     }
33295                     
33296                     boxes.push([item]);
33297                     
33298                     break;
33299                     
33300                 case 'xs' :
33301                 case 'sm' :
33302                 case 'tall' :
33303                     
33304                     box.push(item);
33305                     
33306                     break;
33307                 default :
33308                     break;
33309                     
33310             }
33311             
33312         }, this);
33313         
33314         if(box.length){
33315             boxes.push(box);
33316             box = [];
33317         }
33318         
33319         var filterPattern = function(box, length)
33320         {
33321             if(!box.length){
33322                 return;
33323             }
33324             
33325             var match = false;
33326             
33327             var pattern = box.slice(0, length);
33328             
33329             var format = [];
33330             
33331             Roo.each(pattern, function(i){
33332                 format.push(i.size);
33333             }, this);
33334             
33335             Roo.each(standard, function(s){
33336                 
33337                 if(String(s) != String(format)){
33338                     return;
33339                 }
33340                 
33341                 match = true;
33342                 return false;
33343                 
33344             }, this);
33345             
33346             if(!match && length == 1){
33347                 return;
33348             }
33349             
33350             if(!match){
33351                 filterPattern(box, length - 1);
33352                 return;
33353             }
33354                 
33355             queue.push(pattern);
33356
33357             box = box.slice(length, box.length);
33358
33359             filterPattern(box, 4);
33360
33361             return;
33362             
33363         }
33364         
33365         Roo.each(boxes, function(box, k){
33366             
33367             if(!box.length){
33368                 return;
33369             }
33370             
33371             if(box.length == 1){
33372                 queue.push(box);
33373                 return;
33374             }
33375             
33376             filterPattern(box, 4);
33377             
33378         }, this);
33379         
33380         this._processVerticalLayoutQueue( queue, isInstant );
33381         
33382     },
33383     
33384 //    _verticalAlternativeLayoutItems : function( items , isInstant )
33385 //    {
33386 //        if ( !items || !items.length ) {
33387 //            return;
33388 //        }
33389 //
33390 //        this._processVerticalAlternativeLayoutQueue( items, isInstant );
33391 //        
33392 //    },
33393     
33394     _horizontalLayoutItems : function ( items , isInstant)
33395     {
33396         if ( !items || !items.length || items.length < 3) {
33397             return;
33398         }
33399         
33400         items.reverse();
33401         
33402         var eItems = items.slice(0, 3);
33403         
33404         items = items.slice(3, items.length);
33405         
33406         var standard = [
33407             ['xs', 'xs', 'xs', 'wide'],
33408             ['xs', 'xs', 'wide'],
33409             ['xs', 'xs', 'sm'],
33410             ['xs', 'xs', 'xs'],
33411             ['xs', 'wide'],
33412             ['xs', 'sm'],
33413             ['xs', 'xs'],
33414             ['xs'],
33415             
33416             ['sm', 'xs', 'xs'],
33417             ['sm', 'xs'],
33418             ['sm'],
33419             
33420             ['wide', 'xs', 'xs', 'xs'],
33421             ['wide', 'xs', 'xs'],
33422             ['wide', 'xs'],
33423             ['wide'],
33424             
33425             ['wide-thin']
33426         ];
33427         
33428         var queue = [];
33429         
33430         var boxes = [];
33431         
33432         var box = [];
33433         
33434         Roo.each(items, function(item, k){
33435             
33436             switch (item.size) {
33437                 case 'md' :
33438                 case 'md-left' :
33439                 case 'md-right' :
33440                 case 'tall' :
33441                     
33442                     if(box.length){
33443                         boxes.push(box);
33444                         box = [];
33445                     }
33446                     
33447                     boxes.push([item]);
33448                     
33449                     break;
33450                     
33451                 case 'xs' :
33452                 case 'sm' :
33453                 case 'wide' :
33454                 case 'wide-thin' :
33455                     
33456                     box.push(item);
33457                     
33458                     break;
33459                 default :
33460                     break;
33461                     
33462             }
33463             
33464         }, this);
33465         
33466         if(box.length){
33467             boxes.push(box);
33468             box = [];
33469         }
33470         
33471         var filterPattern = function(box, length)
33472         {
33473             if(!box.length){
33474                 return;
33475             }
33476             
33477             var match = false;
33478             
33479             var pattern = box.slice(0, length);
33480             
33481             var format = [];
33482             
33483             Roo.each(pattern, function(i){
33484                 format.push(i.size);
33485             }, this);
33486             
33487             Roo.each(standard, function(s){
33488                 
33489                 if(String(s) != String(format)){
33490                     return;
33491                 }
33492                 
33493                 match = true;
33494                 return false;
33495                 
33496             }, this);
33497             
33498             if(!match && length == 1){
33499                 return;
33500             }
33501             
33502             if(!match){
33503                 filterPattern(box, length - 1);
33504                 return;
33505             }
33506                 
33507             queue.push(pattern);
33508
33509             box = box.slice(length, box.length);
33510
33511             filterPattern(box, 4);
33512
33513             return;
33514             
33515         }
33516         
33517         Roo.each(boxes, function(box, k){
33518             
33519             if(!box.length){
33520                 return;
33521             }
33522             
33523             if(box.length == 1){
33524                 queue.push(box);
33525                 return;
33526             }
33527             
33528             filterPattern(box, 4);
33529             
33530         }, this);
33531         
33532         
33533         var prune = [];
33534         
33535         var pos = this.el.getBox(true);
33536         
33537         var minX = pos.x;
33538         
33539         var maxX = pos.right - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
33540         
33541         var hit_end = false;
33542         
33543         Roo.each(queue, function(box){
33544             
33545             if(hit_end){
33546                 
33547                 Roo.each(box, function(b){
33548                 
33549                     b.el.setVisibilityMode(Roo.Element.DISPLAY);
33550                     b.el.hide();
33551
33552                 }, this);
33553
33554                 return;
33555             }
33556             
33557             var mx = 0;
33558             
33559             Roo.each(box, function(b){
33560                 
33561                 b.el.setVisibilityMode(Roo.Element.DISPLAY);
33562                 b.el.show();
33563
33564                 mx = Math.max(mx, b.x);
33565                 
33566             }, this);
33567             
33568             maxX = maxX - this.unitWidth * mx - this.gutter * (mx - 1) - this.padWidth;
33569             
33570             if(maxX < minX){
33571                 
33572                 Roo.each(box, function(b){
33573                 
33574                     b.el.setVisibilityMode(Roo.Element.DISPLAY);
33575                     b.el.hide();
33576                     
33577                 }, this);
33578                 
33579                 hit_end = true;
33580                 
33581                 return;
33582             }
33583             
33584             prune.push(box);
33585             
33586         }, this);
33587         
33588         this._processHorizontalLayoutQueue( prune, eItems, isInstant );
33589     },
33590     
33591     /** Sets position of item in DOM
33592     * @param {Element} item
33593     * @param {Number} x - horizontal position
33594     * @param {Number} y - vertical position
33595     * @param {Boolean} isInstant - disables transitions
33596     */
33597     _processVerticalLayoutQueue : function( queue, isInstant )
33598     {
33599         var pos = this.el.getBox(true);
33600         var x = pos.x;
33601         var y = pos.y;
33602         var maxY = [];
33603         
33604         for (var i = 0; i < this.cols; i++){
33605             maxY[i] = pos.y;
33606         }
33607         
33608         Roo.each(queue, function(box, k){
33609             
33610             var col = k % this.cols;
33611             
33612             Roo.each(box, function(b,kk){
33613                 
33614                 b.el.position('absolute');
33615                 
33616                 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
33617                 var height = Math.floor(this.unitHeight * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
33618                 
33619                 if(b.size == 'md-left' || b.size == 'md-right'){
33620                     width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
33621                     height = Math.floor(this.unitHeight * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
33622                 }
33623                 
33624                 b.el.setWidth(width);
33625                 b.el.setHeight(height);
33626                 // iframe?
33627                 b.el.select('iframe',true).setSize(width,height);
33628                 
33629             }, this);
33630             
33631             for (var i = 0; i < this.cols; i++){
33632                 
33633                 if(maxY[i] < maxY[col]){
33634                     col = i;
33635                     continue;
33636                 }
33637                 
33638                 col = Math.min(col, i);
33639                 
33640             }
33641             
33642             x = pos.x + col * (this.colWidth + this.padWidth);
33643             
33644             y = maxY[col];
33645             
33646             var positions = [];
33647             
33648             switch (box.length){
33649                 case 1 :
33650                     positions = this.getVerticalOneBoxColPositions(x, y, box);
33651                     break;
33652                 case 2 :
33653                     positions = this.getVerticalTwoBoxColPositions(x, y, box);
33654                     break;
33655                 case 3 :
33656                     positions = this.getVerticalThreeBoxColPositions(x, y, box);
33657                     break;
33658                 case 4 :
33659                     positions = this.getVerticalFourBoxColPositions(x, y, box);
33660                     break;
33661                 default :
33662                     break;
33663             }
33664             
33665             Roo.each(box, function(b,kk){
33666                 
33667                 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
33668                 
33669                 var sz = b.el.getSize();
33670                 
33671                 maxY[col] = Math.max(maxY[col], positions[kk].y + sz.height + this.padWidth);
33672                 
33673             }, this);
33674             
33675         }, this);
33676         
33677         var mY = 0;
33678         
33679         for (var i = 0; i < this.cols; i++){
33680             mY = Math.max(mY, maxY[i]);
33681         }
33682         
33683         this.el.setHeight(mY - pos.y);
33684         
33685     },
33686     
33687 //    _processVerticalAlternativeLayoutQueue : function( items, isInstant )
33688 //    {
33689 //        var pos = this.el.getBox(true);
33690 //        var x = pos.x;
33691 //        var y = pos.y;
33692 //        var maxX = pos.right;
33693 //        
33694 //        var maxHeight = 0;
33695 //        
33696 //        Roo.each(items, function(item, k){
33697 //            
33698 //            var c = k % 2;
33699 //            
33700 //            item.el.position('absolute');
33701 //                
33702 //            var width = Math.floor(this.colWidth + item.el.getPadding('lr'));
33703 //
33704 //            item.el.setWidth(width);
33705 //
33706 //            var height = Math.floor(this.colWidth * item.y / item.x + item.el.getPadding('tb'));
33707 //
33708 //            item.el.setHeight(height);
33709 //            
33710 //            if(c == 0){
33711 //                item.el.setXY([x, y], isInstant ? false : true);
33712 //            } else {
33713 //                item.el.setXY([maxX - width, y], isInstant ? false : true);
33714 //            }
33715 //            
33716 //            y = y + height + this.alternativePadWidth;
33717 //            
33718 //            maxHeight = maxHeight + height + this.alternativePadWidth;
33719 //            
33720 //        }, this);
33721 //        
33722 //        this.el.setHeight(maxHeight);
33723 //        
33724 //    },
33725     
33726     _processHorizontalLayoutQueue : function( queue, eItems, isInstant )
33727     {
33728         var pos = this.el.getBox(true);
33729         
33730         var minX = pos.x;
33731         var minY = pos.y;
33732         
33733         var maxX = pos.right;
33734         
33735         this._processHorizontalEndItem(eItems, maxX, minX, minY, isInstant);
33736         
33737         var maxX = maxX - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
33738         
33739         Roo.each(queue, function(box, k){
33740             
33741             Roo.each(box, function(b, kk){
33742                 
33743                 b.el.position('absolute');
33744                 
33745                 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
33746                 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
33747                 
33748                 if(b.size == 'md-left' || b.size == 'md-right'){
33749                     width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
33750                     height = Math.floor(this.unitWidth * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
33751                 }
33752                 
33753                 b.el.setWidth(width);
33754                 b.el.setHeight(height);
33755                 
33756             }, this);
33757             
33758             if(!box.length){
33759                 return;
33760             }
33761             
33762             var positions = [];
33763             
33764             switch (box.length){
33765                 case 1 :
33766                     positions = this.getHorizontalOneBoxColPositions(maxX, minY, box);
33767                     break;
33768                 case 2 :
33769                     positions = this.getHorizontalTwoBoxColPositions(maxX, minY, box);
33770                     break;
33771                 case 3 :
33772                     positions = this.getHorizontalThreeBoxColPositions(maxX, minY, box);
33773                     break;
33774                 case 4 :
33775                     positions = this.getHorizontalFourBoxColPositions(maxX, minY, box);
33776                     break;
33777                 default :
33778                     break;
33779             }
33780             
33781             Roo.each(box, function(b,kk){
33782                 
33783                 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
33784                 
33785                 maxX = Math.min(maxX, positions[kk].x - this.padWidth);
33786                 
33787             }, this);
33788             
33789         }, this);
33790         
33791     },
33792     
33793     _processHorizontalEndItem : function(eItems, maxX, minX, minY, isInstant)
33794     {
33795         Roo.each(eItems, function(b,k){
33796             
33797             b.size = (k == 0) ? 'sm' : 'xs';
33798             b.x = (k == 0) ? 2 : 1;
33799             b.y = (k == 0) ? 2 : 1;
33800             
33801             b.el.position('absolute');
33802             
33803             var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
33804                 
33805             b.el.setWidth(width);
33806             
33807             var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
33808             
33809             b.el.setHeight(height);
33810             
33811         }, this);
33812
33813         var positions = [];
33814         
33815         positions.push({
33816             x : maxX - this.unitWidth * 2 - this.gutter,
33817             y : minY
33818         });
33819         
33820         positions.push({
33821             x : maxX - this.unitWidth,
33822             y : minY + (this.unitWidth + this.gutter) * 2
33823         });
33824         
33825         positions.push({
33826             x : maxX - this.unitWidth * 3 - this.gutter * 2,
33827             y : minY
33828         });
33829         
33830         Roo.each(eItems, function(b,k){
33831             
33832             b.el.setXY([positions[k].x, positions[k].y], isInstant ? false : true);
33833
33834         }, this);
33835         
33836     },
33837     
33838     getVerticalOneBoxColPositions : function(x, y, box)
33839     {
33840         var pos = [];
33841         
33842         var rand = Math.floor(Math.random() * ((4 - box[0].x)));
33843         
33844         if(box[0].size == 'md-left'){
33845             rand = 0;
33846         }
33847         
33848         if(box[0].size == 'md-right'){
33849             rand = 1;
33850         }
33851         
33852         pos.push({
33853             x : x + (this.unitWidth + this.gutter) * rand,
33854             y : y
33855         });
33856         
33857         return pos;
33858     },
33859     
33860     getVerticalTwoBoxColPositions : function(x, y, box)
33861     {
33862         var pos = [];
33863         
33864         if(box[0].size == 'xs'){
33865             
33866             pos.push({
33867                 x : x,
33868                 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[1].y))
33869             });
33870
33871             pos.push({
33872                 x : x + (this.unitWidth + this.gutter) * (3 - box[1].x),
33873                 y : y
33874             });
33875             
33876             return pos;
33877             
33878         }
33879         
33880         pos.push({
33881             x : x,
33882             y : y
33883         });
33884
33885         pos.push({
33886             x : x + (this.unitWidth + this.gutter) * 2,
33887             y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[0].y))
33888         });
33889         
33890         return pos;
33891         
33892     },
33893     
33894     getVerticalThreeBoxColPositions : function(x, y, box)
33895     {
33896         var pos = [];
33897         
33898         if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
33899             
33900             pos.push({
33901                 x : x,
33902                 y : y
33903             });
33904
33905             pos.push({
33906                 x : x + (this.unitWidth + this.gutter) * 1,
33907                 y : y
33908             });
33909             
33910             pos.push({
33911                 x : x + (this.unitWidth + this.gutter) * 2,
33912                 y : y
33913             });
33914             
33915             return pos;
33916             
33917         }
33918         
33919         if(box[0].size == 'xs' && box[1].size == 'xs'){
33920             
33921             pos.push({
33922                 x : x,
33923                 y : y
33924             });
33925
33926             pos.push({
33927                 x : x,
33928                 y : y + ((this.unitHeight + this.gutter) * (box[2].y - 1))
33929             });
33930             
33931             pos.push({
33932                 x : x + (this.unitWidth + this.gutter) * 1,
33933                 y : y
33934             });
33935             
33936             return pos;
33937             
33938         }
33939         
33940         pos.push({
33941             x : x,
33942             y : y
33943         });
33944
33945         pos.push({
33946             x : x + (this.unitWidth + this.gutter) * 2,
33947             y : y
33948         });
33949
33950         pos.push({
33951             x : x + (this.unitWidth + this.gutter) * 2,
33952             y : y + (this.unitHeight + this.gutter) * (box[0].y - 1)
33953         });
33954             
33955         return pos;
33956         
33957     },
33958     
33959     getVerticalFourBoxColPositions : function(x, y, box)
33960     {
33961         var pos = [];
33962         
33963         if(box[0].size == 'xs'){
33964             
33965             pos.push({
33966                 x : x,
33967                 y : y
33968             });
33969
33970             pos.push({
33971                 x : x,
33972                 y : y + (this.unitHeight + this.gutter) * 1
33973             });
33974             
33975             pos.push({
33976                 x : x,
33977                 y : y + (this.unitHeight + this.gutter) * 2
33978             });
33979             
33980             pos.push({
33981                 x : x + (this.unitWidth + this.gutter) * 1,
33982                 y : y
33983             });
33984             
33985             return pos;
33986             
33987         }
33988         
33989         pos.push({
33990             x : x,
33991             y : y
33992         });
33993
33994         pos.push({
33995             x : x + (this.unitWidth + this.gutter) * 2,
33996             y : y
33997         });
33998
33999         pos.push({
34000             x : x + (this.unitHeightunitWidth + this.gutter) * 2,
34001             y : y + (this.unitHeight + this.gutter) * 1
34002         });
34003
34004         pos.push({
34005             x : x + (this.unitWidth + this.gutter) * 2,
34006             y : y + (this.unitWidth + this.gutter) * 2
34007         });
34008
34009         return pos;
34010         
34011     },
34012     
34013     getHorizontalOneBoxColPositions : function(maxX, minY, box)
34014     {
34015         var pos = [];
34016         
34017         if(box[0].size == 'md-left'){
34018             pos.push({
34019                 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
34020                 y : minY
34021             });
34022             
34023             return pos;
34024         }
34025         
34026         if(box[0].size == 'md-right'){
34027             pos.push({
34028                 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
34029                 y : minY + (this.unitWidth + this.gutter) * 1
34030             });
34031             
34032             return pos;
34033         }
34034         
34035         var rand = Math.floor(Math.random() * (4 - box[0].y));
34036         
34037         pos.push({
34038             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34039             y : minY + (this.unitWidth + this.gutter) * rand
34040         });
34041         
34042         return pos;
34043         
34044     },
34045     
34046     getHorizontalTwoBoxColPositions : function(maxX, minY, box)
34047     {
34048         var pos = [];
34049         
34050         if(box[0].size == 'xs'){
34051             
34052             pos.push({
34053                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34054                 y : minY
34055             });
34056
34057             pos.push({
34058                 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34059                 y : minY + (this.unitWidth + this.gutter) * (3 - box[1].y)
34060             });
34061             
34062             return pos;
34063             
34064         }
34065         
34066         pos.push({
34067             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34068             y : minY
34069         });
34070
34071         pos.push({
34072             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34073             y : minY + (this.unitWidth + this.gutter) * 2
34074         });
34075         
34076         return pos;
34077         
34078     },
34079     
34080     getHorizontalThreeBoxColPositions : function(maxX, minY, box)
34081     {
34082         var pos = [];
34083         
34084         if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
34085             
34086             pos.push({
34087                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34088                 y : minY
34089             });
34090
34091             pos.push({
34092                 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34093                 y : minY + (this.unitWidth + this.gutter) * 1
34094             });
34095             
34096             pos.push({
34097                 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
34098                 y : minY + (this.unitWidth + this.gutter) * 2
34099             });
34100             
34101             return pos;
34102             
34103         }
34104         
34105         if(box[0].size == 'xs' && box[1].size == 'xs'){
34106             
34107             pos.push({
34108                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34109                 y : minY
34110             });
34111
34112             pos.push({
34113                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34114                 y : minY
34115             });
34116             
34117             pos.push({
34118                 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
34119                 y : minY + (this.unitWidth + this.gutter) * 1
34120             });
34121             
34122             return pos;
34123             
34124         }
34125         
34126         pos.push({
34127             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34128             y : minY
34129         });
34130
34131         pos.push({
34132             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34133             y : minY + (this.unitWidth + this.gutter) * 2
34134         });
34135
34136         pos.push({
34137             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
34138             y : minY + (this.unitWidth + this.gutter) * 2
34139         });
34140             
34141         return pos;
34142         
34143     },
34144     
34145     getHorizontalFourBoxColPositions : function(maxX, minY, box)
34146     {
34147         var pos = [];
34148         
34149         if(box[0].size == 'xs'){
34150             
34151             pos.push({
34152                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34153                 y : minY
34154             });
34155
34156             pos.push({
34157                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34158                 y : minY
34159             });
34160             
34161             pos.push({
34162                 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),
34163                 y : minY
34164             });
34165             
34166             pos.push({
34167                 x : maxX - this.unitWidth * box[3].x - this.gutter * (box[3].x - 1),
34168                 y : minY + (this.unitWidth + this.gutter) * 1
34169             });
34170             
34171             return pos;
34172             
34173         }
34174         
34175         pos.push({
34176             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34177             y : minY
34178         });
34179         
34180         pos.push({
34181             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34182             y : minY + (this.unitWidth + this.gutter) * 2
34183         });
34184         
34185         pos.push({
34186             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
34187             y : minY + (this.unitWidth + this.gutter) * 2
34188         });
34189         
34190         pos.push({
34191             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),
34192             y : minY + (this.unitWidth + this.gutter) * 2
34193         });
34194
34195         return pos;
34196         
34197     },
34198     
34199     /**
34200     * remove a Masonry Brick
34201     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to remove
34202     */
34203     removeBrick : function(brick_id)
34204     {
34205         if (!brick_id) {
34206             return;
34207         }
34208         
34209         for (var i = 0; i<this.bricks.length; i++) {
34210             if (this.bricks[i].id == brick_id) {
34211                 this.bricks.splice(i,1);
34212                 this.el.dom.removeChild(Roo.get(brick_id).dom);
34213                 this.initial();
34214             }
34215         }
34216     },
34217     
34218     /**
34219     * adds a Masonry Brick
34220     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
34221     */
34222     addBrick : function(cfg)
34223     {
34224         var cn = new Roo.bootstrap.MasonryBrick(cfg);
34225         //this.register(cn);
34226         cn.parentId = this.id;
34227         cn.render(this.el);
34228         return cn;
34229     },
34230     
34231     /**
34232     * register a Masonry Brick
34233     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
34234     */
34235     
34236     register : function(brick)
34237     {
34238         this.bricks.push(brick);
34239         brick.masonryId = this.id;
34240     },
34241     
34242     /**
34243     * clear all the Masonry Brick
34244     */
34245     clearAll : function()
34246     {
34247         this.bricks = [];
34248         //this.getChildContainer().dom.innerHTML = "";
34249         this.el.dom.innerHTML = '';
34250     },
34251     
34252     getSelected : function()
34253     {
34254         if (!this.selectedBrick) {
34255             return false;
34256         }
34257         
34258         return this.selectedBrick;
34259     }
34260 });
34261
34262 Roo.apply(Roo.bootstrap.LayoutMasonry, {
34263     
34264     groups: {},
34265      /**
34266     * register a Masonry Layout
34267     * @param {Roo.bootstrap.LayoutMasonry} the masonry layout to add
34268     */
34269     
34270     register : function(layout)
34271     {
34272         this.groups[layout.id] = layout;
34273     },
34274     /**
34275     * fetch a  Masonry Layout based on the masonry layout ID
34276     * @param {string} the masonry layout to add
34277     * @returns {Roo.bootstrap.LayoutMasonry} the masonry layout
34278     */
34279     
34280     get: function(layout_id) {
34281         if (typeof(this.groups[layout_id]) == 'undefined') {
34282             return false;
34283         }
34284         return this.groups[layout_id] ;
34285     }
34286     
34287     
34288     
34289 });
34290
34291  
34292
34293  /**
34294  *
34295  * This is based on 
34296  * http://masonry.desandro.com
34297  *
34298  * The idea is to render all the bricks based on vertical width...
34299  *
34300  * The original code extends 'outlayer' - we might need to use that....
34301  * 
34302  */
34303
34304
34305 /**
34306  * @class Roo.bootstrap.LayoutMasonryAuto
34307  * @extends Roo.bootstrap.Component
34308  * Bootstrap Layout Masonry class
34309  * 
34310  * @constructor
34311  * Create a new Element
34312  * @param {Object} config The config object
34313  */
34314
34315 Roo.bootstrap.LayoutMasonryAuto = function(config){
34316     Roo.bootstrap.LayoutMasonryAuto.superclass.constructor.call(this, config);
34317 };
34318
34319 Roo.extend(Roo.bootstrap.LayoutMasonryAuto, Roo.bootstrap.Component,  {
34320     
34321       /**
34322      * @cfg {Boolean} isFitWidth  - resize the width..
34323      */   
34324     isFitWidth : false,  // options..
34325     /**
34326      * @cfg {Boolean} isOriginLeft = left align?
34327      */   
34328     isOriginLeft : true,
34329     /**
34330      * @cfg {Boolean} isOriginTop = top align?
34331      */   
34332     isOriginTop : false,
34333     /**
34334      * @cfg {Boolean} isLayoutInstant = no animation?
34335      */   
34336     isLayoutInstant : false, // needed?
34337     /**
34338      * @cfg {Boolean} isResizingContainer = not sure if this is used..
34339      */   
34340     isResizingContainer : true,
34341     /**
34342      * @cfg {Number} columnWidth  width of the columns 
34343      */   
34344     
34345     columnWidth : 0,
34346     
34347     /**
34348      * @cfg {Number} maxCols maximum number of columns
34349      */   
34350     
34351     maxCols: 0,
34352     /**
34353      * @cfg {Number} padHeight padding below box..
34354      */   
34355     
34356     padHeight : 10, 
34357     
34358     /**
34359      * @cfg {Boolean} isAutoInitial defalut true
34360      */   
34361     
34362     isAutoInitial : true, 
34363     
34364     // private?
34365     gutter : 0,
34366     
34367     containerWidth: 0,
34368     initialColumnWidth : 0,
34369     currentSize : null,
34370     
34371     colYs : null, // array.
34372     maxY : 0,
34373     padWidth: 10,
34374     
34375     
34376     tag: 'div',
34377     cls: '',
34378     bricks: null, //CompositeElement
34379     cols : 0, // array?
34380     // element : null, // wrapped now this.el
34381     _isLayoutInited : null, 
34382     
34383     
34384     getAutoCreate : function(){
34385         
34386         var cfg = {
34387             tag: this.tag,
34388             cls: 'blog-masonary-wrapper ' + this.cls,
34389             cn : {
34390                 cls : 'mas-boxes masonary'
34391             }
34392         };
34393         
34394         return cfg;
34395     },
34396     
34397     getChildContainer: function( )
34398     {
34399         if (this.boxesEl) {
34400             return this.boxesEl;
34401         }
34402         
34403         this.boxesEl = this.el.select('.mas-boxes').first();
34404         
34405         return this.boxesEl;
34406     },
34407     
34408     
34409     initEvents : function()
34410     {
34411         var _this = this;
34412         
34413         if(this.isAutoInitial){
34414             Roo.log('hook children rendered');
34415             this.on('childrenrendered', function() {
34416                 Roo.log('children rendered');
34417                 _this.initial();
34418             } ,this);
34419         }
34420         
34421     },
34422     
34423     initial : function()
34424     {
34425         this.reloadItems();
34426
34427         this.currentSize = this.el.getBox(true);
34428
34429         /// was window resize... - let's see if this works..
34430         Roo.EventManager.onWindowResize(this.resize, this); 
34431
34432         if(!this.isAutoInitial){
34433             this.layout();
34434             return;
34435         }
34436         
34437         this.layout.defer(500,this);
34438     },
34439     
34440     reloadItems: function()
34441     {
34442         this.bricks = this.el.select('.masonry-brick', true);
34443         
34444         this.bricks.each(function(b) {
34445             //Roo.log(b.getSize());
34446             if (!b.attr('originalwidth')) {
34447                 b.attr('originalwidth',  b.getSize().width);
34448             }
34449             
34450         });
34451         
34452         Roo.log(this.bricks.elements.length);
34453     },
34454     
34455     resize : function()
34456     {
34457         Roo.log('resize');
34458         var cs = this.el.getBox(true);
34459         
34460         if (this.currentSize.width == cs.width && this.currentSize.x == cs.x ) {
34461             Roo.log("no change in with or X");
34462             return;
34463         }
34464         this.currentSize = cs;
34465         this.layout();
34466     },
34467     
34468     layout : function()
34469     {
34470          Roo.log('layout');
34471         this._resetLayout();
34472         //this._manageStamps();
34473       
34474         // don't animate first layout
34475         var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
34476         this.layoutItems( isInstant );
34477       
34478         // flag for initalized
34479         this._isLayoutInited = true;
34480     },
34481     
34482     layoutItems : function( isInstant )
34483     {
34484         //var items = this._getItemsForLayout( this.items );
34485         // original code supports filtering layout items.. we just ignore it..
34486         
34487         this._layoutItems( this.bricks , isInstant );
34488       
34489         this._postLayout();
34490     },
34491     _layoutItems : function ( items , isInstant)
34492     {
34493        //this.fireEvent( 'layout', this, items );
34494     
34495
34496         if ( !items || !items.elements.length ) {
34497           // no items, emit event with empty array
34498             return;
34499         }
34500
34501         var queue = [];
34502         items.each(function(item) {
34503             Roo.log("layout item");
34504             Roo.log(item);
34505             // get x/y object from method
34506             var position = this._getItemLayoutPosition( item );
34507             // enqueue
34508             position.item = item;
34509             position.isInstant = isInstant; // || item.isLayoutInstant; << not set yet...
34510             queue.push( position );
34511         }, this);
34512       
34513         this._processLayoutQueue( queue );
34514     },
34515     /** Sets position of item in DOM
34516     * @param {Element} item
34517     * @param {Number} x - horizontal position
34518     * @param {Number} y - vertical position
34519     * @param {Boolean} isInstant - disables transitions
34520     */
34521     _processLayoutQueue : function( queue )
34522     {
34523         for ( var i=0, len = queue.length; i < len; i++ ) {
34524             var obj = queue[i];
34525             obj.item.position('absolute');
34526             obj.item.setXY([obj.x,obj.y], obj.isInstant ? false : true);
34527         }
34528     },
34529       
34530     
34531     /**
34532     * Any logic you want to do after each layout,
34533     * i.e. size the container
34534     */
34535     _postLayout : function()
34536     {
34537         this.resizeContainer();
34538     },
34539     
34540     resizeContainer : function()
34541     {
34542         if ( !this.isResizingContainer ) {
34543             return;
34544         }
34545         var size = this._getContainerSize();
34546         if ( size ) {
34547             this.el.setSize(size.width,size.height);
34548             this.boxesEl.setSize(size.width,size.height);
34549         }
34550     },
34551     
34552     
34553     
34554     _resetLayout : function()
34555     {
34556         //this.getSize();  // -- does not really do anything.. it probably applies left/right etc. to obuject but not used
34557         this.colWidth = this.el.getWidth();
34558         //this.gutter = this.el.getWidth(); 
34559         
34560         this.measureColumns();
34561
34562         // reset column Y
34563         var i = this.cols;
34564         this.colYs = [];
34565         while (i--) {
34566             this.colYs.push( 0 );
34567         }
34568     
34569         this.maxY = 0;
34570     },
34571
34572     measureColumns : function()
34573     {
34574         this.getContainerWidth();
34575       // if columnWidth is 0, default to outerWidth of first item
34576         if ( !this.columnWidth ) {
34577             var firstItem = this.bricks.first();
34578             Roo.log(firstItem);
34579             this.columnWidth  = this.containerWidth;
34580             if (firstItem && firstItem.attr('originalwidth') ) {
34581                 this.columnWidth = 1* (firstItem.attr('originalwidth') || firstItem.getWidth());
34582             }
34583             // columnWidth fall back to item of first element
34584             Roo.log("set column width?");
34585                         this.initialColumnWidth = this.columnWidth  ;
34586
34587             // if first elem has no width, default to size of container
34588             
34589         }
34590         
34591         
34592         if (this.initialColumnWidth) {
34593             this.columnWidth = this.initialColumnWidth;
34594         }
34595         
34596         
34597             
34598         // column width is fixed at the top - however if container width get's smaller we should
34599         // reduce it...
34600         
34601         // this bit calcs how man columns..
34602             
34603         var columnWidth = this.columnWidth += this.gutter;
34604       
34605         // calculate columns
34606         var containerWidth = this.containerWidth + this.gutter;
34607         
34608         var cols = (containerWidth - this.padWidth) / (columnWidth - this.padWidth);
34609         // fix rounding errors, typically with gutters
34610         var excess = columnWidth - containerWidth % columnWidth;
34611         
34612         
34613         // if overshoot is less than a pixel, round up, otherwise floor it
34614         var mathMethod = excess && excess < 1 ? 'round' : 'floor';
34615         cols = Math[ mathMethod ]( cols );
34616         this.cols = Math.max( cols, 1 );
34617         this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
34618         
34619          // padding positioning..
34620         var totalColWidth = this.cols * this.columnWidth;
34621         var padavail = this.containerWidth - totalColWidth;
34622         // so for 2 columns - we need 3 'pads'
34623         
34624         var padNeeded = (1+this.cols) * this.padWidth;
34625         
34626         var padExtra = Math.floor((padavail - padNeeded) / this.cols);
34627         
34628         this.columnWidth += padExtra
34629         //this.padWidth = Math.floor(padavail /  ( this.cols));
34630         
34631         // adjust colum width so that padding is fixed??
34632         
34633         // we have 3 columns ... total = width * 3
34634         // we have X left over... that should be used by 
34635         
34636         //if (this.expandC) {
34637             
34638         //}
34639         
34640         
34641         
34642     },
34643     
34644     getContainerWidth : function()
34645     {
34646        /* // container is parent if fit width
34647         var container = this.isFitWidth ? this.element.parentNode : this.element;
34648         // check that this.size and size are there
34649         // IE8 triggers resize on body size change, so they might not be
34650         
34651         var size = getSize( container );  //FIXME
34652         this.containerWidth = size && size.innerWidth; //FIXME
34653         */
34654          
34655         this.containerWidth = this.el.getBox(true).width;  //maybe use getComputedWidth
34656         
34657     },
34658     
34659     _getItemLayoutPosition : function( item )  // what is item?
34660     {
34661         // we resize the item to our columnWidth..
34662       
34663         item.setWidth(this.columnWidth);
34664         item.autoBoxAdjust  = false;
34665         
34666         var sz = item.getSize();
34667  
34668         // how many columns does this brick span
34669         var remainder = this.containerWidth % this.columnWidth;
34670         
34671         var mathMethod = remainder && remainder < 1 ? 'round' : 'ceil';
34672         // round if off by 1 pixel, otherwise use ceil
34673         var colSpan = Math[ mathMethod ]( sz.width  / this.columnWidth );
34674         colSpan = Math.min( colSpan, this.cols );
34675         
34676         // normally this should be '1' as we dont' currently allow multi width columns..
34677         
34678         var colGroup = this._getColGroup( colSpan );
34679         // get the minimum Y value from the columns
34680         var minimumY = Math.min.apply( Math, colGroup );
34681         Roo.log([ 'setHeight',  minimumY, sz.height, setHeight ]);
34682         
34683         var shortColIndex = colGroup.indexOf(  minimumY ); // broken on ie8..?? probably...
34684          
34685         // position the brick
34686         var position = {
34687             x: this.currentSize.x + (this.padWidth /2) + ((this.columnWidth + this.padWidth )* shortColIndex),
34688             y: this.currentSize.y + minimumY + this.padHeight
34689         };
34690         
34691         Roo.log(position);
34692         // apply setHeight to necessary columns
34693         var setHeight = minimumY + sz.height + this.padHeight;
34694         //Roo.log([ 'setHeight',  minimumY, sz.height, setHeight ]);
34695         
34696         var setSpan = this.cols + 1 - colGroup.length;
34697         for ( var i = 0; i < setSpan; i++ ) {
34698           this.colYs[ shortColIndex + i ] = setHeight ;
34699         }
34700       
34701         return position;
34702     },
34703     
34704     /**
34705      * @param {Number} colSpan - number of columns the element spans
34706      * @returns {Array} colGroup
34707      */
34708     _getColGroup : function( colSpan )
34709     {
34710         if ( colSpan < 2 ) {
34711           // if brick spans only one column, use all the column Ys
34712           return this.colYs;
34713         }
34714       
34715         var colGroup = [];
34716         // how many different places could this brick fit horizontally
34717         var groupCount = this.cols + 1 - colSpan;
34718         // for each group potential horizontal position
34719         for ( var i = 0; i < groupCount; i++ ) {
34720           // make an array of colY values for that one group
34721           var groupColYs = this.colYs.slice( i, i + colSpan );
34722           // and get the max value of the array
34723           colGroup[i] = Math.max.apply( Math, groupColYs );
34724         }
34725         return colGroup;
34726     },
34727     /*
34728     _manageStamp : function( stamp )
34729     {
34730         var stampSize =  stamp.getSize();
34731         var offset = stamp.getBox();
34732         // get the columns that this stamp affects
34733         var firstX = this.isOriginLeft ? offset.x : offset.right;
34734         var lastX = firstX + stampSize.width;
34735         var firstCol = Math.floor( firstX / this.columnWidth );
34736         firstCol = Math.max( 0, firstCol );
34737         
34738         var lastCol = Math.floor( lastX / this.columnWidth );
34739         // lastCol should not go over if multiple of columnWidth #425
34740         lastCol -= lastX % this.columnWidth ? 0 : 1;
34741         lastCol = Math.min( this.cols - 1, lastCol );
34742         
34743         // set colYs to bottom of the stamp
34744         var stampMaxY = ( this.isOriginTop ? offset.y : offset.bottom ) +
34745             stampSize.height;
34746             
34747         for ( var i = firstCol; i <= lastCol; i++ ) {
34748           this.colYs[i] = Math.max( stampMaxY, this.colYs[i] );
34749         }
34750     },
34751     */
34752     
34753     _getContainerSize : function()
34754     {
34755         this.maxY = Math.max.apply( Math, this.colYs );
34756         var size = {
34757             height: this.maxY
34758         };
34759       
34760         if ( this.isFitWidth ) {
34761             size.width = this._getContainerFitWidth();
34762         }
34763       
34764         return size;
34765     },
34766     
34767     _getContainerFitWidth : function()
34768     {
34769         var unusedCols = 0;
34770         // count unused columns
34771         var i = this.cols;
34772         while ( --i ) {
34773           if ( this.colYs[i] !== 0 ) {
34774             break;
34775           }
34776           unusedCols++;
34777         }
34778         // fit container to columns that have been used
34779         return ( this.cols - unusedCols ) * this.columnWidth - this.gutter;
34780     },
34781     
34782     needsResizeLayout : function()
34783     {
34784         var previousWidth = this.containerWidth;
34785         this.getContainerWidth();
34786         return previousWidth !== this.containerWidth;
34787     }
34788  
34789 });
34790
34791  
34792
34793  /*
34794  * - LGPL
34795  *
34796  * element
34797  * 
34798  */
34799
34800 /**
34801  * @class Roo.bootstrap.MasonryBrick
34802  * @extends Roo.bootstrap.Component
34803  * Bootstrap MasonryBrick class
34804  * 
34805  * @constructor
34806  * Create a new MasonryBrick
34807  * @param {Object} config The config object
34808  */
34809
34810 Roo.bootstrap.MasonryBrick = function(config){
34811     
34812     Roo.bootstrap.MasonryBrick.superclass.constructor.call(this, config);
34813     
34814     Roo.bootstrap.MasonryBrick.register(this);
34815     
34816     this.addEvents({
34817         // raw events
34818         /**
34819          * @event click
34820          * When a MasonryBrick is clcik
34821          * @param {Roo.bootstrap.MasonryBrick} this
34822          * @param {Roo.EventObject} e
34823          */
34824         "click" : true
34825     });
34826 };
34827
34828 Roo.extend(Roo.bootstrap.MasonryBrick, Roo.bootstrap.Component,  {
34829     
34830     /**
34831      * @cfg {String} title
34832      */   
34833     title : '',
34834     /**
34835      * @cfg {String} html
34836      */   
34837     html : '',
34838     /**
34839      * @cfg {String} bgimage
34840      */   
34841     bgimage : '',
34842     /**
34843      * @cfg {String} videourl
34844      */   
34845     videourl : '',
34846     /**
34847      * @cfg {String} cls
34848      */   
34849     cls : '',
34850     /**
34851      * @cfg {String} href
34852      */   
34853     href : '',
34854     /**
34855      * @cfg {String} size (xs|sm|md|md-left|md-right|tall|wide)
34856      */   
34857     size : 'xs',
34858     
34859     /**
34860      * @cfg {String} placetitle (center|bottom)
34861      */   
34862     placetitle : '',
34863     
34864     /**
34865      * @cfg {Boolean} isFitContainer defalut true
34866      */   
34867     isFitContainer : true, 
34868     
34869     /**
34870      * @cfg {Boolean} preventDefault defalut false
34871      */   
34872     preventDefault : false, 
34873     
34874     /**
34875      * @cfg {Boolean} inverse defalut false
34876      */   
34877     maskInverse : false, 
34878     
34879     getAutoCreate : function()
34880     {
34881         if(!this.isFitContainer){
34882             return this.getSplitAutoCreate();
34883         }
34884         
34885         var cls = 'masonry-brick masonry-brick-full';
34886         
34887         if(this.href.length){
34888             cls += ' masonry-brick-link';
34889         }
34890         
34891         if(this.bgimage.length){
34892             cls += ' masonry-brick-image';
34893         }
34894         
34895         if(this.maskInverse){
34896             cls += ' mask-inverse';
34897         }
34898         
34899         if(!this.html.length && !this.maskInverse && !this.videourl.length){
34900             cls += ' enable-mask';
34901         }
34902         
34903         if(this.size){
34904             cls += ' masonry-' + this.size + '-brick';
34905         }
34906         
34907         if(this.placetitle.length){
34908             
34909             switch (this.placetitle) {
34910                 case 'center' :
34911                     cls += ' masonry-center-title';
34912                     break;
34913                 case 'bottom' :
34914                     cls += ' masonry-bottom-title';
34915                     break;
34916                 default:
34917                     break;
34918             }
34919             
34920         } else {
34921             if(!this.html.length && !this.bgimage.length){
34922                 cls += ' masonry-center-title';
34923             }
34924
34925             if(!this.html.length && this.bgimage.length){
34926                 cls += ' masonry-bottom-title';
34927             }
34928         }
34929         
34930         if(this.cls){
34931             cls += ' ' + this.cls;
34932         }
34933         
34934         var cfg = {
34935             tag: (this.href.length) ? 'a' : 'div',
34936             cls: cls,
34937             cn: [
34938                 {
34939                     tag: 'div',
34940                     cls: 'masonry-brick-mask'
34941                 },
34942                 {
34943                     tag: 'div',
34944                     cls: 'masonry-brick-paragraph',
34945                     cn: []
34946                 }
34947             ]
34948         };
34949         
34950         if(this.href.length){
34951             cfg.href = this.href;
34952         }
34953         
34954         var cn = cfg.cn[1].cn;
34955         
34956         if(this.title.length){
34957             cn.push({
34958                 tag: 'h4',
34959                 cls: 'masonry-brick-title',
34960                 html: this.title
34961             });
34962         }
34963         
34964         if(this.html.length){
34965             cn.push({
34966                 tag: 'p',
34967                 cls: 'masonry-brick-text',
34968                 html: this.html
34969             });
34970         }
34971         
34972         if (!this.title.length && !this.html.length) {
34973             cfg.cn[1].cls += ' hide';
34974         }
34975         
34976         if(this.bgimage.length){
34977             cfg.cn.push({
34978                 tag: 'img',
34979                 cls: 'masonry-brick-image-view',
34980                 src: this.bgimage
34981             });
34982         }
34983         
34984         if(this.videourl.length){
34985             var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
34986             // youtube support only?
34987             cfg.cn.push({
34988                 tag: 'iframe',
34989                 cls: 'masonry-brick-image-view',
34990                 src: vurl,
34991                 frameborder : 0,
34992                 allowfullscreen : true
34993             });
34994         }
34995         
34996         return cfg;
34997         
34998     },
34999     
35000     getSplitAutoCreate : function()
35001     {
35002         var cls = 'masonry-brick masonry-brick-split';
35003         
35004         if(this.href.length){
35005             cls += ' masonry-brick-link';
35006         }
35007         
35008         if(this.bgimage.length){
35009             cls += ' masonry-brick-image';
35010         }
35011         
35012         if(this.size){
35013             cls += ' masonry-' + this.size + '-brick';
35014         }
35015         
35016         switch (this.placetitle) {
35017             case 'center' :
35018                 cls += ' masonry-center-title';
35019                 break;
35020             case 'bottom' :
35021                 cls += ' masonry-bottom-title';
35022                 break;
35023             default:
35024                 if(!this.bgimage.length){
35025                     cls += ' masonry-center-title';
35026                 }
35027
35028                 if(this.bgimage.length){
35029                     cls += ' masonry-bottom-title';
35030                 }
35031                 break;
35032         }
35033         
35034         if(this.cls){
35035             cls += ' ' + this.cls;
35036         }
35037         
35038         var cfg = {
35039             tag: (this.href.length) ? 'a' : 'div',
35040             cls: cls,
35041             cn: [
35042                 {
35043                     tag: 'div',
35044                     cls: 'masonry-brick-split-head',
35045                     cn: [
35046                         {
35047                             tag: 'div',
35048                             cls: 'masonry-brick-paragraph',
35049                             cn: []
35050                         }
35051                     ]
35052                 },
35053                 {
35054                     tag: 'div',
35055                     cls: 'masonry-brick-split-body',
35056                     cn: []
35057                 }
35058             ]
35059         };
35060         
35061         if(this.href.length){
35062             cfg.href = this.href;
35063         }
35064         
35065         if(this.title.length){
35066             cfg.cn[0].cn[0].cn.push({
35067                 tag: 'h4',
35068                 cls: 'masonry-brick-title',
35069                 html: this.title
35070             });
35071         }
35072         
35073         if(this.html.length){
35074             cfg.cn[1].cn.push({
35075                 tag: 'p',
35076                 cls: 'masonry-brick-text',
35077                 html: this.html
35078             });
35079         }
35080
35081         if(this.bgimage.length){
35082             cfg.cn[0].cn.push({
35083                 tag: 'img',
35084                 cls: 'masonry-brick-image-view',
35085                 src: this.bgimage
35086             });
35087         }
35088         
35089         if(this.videourl.length){
35090             var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
35091             // youtube support only?
35092             cfg.cn[0].cn.cn.push({
35093                 tag: 'iframe',
35094                 cls: 'masonry-brick-image-view',
35095                 src: vurl,
35096                 frameborder : 0,
35097                 allowfullscreen : true
35098             });
35099         }
35100         
35101         return cfg;
35102     },
35103     
35104     initEvents: function() 
35105     {
35106         switch (this.size) {
35107             case 'xs' :
35108                 this.x = 1;
35109                 this.y = 1;
35110                 break;
35111             case 'sm' :
35112                 this.x = 2;
35113                 this.y = 2;
35114                 break;
35115             case 'md' :
35116             case 'md-left' :
35117             case 'md-right' :
35118                 this.x = 3;
35119                 this.y = 3;
35120                 break;
35121             case 'tall' :
35122                 this.x = 2;
35123                 this.y = 3;
35124                 break;
35125             case 'wide' :
35126                 this.x = 3;
35127                 this.y = 2;
35128                 break;
35129             case 'wide-thin' :
35130                 this.x = 3;
35131                 this.y = 1;
35132                 break;
35133                         
35134             default :
35135                 break;
35136         }
35137         
35138         if(Roo.isTouch){
35139             this.el.on('touchstart', this.onTouchStart, this);
35140             this.el.on('touchmove', this.onTouchMove, this);
35141             this.el.on('touchend', this.onTouchEnd, this);
35142             this.el.on('contextmenu', this.onContextMenu, this);
35143         } else {
35144             this.el.on('mouseenter'  ,this.enter, this);
35145             this.el.on('mouseleave', this.leave, this);
35146             this.el.on('click', this.onClick, this);
35147         }
35148         
35149         if (typeof(this.parent().bricks) == 'object' && this.parent().bricks != null) {
35150             this.parent().bricks.push(this);   
35151         }
35152         
35153     },
35154     
35155     onClick: function(e, el)
35156     {
35157         var time = this.endTimer - this.startTimer;
35158         // Roo.log(e.preventDefault());
35159         if(Roo.isTouch){
35160             if(time > 1000){
35161                 e.preventDefault();
35162                 return;
35163             }
35164         }
35165         
35166         if(!this.preventDefault){
35167             return;
35168         }
35169         
35170         e.preventDefault();
35171         
35172         if (this.activeClass != '') {
35173             this.selectBrick();
35174         }
35175         
35176         this.fireEvent('click', this, e);
35177     },
35178     
35179     enter: function(e, el)
35180     {
35181         e.preventDefault();
35182         
35183         if(!this.isFitContainer || this.maskInverse || this.videourl.length){
35184             return;
35185         }
35186         
35187         if(this.bgimage.length && this.html.length){
35188             this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
35189         }
35190     },
35191     
35192     leave: function(e, el)
35193     {
35194         e.preventDefault();
35195         
35196         if(!this.isFitContainer || this.maskInverse  || this.videourl.length){
35197             return;
35198         }
35199         
35200         if(this.bgimage.length && this.html.length){
35201             this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
35202         }
35203     },
35204     
35205     onTouchStart: function(e, el)
35206     {
35207 //        e.preventDefault();
35208         
35209         this.touchmoved = false;
35210         
35211         if(!this.isFitContainer){
35212             return;
35213         }
35214         
35215         if(!this.bgimage.length || !this.html.length){
35216             return;
35217         }
35218         
35219         this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
35220         
35221         this.timer = new Date().getTime();
35222         
35223     },
35224     
35225     onTouchMove: function(e, el)
35226     {
35227         this.touchmoved = true;
35228     },
35229     
35230     onContextMenu : function(e,el)
35231     {
35232         e.preventDefault();
35233         e.stopPropagation();
35234         return false;
35235     },
35236     
35237     onTouchEnd: function(e, el)
35238     {
35239 //        e.preventDefault();
35240         
35241         if((new Date().getTime() - this.timer > 1000) || !this.href.length || this.touchmoved){
35242         
35243             this.leave(e,el);
35244             
35245             return;
35246         }
35247         
35248         if(!this.bgimage.length || !this.html.length){
35249             
35250             if(this.href.length){
35251                 window.location.href = this.href;
35252             }
35253             
35254             return;
35255         }
35256         
35257         if(!this.isFitContainer){
35258             return;
35259         }
35260         
35261         this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
35262         
35263         window.location.href = this.href;
35264     },
35265     
35266     //selection on single brick only
35267     selectBrick : function() {
35268         
35269         if (!this.parentId) {
35270             return;
35271         }
35272         
35273         var m = Roo.bootstrap.LayoutMasonry.get(this.parentId);
35274         var index = m.selectedBrick.indexOf(this.id);
35275         
35276         if ( index > -1) {
35277             m.selectedBrick.splice(index,1);
35278             this.el.removeClass(this.activeClass);
35279             return;
35280         }
35281         
35282         for(var i = 0; i < m.selectedBrick.length; i++) {
35283             var b = Roo.bootstrap.MasonryBrick.get(m.selectedBrick[i]);
35284             b.el.removeClass(b.activeClass);
35285         }
35286         
35287         m.selectedBrick = [];
35288         
35289         m.selectedBrick.push(this.id);
35290         this.el.addClass(this.activeClass);
35291         return;
35292     },
35293     
35294     isSelected : function(){
35295         return this.el.hasClass(this.activeClass);
35296         
35297     }
35298 });
35299
35300 Roo.apply(Roo.bootstrap.MasonryBrick, {
35301     
35302     //groups: {},
35303     groups : new Roo.util.MixedCollection(false, function(o) { return o.el.id; }),
35304      /**
35305     * register a Masonry Brick
35306     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
35307     */
35308     
35309     register : function(brick)
35310     {
35311         //this.groups[brick.id] = brick;
35312         this.groups.add(brick.id, brick);
35313     },
35314     /**
35315     * fetch a  masonry brick based on the masonry brick ID
35316     * @param {string} the masonry brick to add
35317     * @returns {Roo.bootstrap.MasonryBrick} the masonry brick
35318     */
35319     
35320     get: function(brick_id) 
35321     {
35322         // if (typeof(this.groups[brick_id]) == 'undefined') {
35323         //     return false;
35324         // }
35325         // return this.groups[brick_id] ;
35326         
35327         if(this.groups.key(brick_id)) {
35328             return this.groups.key(brick_id);
35329         }
35330         
35331         return false;
35332     }
35333     
35334     
35335     
35336 });
35337
35338  /*
35339  * - LGPL
35340  *
35341  * element
35342  * 
35343  */
35344
35345 /**
35346  * @class Roo.bootstrap.Brick
35347  * @extends Roo.bootstrap.Component
35348  * Bootstrap Brick class
35349  * 
35350  * @constructor
35351  * Create a new Brick
35352  * @param {Object} config The config object
35353  */
35354
35355 Roo.bootstrap.Brick = function(config){
35356     Roo.bootstrap.Brick.superclass.constructor.call(this, config);
35357     
35358     this.addEvents({
35359         // raw events
35360         /**
35361          * @event click
35362          * When a Brick is click
35363          * @param {Roo.bootstrap.Brick} this
35364          * @param {Roo.EventObject} e
35365          */
35366         "click" : true
35367     });
35368 };
35369
35370 Roo.extend(Roo.bootstrap.Brick, Roo.bootstrap.Component,  {
35371     
35372     /**
35373      * @cfg {String} title
35374      */   
35375     title : '',
35376     /**
35377      * @cfg {String} html
35378      */   
35379     html : '',
35380     /**
35381      * @cfg {String} bgimage
35382      */   
35383     bgimage : '',
35384     /**
35385      * @cfg {String} cls
35386      */   
35387     cls : '',
35388     /**
35389      * @cfg {String} href
35390      */   
35391     href : '',
35392     /**
35393      * @cfg {String} video
35394      */   
35395     video : '',
35396     /**
35397      * @cfg {Boolean} square
35398      */   
35399     square : true,
35400     
35401     getAutoCreate : function()
35402     {
35403         var cls = 'roo-brick';
35404         
35405         if(this.href.length){
35406             cls += ' roo-brick-link';
35407         }
35408         
35409         if(this.bgimage.length){
35410             cls += ' roo-brick-image';
35411         }
35412         
35413         if(!this.html.length && !this.bgimage.length){
35414             cls += ' roo-brick-center-title';
35415         }
35416         
35417         if(!this.html.length && this.bgimage.length){
35418             cls += ' roo-brick-bottom-title';
35419         }
35420         
35421         if(this.cls){
35422             cls += ' ' + this.cls;
35423         }
35424         
35425         var cfg = {
35426             tag: (this.href.length) ? 'a' : 'div',
35427             cls: cls,
35428             cn: [
35429                 {
35430                     tag: 'div',
35431                     cls: 'roo-brick-paragraph',
35432                     cn: []
35433                 }
35434             ]
35435         };
35436         
35437         if(this.href.length){
35438             cfg.href = this.href;
35439         }
35440         
35441         var cn = cfg.cn[0].cn;
35442         
35443         if(this.title.length){
35444             cn.push({
35445                 tag: 'h4',
35446                 cls: 'roo-brick-title',
35447                 html: this.title
35448             });
35449         }
35450         
35451         if(this.html.length){
35452             cn.push({
35453                 tag: 'p',
35454                 cls: 'roo-brick-text',
35455                 html: this.html
35456             });
35457         } else {
35458             cn.cls += ' hide';
35459         }
35460         
35461         if(this.bgimage.length){
35462             cfg.cn.push({
35463                 tag: 'img',
35464                 cls: 'roo-brick-image-view',
35465                 src: this.bgimage
35466             });
35467         }
35468         
35469         return cfg;
35470     },
35471     
35472     initEvents: function() 
35473     {
35474         if(this.title.length || this.html.length){
35475             this.el.on('mouseenter'  ,this.enter, this);
35476             this.el.on('mouseleave', this.leave, this);
35477         }
35478         
35479         Roo.EventManager.onWindowResize(this.resize, this); 
35480         
35481         if(this.bgimage.length){
35482             this.imageEl = this.el.select('.roo-brick-image-view', true).first();
35483             this.imageEl.on('load', this.onImageLoad, this);
35484             return;
35485         }
35486         
35487         this.resize();
35488     },
35489     
35490     onImageLoad : function()
35491     {
35492         this.resize();
35493     },
35494     
35495     resize : function()
35496     {
35497         var paragraph = this.el.select('.roo-brick-paragraph', true).first();
35498         
35499         paragraph.setHeight(paragraph.getWidth() + paragraph.getPadding('tb'));
35500         
35501         if(this.bgimage.length){
35502             var image = this.el.select('.roo-brick-image-view', true).first();
35503             
35504             image.setWidth(paragraph.getWidth());
35505             
35506             if(this.square){
35507                 image.setHeight(paragraph.getWidth());
35508             }
35509             
35510             this.el.setHeight(image.getHeight());
35511             paragraph.setHeight(image.getHeight());
35512             
35513         }
35514         
35515     },
35516     
35517     enter: function(e, el)
35518     {
35519         e.preventDefault();
35520         
35521         if(this.bgimage.length){
35522             this.el.select('.roo-brick-paragraph', true).first().setOpacity(0.9, true);
35523             this.el.select('.roo-brick-image-view', true).first().setOpacity(0.1, true);
35524         }
35525     },
35526     
35527     leave: function(e, el)
35528     {
35529         e.preventDefault();
35530         
35531         if(this.bgimage.length){
35532             this.el.select('.roo-brick-paragraph', true).first().setOpacity(0, true);
35533             this.el.select('.roo-brick-image-view', true).first().setOpacity(1, true);
35534         }
35535     }
35536     
35537 });
35538
35539  
35540
35541  /*
35542  * - LGPL
35543  *
35544  * Number field 
35545  */
35546
35547 /**
35548  * @class Roo.bootstrap.NumberField
35549  * @extends Roo.bootstrap.Input
35550  * Bootstrap NumberField class
35551  * 
35552  * 
35553  * 
35554  * 
35555  * @constructor
35556  * Create a new NumberField
35557  * @param {Object} config The config object
35558  */
35559
35560 Roo.bootstrap.NumberField = function(config){
35561     Roo.bootstrap.NumberField.superclass.constructor.call(this, config);
35562 };
35563
35564 Roo.extend(Roo.bootstrap.NumberField, Roo.bootstrap.Input, {
35565     
35566     /**
35567      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
35568      */
35569     allowDecimals : true,
35570     /**
35571      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
35572      */
35573     decimalSeparator : ".",
35574     /**
35575      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
35576      */
35577     decimalPrecision : 2,
35578     /**
35579      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
35580      */
35581     allowNegative : true,
35582     
35583     /**
35584      * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
35585      */
35586     allowZero: true,
35587     /**
35588      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
35589      */
35590     minValue : Number.NEGATIVE_INFINITY,
35591     /**
35592      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
35593      */
35594     maxValue : Number.MAX_VALUE,
35595     /**
35596      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
35597      */
35598     minText : "The minimum value for this field is {0}",
35599     /**
35600      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
35601      */
35602     maxText : "The maximum value for this field is {0}",
35603     /**
35604      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
35605      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
35606      */
35607     nanText : "{0} is not a valid number",
35608     /**
35609      * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
35610      */
35611     thousandsDelimiter : false,
35612     /**
35613      * @cfg {String} valueAlign alignment of value
35614      */
35615     valueAlign : "left",
35616
35617     getAutoCreate : function()
35618     {
35619         var hiddenInput = {
35620             tag: 'input',
35621             type: 'hidden',
35622             id: Roo.id(),
35623             cls: 'hidden-number-input'
35624         };
35625         
35626         if (this.name) {
35627             hiddenInput.name = this.name;
35628         }
35629         
35630         this.name = '';
35631         
35632         var cfg = Roo.bootstrap.NumberField.superclass.getAutoCreate.call(this);
35633         
35634         this.name = hiddenInput.name;
35635         
35636         if(cfg.cn.length > 0) {
35637             cfg.cn.push(hiddenInput);
35638         }
35639         
35640         return cfg;
35641     },
35642
35643     // private
35644     initEvents : function()
35645     {   
35646         Roo.bootstrap.NumberField.superclass.initEvents.call(this);
35647         
35648         var allowed = "0123456789";
35649         
35650         if(this.allowDecimals){
35651             allowed += this.decimalSeparator;
35652         }
35653         
35654         if(this.allowNegative){
35655             allowed += "-";
35656         }
35657         
35658         if(this.thousandsDelimiter) {
35659             allowed += ",";
35660         }
35661         
35662         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
35663         
35664         var keyPress = function(e){
35665             
35666             var k = e.getKey();
35667             
35668             var c = e.getCharCode();
35669             
35670             if(
35671                     (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
35672                     allowed.indexOf(String.fromCharCode(c)) === -1
35673             ){
35674                 e.stopEvent();
35675                 return;
35676             }
35677             
35678             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
35679                 return;
35680             }
35681             
35682             if(allowed.indexOf(String.fromCharCode(c)) === -1){
35683                 e.stopEvent();
35684             }
35685         };
35686         
35687         this.el.on("keypress", keyPress, this);
35688     },
35689     
35690     validateValue : function(value)
35691     {
35692         
35693         if(!Roo.bootstrap.NumberField.superclass.validateValue.call(this, value)){
35694             return false;
35695         }
35696         
35697         var num = this.parseValue(value);
35698         
35699         if(isNaN(num)){
35700             this.markInvalid(String.format(this.nanText, value));
35701             return false;
35702         }
35703         
35704         if(num < this.minValue){
35705             this.markInvalid(String.format(this.minText, this.minValue));
35706             return false;
35707         }
35708         
35709         if(num > this.maxValue){
35710             this.markInvalid(String.format(this.maxText, this.maxValue));
35711             return false;
35712         }
35713         
35714         return true;
35715     },
35716
35717     getValue : function()
35718     {
35719         var v = this.hiddenEl().getValue();
35720         
35721         return this.fixPrecision(this.parseValue(v));
35722     },
35723
35724     parseValue : function(value)
35725     {
35726         if(this.thousandsDelimiter) {
35727             value += "";
35728             r = new RegExp(",", "g");
35729             value = value.replace(r, "");
35730         }
35731         
35732         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
35733         return isNaN(value) ? '' : value;
35734     },
35735
35736     fixPrecision : function(value)
35737     {
35738         if(this.thousandsDelimiter) {
35739             value += "";
35740             r = new RegExp(",", "g");
35741             value = value.replace(r, "");
35742         }
35743         
35744         var nan = isNaN(value);
35745         
35746         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
35747             return nan ? '' : value;
35748         }
35749         return parseFloat(value).toFixed(this.decimalPrecision);
35750     },
35751
35752     setValue : function(v)
35753     {
35754         v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
35755         
35756         this.value = v;
35757         
35758         if(this.rendered){
35759             
35760             this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
35761             
35762             this.inputEl().dom.value = (v == '') ? '' :
35763                 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
35764             
35765             if(!this.allowZero && v === '0') {
35766                 this.hiddenEl().dom.value = '';
35767                 this.inputEl().dom.value = '';
35768             }
35769             
35770             this.validate();
35771         }
35772     },
35773
35774     decimalPrecisionFcn : function(v)
35775     {
35776         return Math.floor(v);
35777     },
35778
35779     beforeBlur : function()
35780     {
35781         var v = this.parseValue(this.getRawValue());
35782         
35783         if(v || v === 0 || v === ''){
35784             this.setValue(v);
35785         }
35786     },
35787     
35788     hiddenEl : function()
35789     {
35790         return this.el.select('input.hidden-number-input',true).first();
35791     }
35792     
35793 });
35794
35795  
35796
35797 /*
35798 * Licence: LGPL
35799 */
35800
35801 /**
35802  * @class Roo.bootstrap.DocumentSlider
35803  * @extends Roo.bootstrap.Component
35804  * Bootstrap DocumentSlider class
35805  * 
35806  * @constructor
35807  * Create a new DocumentViewer
35808  * @param {Object} config The config object
35809  */
35810
35811 Roo.bootstrap.DocumentSlider = function(config){
35812     Roo.bootstrap.DocumentSlider.superclass.constructor.call(this, config);
35813     
35814     this.files = [];
35815     
35816     this.addEvents({
35817         /**
35818          * @event initial
35819          * Fire after initEvent
35820          * @param {Roo.bootstrap.DocumentSlider} this
35821          */
35822         "initial" : true,
35823         /**
35824          * @event update
35825          * Fire after update
35826          * @param {Roo.bootstrap.DocumentSlider} this
35827          */
35828         "update" : true,
35829         /**
35830          * @event click
35831          * Fire after click
35832          * @param {Roo.bootstrap.DocumentSlider} this
35833          */
35834         "click" : true
35835     });
35836 };
35837
35838 Roo.extend(Roo.bootstrap.DocumentSlider, Roo.bootstrap.Component,  {
35839     
35840     files : false,
35841     
35842     indicator : 0,
35843     
35844     getAutoCreate : function()
35845     {
35846         var cfg = {
35847             tag : 'div',
35848             cls : 'roo-document-slider',
35849             cn : [
35850                 {
35851                     tag : 'div',
35852                     cls : 'roo-document-slider-header',
35853                     cn : [
35854                         {
35855                             tag : 'div',
35856                             cls : 'roo-document-slider-header-title'
35857                         }
35858                     ]
35859                 },
35860                 {
35861                     tag : 'div',
35862                     cls : 'roo-document-slider-body',
35863                     cn : [
35864                         {
35865                             tag : 'div',
35866                             cls : 'roo-document-slider-prev',
35867                             cn : [
35868                                 {
35869                                     tag : 'i',
35870                                     cls : 'fa fa-chevron-left'
35871                                 }
35872                             ]
35873                         },
35874                         {
35875                             tag : 'div',
35876                             cls : 'roo-document-slider-thumb',
35877                             cn : [
35878                                 {
35879                                     tag : 'img',
35880                                     cls : 'roo-document-slider-image'
35881                                 }
35882                             ]
35883                         },
35884                         {
35885                             tag : 'div',
35886                             cls : 'roo-document-slider-next',
35887                             cn : [
35888                                 {
35889                                     tag : 'i',
35890                                     cls : 'fa fa-chevron-right'
35891                                 }
35892                             ]
35893                         }
35894                     ]
35895                 }
35896             ]
35897         };
35898         
35899         return cfg;
35900     },
35901     
35902     initEvents : function()
35903     {
35904         this.headerEl = this.el.select('.roo-document-slider-header', true).first();
35905         this.headerEl.setVisibilityMode(Roo.Element.DISPLAY);
35906         
35907         this.titleEl = this.el.select('.roo-document-slider-header .roo-document-slider-header-title', true).first();
35908         this.titleEl.setVisibilityMode(Roo.Element.DISPLAY);
35909         
35910         this.bodyEl = this.el.select('.roo-document-slider-body', true).first();
35911         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
35912         
35913         this.thumbEl = this.el.select('.roo-document-slider-thumb', true).first();
35914         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
35915         
35916         this.imageEl = this.el.select('.roo-document-slider-image', true).first();
35917         this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
35918         
35919         this.prevIndicator = this.el.select('.roo-document-slider-prev i', true).first();
35920         this.prevIndicator.setVisibilityMode(Roo.Element.DISPLAY);
35921         
35922         this.nextIndicator = this.el.select('.roo-document-slider-next i', true).first();
35923         this.nextIndicator.setVisibilityMode(Roo.Element.DISPLAY);
35924         
35925         this.thumbEl.on('click', this.onClick, this);
35926         
35927         this.prevIndicator.on('click', this.prev, this);
35928         
35929         this.nextIndicator.on('click', this.next, this);
35930         
35931     },
35932     
35933     initial : function()
35934     {
35935         if(this.files.length){
35936             this.indicator = 1;
35937             this.update()
35938         }
35939         
35940         this.fireEvent('initial', this);
35941     },
35942     
35943     update : function()
35944     {
35945         this.imageEl.attr('src', this.files[this.indicator - 1]);
35946         
35947         this.titleEl.dom.innerHTML = String.format('{0} / {1}', this.indicator, this.files.length);
35948         
35949         this.prevIndicator.show();
35950         
35951         if(this.indicator == 1){
35952             this.prevIndicator.hide();
35953         }
35954         
35955         this.nextIndicator.show();
35956         
35957         if(this.indicator == this.files.length){
35958             this.nextIndicator.hide();
35959         }
35960         
35961         this.thumbEl.scrollTo('top');
35962         
35963         this.fireEvent('update', this);
35964     },
35965     
35966     onClick : function(e)
35967     {
35968         e.preventDefault();
35969         
35970         this.fireEvent('click', this);
35971     },
35972     
35973     prev : function(e)
35974     {
35975         e.preventDefault();
35976         
35977         this.indicator = Math.max(1, this.indicator - 1);
35978         
35979         this.update();
35980     },
35981     
35982     next : function(e)
35983     {
35984         e.preventDefault();
35985         
35986         this.indicator = Math.min(this.files.length, this.indicator + 1);
35987         
35988         this.update();
35989     }
35990 });
35991 /*
35992  * - LGPL
35993  *
35994  * RadioSet
35995  *
35996  *
35997  */
35998
35999 /**
36000  * @class Roo.bootstrap.RadioSet
36001  * @extends Roo.bootstrap.Input
36002  * Bootstrap RadioSet class
36003  * @cfg {String} indicatorpos (left|right) default left
36004  * @cfg {Boolean} inline (true|false) inline the element (default true)
36005  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the radio
36006  * @constructor
36007  * Create a new RadioSet
36008  * @param {Object} config The config object
36009  */
36010
36011 Roo.bootstrap.RadioSet = function(config){
36012     
36013     Roo.bootstrap.RadioSet.superclass.constructor.call(this, config);
36014     
36015     this.radioes = [];
36016     
36017     Roo.bootstrap.RadioSet.register(this);
36018     
36019     this.addEvents({
36020         /**
36021         * @event check
36022         * Fires when the element is checked or unchecked.
36023         * @param {Roo.bootstrap.RadioSet} this This radio
36024         * @param {Roo.bootstrap.Radio} item The checked item
36025         */
36026        check : true,
36027        /**
36028         * @event click
36029         * Fires when the element is click.
36030         * @param {Roo.bootstrap.RadioSet} this This radio set
36031         * @param {Roo.bootstrap.Radio} item The checked item
36032         * @param {Roo.EventObject} e The event object
36033         */
36034        click : true
36035     });
36036     
36037 };
36038
36039 Roo.extend(Roo.bootstrap.RadioSet, Roo.bootstrap.Input,  {
36040
36041     radioes : false,
36042     
36043     inline : true,
36044     
36045     weight : '',
36046     
36047     indicatorpos : 'left',
36048     
36049     getAutoCreate : function()
36050     {
36051         var label = {
36052             tag : 'label',
36053             cls : 'roo-radio-set-label',
36054             cn : [
36055                 {
36056                     tag : 'span',
36057                     html : this.fieldLabel
36058                 }
36059             ]
36060         };
36061         if (Roo.bootstrap.version == 3) {
36062             
36063             
36064             if(this.indicatorpos == 'left'){
36065                 label.cn.unshift({
36066                     tag : 'i',
36067                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
36068                     tooltip : 'This field is required'
36069                 });
36070             } else {
36071                 label.cn.push({
36072                     tag : 'i',
36073                     cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
36074                     tooltip : 'This field is required'
36075                 });
36076             }
36077         }
36078         var items = {
36079             tag : 'div',
36080             cls : 'roo-radio-set-items'
36081         };
36082         
36083         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
36084         
36085         if (align === 'left' && this.fieldLabel.length) {
36086             
36087             items = {
36088                 cls : "roo-radio-set-right", 
36089                 cn: [
36090                     items
36091                 ]
36092             };
36093             
36094             if(this.labelWidth > 12){
36095                 label.style = "width: " + this.labelWidth + 'px';
36096             }
36097             
36098             if(this.labelWidth < 13 && this.labelmd == 0){
36099                 this.labelmd = this.labelWidth;
36100             }
36101             
36102             if(this.labellg > 0){
36103                 label.cls += ' col-lg-' + this.labellg;
36104                 items.cls += ' col-lg-' + (12 - this.labellg);
36105             }
36106             
36107             if(this.labelmd > 0){
36108                 label.cls += ' col-md-' + this.labelmd;
36109                 items.cls += ' col-md-' + (12 - this.labelmd);
36110             }
36111             
36112             if(this.labelsm > 0){
36113                 label.cls += ' col-sm-' + this.labelsm;
36114                 items.cls += ' col-sm-' + (12 - this.labelsm);
36115             }
36116             
36117             if(this.labelxs > 0){
36118                 label.cls += ' col-xs-' + this.labelxs;
36119                 items.cls += ' col-xs-' + (12 - this.labelxs);
36120             }
36121         }
36122         
36123         var cfg = {
36124             tag : 'div',
36125             cls : 'roo-radio-set',
36126             cn : [
36127                 {
36128                     tag : 'input',
36129                     cls : 'roo-radio-set-input',
36130                     type : 'hidden',
36131                     name : this.name,
36132                     value : this.value ? this.value :  ''
36133                 },
36134                 label,
36135                 items
36136             ]
36137         };
36138         
36139         if(this.weight.length){
36140             cfg.cls += ' roo-radio-' + this.weight;
36141         }
36142         
36143         if(this.inline) {
36144             cfg.cls += ' roo-radio-set-inline';
36145         }
36146         
36147         var settings=this;
36148         ['xs','sm','md','lg'].map(function(size){
36149             if (settings[size]) {
36150                 cfg.cls += ' col-' + size + '-' + settings[size];
36151             }
36152         });
36153         
36154         return cfg;
36155         
36156     },
36157
36158     initEvents : function()
36159     {
36160         this.labelEl = this.el.select('.roo-radio-set-label', true).first();
36161         this.labelEl.setVisibilityMode(Roo.Element.DISPLAY);
36162         
36163         if(!this.fieldLabel.length){
36164             this.labelEl.hide();
36165         }
36166         
36167         this.itemsEl = this.el.select('.roo-radio-set-items', true).first();
36168         this.itemsEl.setVisibilityMode(Roo.Element.DISPLAY);
36169         
36170         this.indicator = this.indicatorEl();
36171         
36172         if(this.indicator){
36173             this.indicator.addClass('invisible');
36174         }
36175         
36176         this.originalValue = this.getValue();
36177         
36178     },
36179     
36180     inputEl: function ()
36181     {
36182         return this.el.select('.roo-radio-set-input', true).first();
36183     },
36184     
36185     getChildContainer : function()
36186     {
36187         return this.itemsEl;
36188     },
36189     
36190     register : function(item)
36191     {
36192         this.radioes.push(item);
36193         
36194     },
36195     
36196     validate : function()
36197     {   
36198         if(this.getVisibilityEl().hasClass('hidden')){
36199             return true;
36200         }
36201         
36202         var valid = false;
36203         
36204         Roo.each(this.radioes, function(i){
36205             if(!i.checked){
36206                 return;
36207             }
36208             
36209             valid = true;
36210             return false;
36211         });
36212         
36213         if(this.allowBlank) {
36214             return true;
36215         }
36216         
36217         if(this.disabled || valid){
36218             this.markValid();
36219             return true;
36220         }
36221         
36222         this.markInvalid();
36223         return false;
36224         
36225     },
36226     
36227     markValid : function()
36228     {
36229         if(this.labelEl.isVisible(true) && this.indicatorEl()){
36230             this.indicatorEl().removeClass('visible');
36231             this.indicatorEl().addClass('invisible');
36232         }
36233         
36234         
36235         if (Roo.bootstrap.version == 3) {
36236             this.el.removeClass([this.invalidClass, this.validClass]);
36237             this.el.addClass(this.validClass);
36238         } else {
36239             this.el.removeClass(['is-invalid','is-valid']);
36240             this.el.addClass(['is-valid']);
36241         }
36242         this.fireEvent('valid', this);
36243     },
36244     
36245     markInvalid : function(msg)
36246     {
36247         if(this.allowBlank || this.disabled){
36248             return;
36249         }
36250         
36251         if(this.labelEl.isVisible(true) && this.indicatorEl()){
36252             this.indicatorEl().removeClass('invisible');
36253             this.indicatorEl().addClass('visible');
36254         }
36255         if (Roo.bootstrap.version == 3) {
36256             this.el.removeClass([this.invalidClass, this.validClass]);
36257             this.el.addClass(this.invalidClass);
36258         } else {
36259             this.el.removeClass(['is-invalid','is-valid']);
36260             this.el.addClass(['is-invalid']);
36261         }
36262         
36263         this.fireEvent('invalid', this, msg);
36264         
36265     },
36266     
36267     setValue : function(v, suppressEvent)
36268     {   
36269         if(this.value === v){
36270             return;
36271         }
36272         
36273         this.value = v;
36274         
36275         if(this.rendered){
36276             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
36277         }
36278         
36279         Roo.each(this.radioes, function(i){
36280             i.checked = false;
36281             i.el.removeClass('checked');
36282         });
36283         
36284         Roo.each(this.radioes, function(i){
36285             
36286             if(i.value === v || i.value.toString() === v.toString()){
36287                 i.checked = true;
36288                 i.el.addClass('checked');
36289                 
36290                 if(suppressEvent !== true){
36291                     this.fireEvent('check', this, i);
36292                 }
36293                 
36294                 return false;
36295             }
36296             
36297         }, this);
36298         
36299         this.validate();
36300     },
36301     
36302     clearInvalid : function(){
36303         
36304         if(!this.el || this.preventMark){
36305             return;
36306         }
36307         
36308         this.el.removeClass([this.invalidClass]);
36309         
36310         this.fireEvent('valid', this);
36311     }
36312     
36313 });
36314
36315 Roo.apply(Roo.bootstrap.RadioSet, {
36316     
36317     groups: {},
36318     
36319     register : function(set)
36320     {
36321         this.groups[set.name] = set;
36322     },
36323     
36324     get: function(name) 
36325     {
36326         if (typeof(this.groups[name]) == 'undefined') {
36327             return false;
36328         }
36329         
36330         return this.groups[name] ;
36331     }
36332     
36333 });
36334 /*
36335  * Based on:
36336  * Ext JS Library 1.1.1
36337  * Copyright(c) 2006-2007, Ext JS, LLC.
36338  *
36339  * Originally Released Under LGPL - original licence link has changed is not relivant.
36340  *
36341  * Fork - LGPL
36342  * <script type="text/javascript">
36343  */
36344
36345
36346 /**
36347  * @class Roo.bootstrap.SplitBar
36348  * @extends Roo.util.Observable
36349  * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
36350  * <br><br>
36351  * Usage:
36352  * <pre><code>
36353 var split = new Roo.bootstrap.SplitBar("elementToDrag", "elementToSize",
36354                    Roo.bootstrap.SplitBar.HORIZONTAL, Roo.bootstrap.SplitBar.LEFT);
36355 split.setAdapter(new Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter("container"));
36356 split.minSize = 100;
36357 split.maxSize = 600;
36358 split.animate = true;
36359 split.on('moved', splitterMoved);
36360 </code></pre>
36361  * @constructor
36362  * Create a new SplitBar
36363  * @config {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar. 
36364  * @config {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged 
36365  * @config {Number} orientation (optional) Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
36366  * @config {Number} placement (optional) Either Roo.bootstrap.SplitBar.LEFT or Roo.bootstrap.SplitBar.RIGHT for horizontal or  
36367                         Roo.bootstrap.SplitBar.TOP or Roo.bootstrap.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
36368                         position of the SplitBar).
36369  */
36370 Roo.bootstrap.SplitBar = function(cfg){
36371     
36372     /** @private */
36373     
36374     //{
36375     //  dragElement : elm
36376     //  resizingElement: el,
36377         // optional..
36378     //    orientation : Either Roo.bootstrap.SplitBar.HORIZONTAL
36379     //    placement : Roo.bootstrap.SplitBar.LEFT  ,
36380         // existingProxy ???
36381     //}
36382     
36383     this.el = Roo.get(cfg.dragElement, true);
36384     this.el.dom.unselectable = "on";
36385     /** @private */
36386     this.resizingEl = Roo.get(cfg.resizingElement, true);
36387
36388     /**
36389      * @private
36390      * The orientation of the split. Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
36391      * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
36392      * @type Number
36393      */
36394     this.orientation = cfg.orientation || Roo.bootstrap.SplitBar.HORIZONTAL;
36395     
36396     /**
36397      * The minimum size of the resizing element. (Defaults to 0)
36398      * @type Number
36399      */
36400     this.minSize = 0;
36401     
36402     /**
36403      * The maximum size of the resizing element. (Defaults to 2000)
36404      * @type Number
36405      */
36406     this.maxSize = 2000;
36407     
36408     /**
36409      * Whether to animate the transition to the new size
36410      * @type Boolean
36411      */
36412     this.animate = false;
36413     
36414     /**
36415      * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
36416      * @type Boolean
36417      */
36418     this.useShim = false;
36419     
36420     /** @private */
36421     this.shim = null;
36422     
36423     if(!cfg.existingProxy){
36424         /** @private */
36425         this.proxy = Roo.bootstrap.SplitBar.createProxy(this.orientation);
36426     }else{
36427         this.proxy = Roo.get(cfg.existingProxy).dom;
36428     }
36429     /** @private */
36430     this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
36431     
36432     /** @private */
36433     this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
36434     
36435     /** @private */
36436     this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
36437     
36438     /** @private */
36439     this.dragSpecs = {};
36440     
36441     /**
36442      * @private The adapter to use to positon and resize elements
36443      */
36444     this.adapter = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
36445     this.adapter.init(this);
36446     
36447     if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
36448         /** @private */
36449         this.placement = cfg.placement || (this.el.getX() > this.resizingEl.getX() ? Roo.bootstrap.SplitBar.LEFT : Roo.bootstrap.SplitBar.RIGHT);
36450         this.el.addClass("roo-splitbar-h");
36451     }else{
36452         /** @private */
36453         this.placement = cfg.placement || (this.el.getY() > this.resizingEl.getY() ? Roo.bootstrap.SplitBar.TOP : Roo.bootstrap.SplitBar.BOTTOM);
36454         this.el.addClass("roo-splitbar-v");
36455     }
36456     
36457     this.addEvents({
36458         /**
36459          * @event resize
36460          * Fires when the splitter is moved (alias for {@link #event-moved})
36461          * @param {Roo.bootstrap.SplitBar} this
36462          * @param {Number} newSize the new width or height
36463          */
36464         "resize" : true,
36465         /**
36466          * @event moved
36467          * Fires when the splitter is moved
36468          * @param {Roo.bootstrap.SplitBar} this
36469          * @param {Number} newSize the new width or height
36470          */
36471         "moved" : true,
36472         /**
36473          * @event beforeresize
36474          * Fires before the splitter is dragged
36475          * @param {Roo.bootstrap.SplitBar} this
36476          */
36477         "beforeresize" : true,
36478
36479         "beforeapply" : true
36480     });
36481
36482     Roo.util.Observable.call(this);
36483 };
36484
36485 Roo.extend(Roo.bootstrap.SplitBar, Roo.util.Observable, {
36486     onStartProxyDrag : function(x, y){
36487         this.fireEvent("beforeresize", this);
36488         if(!this.overlay){
36489             var o = Roo.DomHelper.insertFirst(document.body,  {cls: "roo-drag-overlay", html: "&#160;"}, true);
36490             o.unselectable();
36491             o.enableDisplayMode("block");
36492             // all splitbars share the same overlay
36493             Roo.bootstrap.SplitBar.prototype.overlay = o;
36494         }
36495         this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
36496         this.overlay.show();
36497         Roo.get(this.proxy).setDisplayed("block");
36498         var size = this.adapter.getElementSize(this);
36499         this.activeMinSize = this.getMinimumSize();;
36500         this.activeMaxSize = this.getMaximumSize();;
36501         var c1 = size - this.activeMinSize;
36502         var c2 = Math.max(this.activeMaxSize - size, 0);
36503         if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
36504             this.dd.resetConstraints();
36505             this.dd.setXConstraint(
36506                 this.placement == Roo.bootstrap.SplitBar.LEFT ? c1 : c2, 
36507                 this.placement == Roo.bootstrap.SplitBar.LEFT ? c2 : c1
36508             );
36509             this.dd.setYConstraint(0, 0);
36510         }else{
36511             this.dd.resetConstraints();
36512             this.dd.setXConstraint(0, 0);
36513             this.dd.setYConstraint(
36514                 this.placement == Roo.bootstrap.SplitBar.TOP ? c1 : c2, 
36515                 this.placement == Roo.bootstrap.SplitBar.TOP ? c2 : c1
36516             );
36517          }
36518         this.dragSpecs.startSize = size;
36519         this.dragSpecs.startPoint = [x, y];
36520         Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
36521     },
36522     
36523     /** 
36524      * @private Called after the drag operation by the DDProxy
36525      */
36526     onEndProxyDrag : function(e){
36527         Roo.get(this.proxy).setDisplayed(false);
36528         var endPoint = Roo.lib.Event.getXY(e);
36529         if(this.overlay){
36530             this.overlay.hide();
36531         }
36532         var newSize;
36533         if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
36534             newSize = this.dragSpecs.startSize + 
36535                 (this.placement == Roo.bootstrap.SplitBar.LEFT ?
36536                     endPoint[0] - this.dragSpecs.startPoint[0] :
36537                     this.dragSpecs.startPoint[0] - endPoint[0]
36538                 );
36539         }else{
36540             newSize = this.dragSpecs.startSize + 
36541                 (this.placement == Roo.bootstrap.SplitBar.TOP ?
36542                     endPoint[1] - this.dragSpecs.startPoint[1] :
36543                     this.dragSpecs.startPoint[1] - endPoint[1]
36544                 );
36545         }
36546         newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
36547         if(newSize != this.dragSpecs.startSize){
36548             if(this.fireEvent('beforeapply', this, newSize) !== false){
36549                 this.adapter.setElementSize(this, newSize);
36550                 this.fireEvent("moved", this, newSize);
36551                 this.fireEvent("resize", this, newSize);
36552             }
36553         }
36554     },
36555     
36556     /**
36557      * Get the adapter this SplitBar uses
36558      * @return The adapter object
36559      */
36560     getAdapter : function(){
36561         return this.adapter;
36562     },
36563     
36564     /**
36565      * Set the adapter this SplitBar uses
36566      * @param {Object} adapter A SplitBar adapter object
36567      */
36568     setAdapter : function(adapter){
36569         this.adapter = adapter;
36570         this.adapter.init(this);
36571     },
36572     
36573     /**
36574      * Gets the minimum size for the resizing element
36575      * @return {Number} The minimum size
36576      */
36577     getMinimumSize : function(){
36578         return this.minSize;
36579     },
36580     
36581     /**
36582      * Sets the minimum size for the resizing element
36583      * @param {Number} minSize The minimum size
36584      */
36585     setMinimumSize : function(minSize){
36586         this.minSize = minSize;
36587     },
36588     
36589     /**
36590      * Gets the maximum size for the resizing element
36591      * @return {Number} The maximum size
36592      */
36593     getMaximumSize : function(){
36594         return this.maxSize;
36595     },
36596     
36597     /**
36598      * Sets the maximum size for the resizing element
36599      * @param {Number} maxSize The maximum size
36600      */
36601     setMaximumSize : function(maxSize){
36602         this.maxSize = maxSize;
36603     },
36604     
36605     /**
36606      * Sets the initialize size for the resizing element
36607      * @param {Number} size The initial size
36608      */
36609     setCurrentSize : function(size){
36610         var oldAnimate = this.animate;
36611         this.animate = false;
36612         this.adapter.setElementSize(this, size);
36613         this.animate = oldAnimate;
36614     },
36615     
36616     /**
36617      * Destroy this splitbar. 
36618      * @param {Boolean} removeEl True to remove the element
36619      */
36620     destroy : function(removeEl){
36621         if(this.shim){
36622             this.shim.remove();
36623         }
36624         this.dd.unreg();
36625         this.proxy.parentNode.removeChild(this.proxy);
36626         if(removeEl){
36627             this.el.remove();
36628         }
36629     }
36630 });
36631
36632 /**
36633  * @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.
36634  */
36635 Roo.bootstrap.SplitBar.createProxy = function(dir){
36636     var proxy = new Roo.Element(document.createElement("div"));
36637     proxy.unselectable();
36638     var cls = 'roo-splitbar-proxy';
36639     proxy.addClass(cls + ' ' + (dir == Roo.bootstrap.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
36640     document.body.appendChild(proxy.dom);
36641     return proxy.dom;
36642 };
36643
36644 /** 
36645  * @class Roo.bootstrap.SplitBar.BasicLayoutAdapter
36646  * Default Adapter. It assumes the splitter and resizing element are not positioned
36647  * elements and only gets/sets the width of the element. Generally used for table based layouts.
36648  */
36649 Roo.bootstrap.SplitBar.BasicLayoutAdapter = function(){
36650 };
36651
36652 Roo.bootstrap.SplitBar.BasicLayoutAdapter.prototype = {
36653     // do nothing for now
36654     init : function(s){
36655     
36656     },
36657     /**
36658      * Called before drag operations to get the current size of the resizing element. 
36659      * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
36660      */
36661      getElementSize : function(s){
36662         if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
36663             return s.resizingEl.getWidth();
36664         }else{
36665             return s.resizingEl.getHeight();
36666         }
36667     },
36668     
36669     /**
36670      * Called after drag operations to set the size of the resizing element.
36671      * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
36672      * @param {Number} newSize The new size to set
36673      * @param {Function} onComplete A function to be invoked when resizing is complete
36674      */
36675     setElementSize : function(s, newSize, onComplete){
36676         if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
36677             if(!s.animate){
36678                 s.resizingEl.setWidth(newSize);
36679                 if(onComplete){
36680                     onComplete(s, newSize);
36681                 }
36682             }else{
36683                 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
36684             }
36685         }else{
36686             
36687             if(!s.animate){
36688                 s.resizingEl.setHeight(newSize);
36689                 if(onComplete){
36690                     onComplete(s, newSize);
36691                 }
36692             }else{
36693                 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
36694             }
36695         }
36696     }
36697 };
36698
36699 /** 
36700  *@class Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter
36701  * @extends Roo.bootstrap.SplitBar.BasicLayoutAdapter
36702  * Adapter that  moves the splitter element to align with the resized sizing element. 
36703  * Used with an absolute positioned SplitBar.
36704  * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
36705  * document.body, make sure you assign an id to the body element.
36706  */
36707 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter = function(container){
36708     this.basic = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
36709     this.container = Roo.get(container);
36710 };
36711
36712 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter.prototype = {
36713     init : function(s){
36714         this.basic.init(s);
36715     },
36716     
36717     getElementSize : function(s){
36718         return this.basic.getElementSize(s);
36719     },
36720     
36721     setElementSize : function(s, newSize, onComplete){
36722         this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
36723     },
36724     
36725     moveSplitter : function(s){
36726         var yes = Roo.bootstrap.SplitBar;
36727         switch(s.placement){
36728             case yes.LEFT:
36729                 s.el.setX(s.resizingEl.getRight());
36730                 break;
36731             case yes.RIGHT:
36732                 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
36733                 break;
36734             case yes.TOP:
36735                 s.el.setY(s.resizingEl.getBottom());
36736                 break;
36737             case yes.BOTTOM:
36738                 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
36739                 break;
36740         }
36741     }
36742 };
36743
36744 /**
36745  * Orientation constant - Create a vertical SplitBar
36746  * @static
36747  * @type Number
36748  */
36749 Roo.bootstrap.SplitBar.VERTICAL = 1;
36750
36751 /**
36752  * Orientation constant - Create a horizontal SplitBar
36753  * @static
36754  * @type Number
36755  */
36756 Roo.bootstrap.SplitBar.HORIZONTAL = 2;
36757
36758 /**
36759  * Placement constant - The resizing element is to the left of the splitter element
36760  * @static
36761  * @type Number
36762  */
36763 Roo.bootstrap.SplitBar.LEFT = 1;
36764
36765 /**
36766  * Placement constant - The resizing element is to the right of the splitter element
36767  * @static
36768  * @type Number
36769  */
36770 Roo.bootstrap.SplitBar.RIGHT = 2;
36771
36772 /**
36773  * Placement constant - The resizing element is positioned above the splitter element
36774  * @static
36775  * @type Number
36776  */
36777 Roo.bootstrap.SplitBar.TOP = 3;
36778
36779 /**
36780  * Placement constant - The resizing element is positioned under splitter element
36781  * @static
36782  * @type Number
36783  */
36784 Roo.bootstrap.SplitBar.BOTTOM = 4;
36785 Roo.namespace("Roo.bootstrap.layout");/*
36786  * Based on:
36787  * Ext JS Library 1.1.1
36788  * Copyright(c) 2006-2007, Ext JS, LLC.
36789  *
36790  * Originally Released Under LGPL - original licence link has changed is not relivant.
36791  *
36792  * Fork - LGPL
36793  * <script type="text/javascript">
36794  */
36795
36796 /**
36797  * @class Roo.bootstrap.layout.Manager
36798  * @extends Roo.bootstrap.Component
36799  * Base class for layout managers.
36800  */
36801 Roo.bootstrap.layout.Manager = function(config)
36802 {
36803     Roo.bootstrap.layout.Manager.superclass.constructor.call(this,config);
36804
36805
36806
36807
36808
36809     /** false to disable window resize monitoring @type Boolean */
36810     this.monitorWindowResize = true;
36811     this.regions = {};
36812     this.addEvents({
36813         /**
36814          * @event layout
36815          * Fires when a layout is performed.
36816          * @param {Roo.LayoutManager} this
36817          */
36818         "layout" : true,
36819         /**
36820          * @event regionresized
36821          * Fires when the user resizes a region.
36822          * @param {Roo.LayoutRegion} region The resized region
36823          * @param {Number} newSize The new size (width for east/west, height for north/south)
36824          */
36825         "regionresized" : true,
36826         /**
36827          * @event regioncollapsed
36828          * Fires when a region is collapsed.
36829          * @param {Roo.LayoutRegion} region The collapsed region
36830          */
36831         "regioncollapsed" : true,
36832         /**
36833          * @event regionexpanded
36834          * Fires when a region is expanded.
36835          * @param {Roo.LayoutRegion} region The expanded region
36836          */
36837         "regionexpanded" : true
36838     });
36839     this.updating = false;
36840
36841     if (config.el) {
36842         this.el = Roo.get(config.el);
36843         this.initEvents();
36844     }
36845
36846 };
36847
36848 Roo.extend(Roo.bootstrap.layout.Manager, Roo.bootstrap.Component, {
36849
36850
36851     regions : null,
36852
36853     monitorWindowResize : true,
36854
36855
36856     updating : false,
36857
36858
36859     onRender : function(ct, position)
36860     {
36861         if(!this.el){
36862             this.el = Roo.get(ct);
36863             this.initEvents();
36864         }
36865         //this.fireEvent('render',this);
36866     },
36867
36868
36869     initEvents: function()
36870     {
36871
36872
36873         // ie scrollbar fix
36874         if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
36875             document.body.scroll = "no";
36876         }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
36877             this.el.position('relative');
36878         }
36879         this.id = this.el.id;
36880         this.el.addClass("roo-layout-container");
36881         Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
36882         if(this.el.dom != document.body ) {
36883             this.el.on('resize', this.layout,this);
36884             this.el.on('show', this.layout,this);
36885         }
36886
36887     },
36888
36889     /**
36890      * Returns true if this layout is currently being updated
36891      * @return {Boolean}
36892      */
36893     isUpdating : function(){
36894         return this.updating;
36895     },
36896
36897     /**
36898      * Suspend the LayoutManager from doing auto-layouts while
36899      * making multiple add or remove calls
36900      */
36901     beginUpdate : function(){
36902         this.updating = true;
36903     },
36904
36905     /**
36906      * Restore auto-layouts and optionally disable the manager from performing a layout
36907      * @param {Boolean} noLayout true to disable a layout update
36908      */
36909     endUpdate : function(noLayout){
36910         this.updating = false;
36911         if(!noLayout){
36912             this.layout();
36913         }
36914     },
36915
36916     layout: function(){
36917         // abstract...
36918     },
36919
36920     onRegionResized : function(region, newSize){
36921         this.fireEvent("regionresized", region, newSize);
36922         this.layout();
36923     },
36924
36925     onRegionCollapsed : function(region){
36926         this.fireEvent("regioncollapsed", region);
36927     },
36928
36929     onRegionExpanded : function(region){
36930         this.fireEvent("regionexpanded", region);
36931     },
36932
36933     /**
36934      * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
36935      * performs box-model adjustments.
36936      * @return {Object} The size as an object {width: (the width), height: (the height)}
36937      */
36938     getViewSize : function()
36939     {
36940         var size;
36941         if(this.el.dom != document.body){
36942             size = this.el.getSize();
36943         }else{
36944             size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
36945         }
36946         size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
36947         size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
36948         return size;
36949     },
36950
36951     /**
36952      * Returns the Element this layout is bound to.
36953      * @return {Roo.Element}
36954      */
36955     getEl : function(){
36956         return this.el;
36957     },
36958
36959     /**
36960      * Returns the specified region.
36961      * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
36962      * @return {Roo.LayoutRegion}
36963      */
36964     getRegion : function(target){
36965         return this.regions[target.toLowerCase()];
36966     },
36967
36968     onWindowResize : function(){
36969         if(this.monitorWindowResize){
36970             this.layout();
36971         }
36972     }
36973 });
36974 /*
36975  * Based on:
36976  * Ext JS Library 1.1.1
36977  * Copyright(c) 2006-2007, Ext JS, LLC.
36978  *
36979  * Originally Released Under LGPL - original licence link has changed is not relivant.
36980  *
36981  * Fork - LGPL
36982  * <script type="text/javascript">
36983  */
36984 /**
36985  * @class Roo.bootstrap.layout.Border
36986  * @extends Roo.bootstrap.layout.Manager
36987  * This class represents a common layout manager used in desktop applications. For screenshots and more details,
36988  * please see: examples/bootstrap/nested.html<br><br>
36989  
36990 <b>The container the layout is rendered into can be either the body element or any other element.
36991 If it is not the body element, the container needs to either be an absolute positioned element,
36992 or you will need to add "position:relative" to the css of the container.  You will also need to specify
36993 the container size if it is not the body element.</b>
36994
36995 * @constructor
36996 * Create a new Border
36997 * @param {Object} config Configuration options
36998  */
36999 Roo.bootstrap.layout.Border = function(config){
37000     config = config || {};
37001     Roo.bootstrap.layout.Border.superclass.constructor.call(this, config);
37002     
37003     
37004     
37005     Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
37006         if(config[region]){
37007             config[region].region = region;
37008             this.addRegion(config[region]);
37009         }
37010     },this);
37011     
37012 };
37013
37014 Roo.bootstrap.layout.Border.regions =  ["north","south","east","west","center"];
37015
37016 Roo.extend(Roo.bootstrap.layout.Border, Roo.bootstrap.layout.Manager, {
37017     
37018     parent : false, // this might point to a 'nest' or a ???
37019     
37020     /**
37021      * Creates and adds a new region if it doesn't already exist.
37022      * @param {String} target The target region key (north, south, east, west or center).
37023      * @param {Object} config The regions config object
37024      * @return {BorderLayoutRegion} The new region
37025      */
37026     addRegion : function(config)
37027     {
37028         if(!this.regions[config.region]){
37029             var r = this.factory(config);
37030             this.bindRegion(r);
37031         }
37032         return this.regions[config.region];
37033     },
37034
37035     // private (kinda)
37036     bindRegion : function(r){
37037         this.regions[r.config.region] = r;
37038         
37039         r.on("visibilitychange",    this.layout, this);
37040         r.on("paneladded",          this.layout, this);
37041         r.on("panelremoved",        this.layout, this);
37042         r.on("invalidated",         this.layout, this);
37043         r.on("resized",             this.onRegionResized, this);
37044         r.on("collapsed",           this.onRegionCollapsed, this);
37045         r.on("expanded",            this.onRegionExpanded, this);
37046     },
37047
37048     /**
37049      * Performs a layout update.
37050      */
37051     layout : function()
37052     {
37053         if(this.updating) {
37054             return;
37055         }
37056         
37057         // render all the rebions if they have not been done alreayd?
37058         Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
37059             if(this.regions[region] && !this.regions[region].bodyEl){
37060                 this.regions[region].onRender(this.el)
37061             }
37062         },this);
37063         
37064         var size = this.getViewSize();
37065         var w = size.width;
37066         var h = size.height;
37067         var centerW = w;
37068         var centerH = h;
37069         var centerY = 0;
37070         var centerX = 0;
37071         //var x = 0, y = 0;
37072
37073         var rs = this.regions;
37074         var north = rs["north"];
37075         var south = rs["south"]; 
37076         var west = rs["west"];
37077         var east = rs["east"];
37078         var center = rs["center"];
37079         //if(this.hideOnLayout){ // not supported anymore
37080             //c.el.setStyle("display", "none");
37081         //}
37082         if(north && north.isVisible()){
37083             var b = north.getBox();
37084             var m = north.getMargins();
37085             b.width = w - (m.left+m.right);
37086             b.x = m.left;
37087             b.y = m.top;
37088             centerY = b.height + b.y + m.bottom;
37089             centerH -= centerY;
37090             north.updateBox(this.safeBox(b));
37091         }
37092         if(south && south.isVisible()){
37093             var b = south.getBox();
37094             var m = south.getMargins();
37095             b.width = w - (m.left+m.right);
37096             b.x = m.left;
37097             var totalHeight = (b.height + m.top + m.bottom);
37098             b.y = h - totalHeight + m.top;
37099             centerH -= totalHeight;
37100             south.updateBox(this.safeBox(b));
37101         }
37102         if(west && west.isVisible()){
37103             var b = west.getBox();
37104             var m = west.getMargins();
37105             b.height = centerH - (m.top+m.bottom);
37106             b.x = m.left;
37107             b.y = centerY + m.top;
37108             var totalWidth = (b.width + m.left + m.right);
37109             centerX += totalWidth;
37110             centerW -= totalWidth;
37111             west.updateBox(this.safeBox(b));
37112         }
37113         if(east && east.isVisible()){
37114             var b = east.getBox();
37115             var m = east.getMargins();
37116             b.height = centerH - (m.top+m.bottom);
37117             var totalWidth = (b.width + m.left + m.right);
37118             b.x = w - totalWidth + m.left;
37119             b.y = centerY + m.top;
37120             centerW -= totalWidth;
37121             east.updateBox(this.safeBox(b));
37122         }
37123         if(center){
37124             var m = center.getMargins();
37125             var centerBox = {
37126                 x: centerX + m.left,
37127                 y: centerY + m.top,
37128                 width: centerW - (m.left+m.right),
37129                 height: centerH - (m.top+m.bottom)
37130             };
37131             //if(this.hideOnLayout){
37132                 //center.el.setStyle("display", "block");
37133             //}
37134             center.updateBox(this.safeBox(centerBox));
37135         }
37136         this.el.repaint();
37137         this.fireEvent("layout", this);
37138     },
37139
37140     // private
37141     safeBox : function(box){
37142         box.width = Math.max(0, box.width);
37143         box.height = Math.max(0, box.height);
37144         return box;
37145     },
37146
37147     /**
37148      * Adds a ContentPanel (or subclass) to this layout.
37149      * @param {String} target The target region key (north, south, east, west or center).
37150      * @param {Roo.ContentPanel} panel The panel to add
37151      * @return {Roo.ContentPanel} The added panel
37152      */
37153     add : function(target, panel){
37154          
37155         target = target.toLowerCase();
37156         return this.regions[target].add(panel);
37157     },
37158
37159     /**
37160      * Remove a ContentPanel (or subclass) to this layout.
37161      * @param {String} target The target region key (north, south, east, west or center).
37162      * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
37163      * @return {Roo.ContentPanel} The removed panel
37164      */
37165     remove : function(target, panel){
37166         target = target.toLowerCase();
37167         return this.regions[target].remove(panel);
37168     },
37169
37170     /**
37171      * Searches all regions for a panel with the specified id
37172      * @param {String} panelId
37173      * @return {Roo.ContentPanel} The panel or null if it wasn't found
37174      */
37175     findPanel : function(panelId){
37176         var rs = this.regions;
37177         for(var target in rs){
37178             if(typeof rs[target] != "function"){
37179                 var p = rs[target].getPanel(panelId);
37180                 if(p){
37181                     return p;
37182                 }
37183             }
37184         }
37185         return null;
37186     },
37187
37188     /**
37189      * Searches all regions for a panel with the specified id and activates (shows) it.
37190      * @param {String/ContentPanel} panelId The panels id or the panel itself
37191      * @return {Roo.ContentPanel} The shown panel or null
37192      */
37193     showPanel : function(panelId) {
37194       var rs = this.regions;
37195       for(var target in rs){
37196          var r = rs[target];
37197          if(typeof r != "function"){
37198             if(r.hasPanel(panelId)){
37199                return r.showPanel(panelId);
37200             }
37201          }
37202       }
37203       return null;
37204    },
37205
37206    /**
37207      * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
37208      * @param {Roo.state.Provider} provider (optional) An alternate state provider
37209      */
37210    /*
37211     restoreState : function(provider){
37212         if(!provider){
37213             provider = Roo.state.Manager;
37214         }
37215         var sm = new Roo.LayoutStateManager();
37216         sm.init(this, provider);
37217     },
37218 */
37219  
37220  
37221     /**
37222      * Adds a xtype elements to the layout.
37223      * <pre><code>
37224
37225 layout.addxtype({
37226        xtype : 'ContentPanel',
37227        region: 'west',
37228        items: [ .... ]
37229    }
37230 );
37231
37232 layout.addxtype({
37233         xtype : 'NestedLayoutPanel',
37234         region: 'west',
37235         layout: {
37236            center: { },
37237            west: { }   
37238         },
37239         items : [ ... list of content panels or nested layout panels.. ]
37240    }
37241 );
37242 </code></pre>
37243      * @param {Object} cfg Xtype definition of item to add.
37244      */
37245     addxtype : function(cfg)
37246     {
37247         // basically accepts a pannel...
37248         // can accept a layout region..!?!?
37249         //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
37250         
37251         
37252         // theory?  children can only be panels??
37253         
37254         //if (!cfg.xtype.match(/Panel$/)) {
37255         //    return false;
37256         //}
37257         var ret = false;
37258         
37259         if (typeof(cfg.region) == 'undefined') {
37260             Roo.log("Failed to add Panel, region was not set");
37261             Roo.log(cfg);
37262             return false;
37263         }
37264         var region = cfg.region;
37265         delete cfg.region;
37266         
37267           
37268         var xitems = [];
37269         if (cfg.items) {
37270             xitems = cfg.items;
37271             delete cfg.items;
37272         }
37273         var nb = false;
37274         
37275         if ( region == 'center') {
37276             Roo.log("Center: " + cfg.title);
37277         }
37278         
37279         
37280         switch(cfg.xtype) 
37281         {
37282             case 'Content':  // ContentPanel (el, cfg)
37283             case 'Scroll':  // ContentPanel (el, cfg)
37284             case 'View': 
37285                 cfg.autoCreate = cfg.autoCreate || true;
37286                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
37287                 //} else {
37288                 //    var el = this.el.createChild();
37289                 //    ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
37290                 //}
37291                 
37292                 this.add(region, ret);
37293                 break;
37294             
37295             /*
37296             case 'TreePanel': // our new panel!
37297                 cfg.el = this.el.createChild();
37298                 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
37299                 this.add(region, ret);
37300                 break;
37301             */
37302             
37303             case 'Nest': 
37304                 // create a new Layout (which is  a Border Layout...
37305                 
37306                 var clayout = cfg.layout;
37307                 clayout.el  = this.el.createChild();
37308                 clayout.items   = clayout.items  || [];
37309                 
37310                 delete cfg.layout;
37311                 
37312                 // replace this exitems with the clayout ones..
37313                 xitems = clayout.items;
37314                  
37315                 // force background off if it's in center...
37316                 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
37317                     cfg.background = false;
37318                 }
37319                 cfg.layout  = new Roo.bootstrap.layout.Border(clayout);
37320                 
37321                 
37322                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
37323                 //console.log('adding nested layout panel '  + cfg.toSource());
37324                 this.add(region, ret);
37325                 nb = {}; /// find first...
37326                 break;
37327             
37328             case 'Grid':
37329                 
37330                 // needs grid and region
37331                 
37332                 //var el = this.getRegion(region).el.createChild();
37333                 /*
37334                  *var el = this.el.createChild();
37335                 // create the grid first...
37336                 cfg.grid.container = el;
37337                 cfg.grid = new cfg.grid.xns[cfg.grid.xtype](cfg.grid);
37338                 */
37339                 
37340                 if (region == 'center' && this.active ) {
37341                     cfg.background = false;
37342                 }
37343                 
37344                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
37345                 
37346                 this.add(region, ret);
37347                 /*
37348                 if (cfg.background) {
37349                     // render grid on panel activation (if panel background)
37350                     ret.on('activate', function(gp) {
37351                         if (!gp.grid.rendered) {
37352                     //        gp.grid.render(el);
37353                         }
37354                     });
37355                 } else {
37356                   //  cfg.grid.render(el);
37357                 }
37358                 */
37359                 break;
37360            
37361            
37362             case 'Border': // it can get called on it'self... - might need to check if this is fixed?
37363                 // it was the old xcomponent building that caused this before.
37364                 // espeically if border is the top element in the tree.
37365                 ret = this;
37366                 break; 
37367                 
37368                     
37369                 
37370                 
37371                 
37372             default:
37373                 /*
37374                 if (typeof(Roo[cfg.xtype]) != 'undefined') {
37375                     
37376                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
37377                     this.add(region, ret);
37378                 } else {
37379                 */
37380                     Roo.log(cfg);
37381                     throw "Can not add '" + cfg.xtype + "' to Border";
37382                     return null;
37383              
37384                                 
37385              
37386         }
37387         this.beginUpdate();
37388         // add children..
37389         var region = '';
37390         var abn = {};
37391         Roo.each(xitems, function(i)  {
37392             region = nb && i.region ? i.region : false;
37393             
37394             var add = ret.addxtype(i);
37395            
37396             if (region) {
37397                 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
37398                 if (!i.background) {
37399                     abn[region] = nb[region] ;
37400                 }
37401             }
37402             
37403         });
37404         this.endUpdate();
37405
37406         // make the last non-background panel active..
37407         //if (nb) { Roo.log(abn); }
37408         if (nb) {
37409             
37410             for(var r in abn) {
37411                 region = this.getRegion(r);
37412                 if (region) {
37413                     // tried using nb[r], but it does not work..
37414                      
37415                     region.showPanel(abn[r]);
37416                    
37417                 }
37418             }
37419         }
37420         return ret;
37421         
37422     },
37423     
37424     
37425 // private
37426     factory : function(cfg)
37427     {
37428         
37429         var validRegions = Roo.bootstrap.layout.Border.regions;
37430
37431         var target = cfg.region;
37432         cfg.mgr = this;
37433         
37434         var r = Roo.bootstrap.layout;
37435         Roo.log(target);
37436         switch(target){
37437             case "north":
37438                 return new r.North(cfg);
37439             case "south":
37440                 return new r.South(cfg);
37441             case "east":
37442                 return new r.East(cfg);
37443             case "west":
37444                 return new r.West(cfg);
37445             case "center":
37446                 return new r.Center(cfg);
37447         }
37448         throw 'Layout region "'+target+'" not supported.';
37449     }
37450     
37451     
37452 });
37453  /*
37454  * Based on:
37455  * Ext JS Library 1.1.1
37456  * Copyright(c) 2006-2007, Ext JS, LLC.
37457  *
37458  * Originally Released Under LGPL - original licence link has changed is not relivant.
37459  *
37460  * Fork - LGPL
37461  * <script type="text/javascript">
37462  */
37463  
37464 /**
37465  * @class Roo.bootstrap.layout.Basic
37466  * @extends Roo.util.Observable
37467  * This class represents a lightweight region in a layout manager. This region does not move dom nodes
37468  * and does not have a titlebar, tabs or any other features. All it does is size and position 
37469  * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
37470  * @cfg {Roo.bootstrap.layout.Manager}   mgr The manager
37471  * @cfg {string}   region  the region that it inhabits..
37472  * @cfg {bool}   skipConfig skip config?
37473  * 
37474
37475  */
37476 Roo.bootstrap.layout.Basic = function(config){
37477     
37478     this.mgr = config.mgr;
37479     
37480     this.position = config.region;
37481     
37482     var skipConfig = config.skipConfig;
37483     
37484     this.events = {
37485         /**
37486          * @scope Roo.BasicLayoutRegion
37487          */
37488         
37489         /**
37490          * @event beforeremove
37491          * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
37492          * @param {Roo.LayoutRegion} this
37493          * @param {Roo.ContentPanel} panel The panel
37494          * @param {Object} e The cancel event object
37495          */
37496         "beforeremove" : true,
37497         /**
37498          * @event invalidated
37499          * Fires when the layout for this region is changed.
37500          * @param {Roo.LayoutRegion} this
37501          */
37502         "invalidated" : true,
37503         /**
37504          * @event visibilitychange
37505          * Fires when this region is shown or hidden 
37506          * @param {Roo.LayoutRegion} this
37507          * @param {Boolean} visibility true or false
37508          */
37509         "visibilitychange" : true,
37510         /**
37511          * @event paneladded
37512          * Fires when a panel is added. 
37513          * @param {Roo.LayoutRegion} this
37514          * @param {Roo.ContentPanel} panel The panel
37515          */
37516         "paneladded" : true,
37517         /**
37518          * @event panelremoved
37519          * Fires when a panel is removed. 
37520          * @param {Roo.LayoutRegion} this
37521          * @param {Roo.ContentPanel} panel The panel
37522          */
37523         "panelremoved" : true,
37524         /**
37525          * @event beforecollapse
37526          * Fires when this region before collapse.
37527          * @param {Roo.LayoutRegion} this
37528          */
37529         "beforecollapse" : true,
37530         /**
37531          * @event collapsed
37532          * Fires when this region is collapsed.
37533          * @param {Roo.LayoutRegion} this
37534          */
37535         "collapsed" : true,
37536         /**
37537          * @event expanded
37538          * Fires when this region is expanded.
37539          * @param {Roo.LayoutRegion} this
37540          */
37541         "expanded" : true,
37542         /**
37543          * @event slideshow
37544          * Fires when this region is slid into view.
37545          * @param {Roo.LayoutRegion} this
37546          */
37547         "slideshow" : true,
37548         /**
37549          * @event slidehide
37550          * Fires when this region slides out of view. 
37551          * @param {Roo.LayoutRegion} this
37552          */
37553         "slidehide" : true,
37554         /**
37555          * @event panelactivated
37556          * Fires when a panel is activated. 
37557          * @param {Roo.LayoutRegion} this
37558          * @param {Roo.ContentPanel} panel The activated panel
37559          */
37560         "panelactivated" : true,
37561         /**
37562          * @event resized
37563          * Fires when the user resizes this region. 
37564          * @param {Roo.LayoutRegion} this
37565          * @param {Number} newSize The new size (width for east/west, height for north/south)
37566          */
37567         "resized" : true
37568     };
37569     /** A collection of panels in this region. @type Roo.util.MixedCollection */
37570     this.panels = new Roo.util.MixedCollection();
37571     this.panels.getKey = this.getPanelId.createDelegate(this);
37572     this.box = null;
37573     this.activePanel = null;
37574     // ensure listeners are added...
37575     
37576     if (config.listeners || config.events) {
37577         Roo.bootstrap.layout.Basic.superclass.constructor.call(this, {
37578             listeners : config.listeners || {},
37579             events : config.events || {}
37580         });
37581     }
37582     
37583     if(skipConfig !== true){
37584         this.applyConfig(config);
37585     }
37586 };
37587
37588 Roo.extend(Roo.bootstrap.layout.Basic, Roo.util.Observable,
37589 {
37590     getPanelId : function(p){
37591         return p.getId();
37592     },
37593     
37594     applyConfig : function(config){
37595         this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
37596         this.config = config;
37597         
37598     },
37599     
37600     /**
37601      * Resizes the region to the specified size. For vertical regions (west, east) this adjusts 
37602      * the width, for horizontal (north, south) the height.
37603      * @param {Number} newSize The new width or height
37604      */
37605     resizeTo : function(newSize){
37606         var el = this.el ? this.el :
37607                  (this.activePanel ? this.activePanel.getEl() : null);
37608         if(el){
37609             switch(this.position){
37610                 case "east":
37611                 case "west":
37612                     el.setWidth(newSize);
37613                     this.fireEvent("resized", this, newSize);
37614                 break;
37615                 case "north":
37616                 case "south":
37617                     el.setHeight(newSize);
37618                     this.fireEvent("resized", this, newSize);
37619                 break;                
37620             }
37621         }
37622     },
37623     
37624     getBox : function(){
37625         return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
37626     },
37627     
37628     getMargins : function(){
37629         return this.margins;
37630     },
37631     
37632     updateBox : function(box){
37633         this.box = box;
37634         var el = this.activePanel.getEl();
37635         el.dom.style.left = box.x + "px";
37636         el.dom.style.top = box.y + "px";
37637         this.activePanel.setSize(box.width, box.height);
37638     },
37639     
37640     /**
37641      * Returns the container element for this region.
37642      * @return {Roo.Element}
37643      */
37644     getEl : function(){
37645         return this.activePanel;
37646     },
37647     
37648     /**
37649      * Returns true if this region is currently visible.
37650      * @return {Boolean}
37651      */
37652     isVisible : function(){
37653         return this.activePanel ? true : false;
37654     },
37655     
37656     setActivePanel : function(panel){
37657         panel = this.getPanel(panel);
37658         if(this.activePanel && this.activePanel != panel){
37659             this.activePanel.setActiveState(false);
37660             this.activePanel.getEl().setLeftTop(-10000,-10000);
37661         }
37662         this.activePanel = panel;
37663         panel.setActiveState(true);
37664         if(this.box){
37665             panel.setSize(this.box.width, this.box.height);
37666         }
37667         this.fireEvent("panelactivated", this, panel);
37668         this.fireEvent("invalidated");
37669     },
37670     
37671     /**
37672      * Show the specified panel.
37673      * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
37674      * @return {Roo.ContentPanel} The shown panel or null
37675      */
37676     showPanel : function(panel){
37677         panel = this.getPanel(panel);
37678         if(panel){
37679             this.setActivePanel(panel);
37680         }
37681         return panel;
37682     },
37683     
37684     /**
37685      * Get the active panel for this region.
37686      * @return {Roo.ContentPanel} The active panel or null
37687      */
37688     getActivePanel : function(){
37689         return this.activePanel;
37690     },
37691     
37692     /**
37693      * Add the passed ContentPanel(s)
37694      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
37695      * @return {Roo.ContentPanel} The panel added (if only one was added)
37696      */
37697     add : function(panel){
37698         if(arguments.length > 1){
37699             for(var i = 0, len = arguments.length; i < len; i++) {
37700                 this.add(arguments[i]);
37701             }
37702             return null;
37703         }
37704         if(this.hasPanel(panel)){
37705             this.showPanel(panel);
37706             return panel;
37707         }
37708         var el = panel.getEl();
37709         if(el.dom.parentNode != this.mgr.el.dom){
37710             this.mgr.el.dom.appendChild(el.dom);
37711         }
37712         if(panel.setRegion){
37713             panel.setRegion(this);
37714         }
37715         this.panels.add(panel);
37716         el.setStyle("position", "absolute");
37717         if(!panel.background){
37718             this.setActivePanel(panel);
37719             if(this.config.initialSize && this.panels.getCount()==1){
37720                 this.resizeTo(this.config.initialSize);
37721             }
37722         }
37723         this.fireEvent("paneladded", this, panel);
37724         return panel;
37725     },
37726     
37727     /**
37728      * Returns true if the panel is in this region.
37729      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
37730      * @return {Boolean}
37731      */
37732     hasPanel : function(panel){
37733         if(typeof panel == "object"){ // must be panel obj
37734             panel = panel.getId();
37735         }
37736         return this.getPanel(panel) ? true : false;
37737     },
37738     
37739     /**
37740      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
37741      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
37742      * @param {Boolean} preservePanel Overrides the config preservePanel option
37743      * @return {Roo.ContentPanel} The panel that was removed
37744      */
37745     remove : function(panel, preservePanel){
37746         panel = this.getPanel(panel);
37747         if(!panel){
37748             return null;
37749         }
37750         var e = {};
37751         this.fireEvent("beforeremove", this, panel, e);
37752         if(e.cancel === true){
37753             return null;
37754         }
37755         var panelId = panel.getId();
37756         this.panels.removeKey(panelId);
37757         return panel;
37758     },
37759     
37760     /**
37761      * Returns the panel specified or null if it's not in this region.
37762      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
37763      * @return {Roo.ContentPanel}
37764      */
37765     getPanel : function(id){
37766         if(typeof id == "object"){ // must be panel obj
37767             return id;
37768         }
37769         return this.panels.get(id);
37770     },
37771     
37772     /**
37773      * Returns this regions position (north/south/east/west/center).
37774      * @return {String} 
37775      */
37776     getPosition: function(){
37777         return this.position;    
37778     }
37779 });/*
37780  * Based on:
37781  * Ext JS Library 1.1.1
37782  * Copyright(c) 2006-2007, Ext JS, LLC.
37783  *
37784  * Originally Released Under LGPL - original licence link has changed is not relivant.
37785  *
37786  * Fork - LGPL
37787  * <script type="text/javascript">
37788  */
37789  
37790 /**
37791  * @class Roo.bootstrap.layout.Region
37792  * @extends Roo.bootstrap.layout.Basic
37793  * This class represents a region in a layout manager.
37794  
37795  * @cfg {Object}    margins         Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
37796  * @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})
37797  * @cfg {String}    tabPosition     (top|bottom) "top" or "bottom" (defaults to "bottom")
37798  * @cfg {Boolean}   alwaysShowTabs  True to always display tabs even when there is only 1 panel (defaults to false)
37799  * @cfg {Boolean}   autoScroll      True to enable overflow scrolling (defaults to false)
37800  * @cfg {Boolean}   titlebar        True to display a title bar (defaults to true)
37801  * @cfg {String}    title           The title for the region (overrides panel titles)
37802  * @cfg {Boolean}   animate         True to animate expand/collapse (defaults to false)
37803  * @cfg {Boolean}   autoHide        False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
37804  * @cfg {Boolean}   preservePanels  True to preserve removed panels so they can be readded later (defaults to false)
37805  * @cfg {Boolean}   closeOnTab      True to place the close icon on the tabs instead of the region titlebar (defaults to false)
37806  * @cfg {Boolean}   hideTabs        True to hide the tab strip (defaults to false)
37807  * @cfg {Boolean}   resizeTabs      True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
37808  *                      the space available, similar to FireFox 1.5 tabs (defaults to false)
37809  * @cfg {Number}    minTabWidth     The minimum tab width (defaults to 40)
37810  * @cfg {Number}    preferredTabWidth The preferred tab width (defaults to 150)
37811  * @cfg {String}    overflow       (hidden|visible) if you have menus in the region, then you need to set this to visible.
37812
37813  * @cfg {Boolean}   hidden          True to start the region hidden (defaults to false)
37814  * @cfg {Boolean}   hideWhenEmpty   True to hide the region when it has no panels
37815  * @cfg {Boolean}   disableTabTips  True to disable tab tooltips
37816  * @cfg {Number}    width           For East/West panels
37817  * @cfg {Number}    height          For North/South panels
37818  * @cfg {Boolean}   split           To show the splitter
37819  * @cfg {Boolean}   toolbar         xtype configuration for a toolbar - shows on right of tabbar
37820  * 
37821  * @cfg {string}   cls             Extra CSS classes to add to region
37822  * 
37823  * @cfg {Roo.bootstrap.layout.Manager}   mgr The manager
37824  * @cfg {string}   region  the region that it inhabits..
37825  *
37826
37827  * @xxxcfg {Boolean}   collapsible     DISABLED False to disable collapsing (defaults to true)
37828  * @xxxcfg {Boolean}   collapsed       DISABLED True to set the initial display to collapsed (defaults to false)
37829
37830  * @xxxcfg {String}    collapsedTitle  DISABLED Optional string message to display in the collapsed block of a north or south region
37831  * @xxxxcfg {Boolean}   floatable       DISABLED False to disable floating (defaults to true)
37832  * @xxxxcfg {Boolean}   showPin         True to show a pin button NOT SUPPORTED YET
37833  */
37834 Roo.bootstrap.layout.Region = function(config)
37835 {
37836     this.applyConfig(config);
37837
37838     var mgr = config.mgr;
37839     var pos = config.region;
37840     config.skipConfig = true;
37841     Roo.bootstrap.layout.Region.superclass.constructor.call(this, config);
37842     
37843     if (mgr.el) {
37844         this.onRender(mgr.el);   
37845     }
37846      
37847     this.visible = true;
37848     this.collapsed = false;
37849     this.unrendered_panels = [];
37850 };
37851
37852 Roo.extend(Roo.bootstrap.layout.Region, Roo.bootstrap.layout.Basic, {
37853
37854     position: '', // set by wrapper (eg. north/south etc..)
37855     unrendered_panels : null,  // unrendered panels.
37856     
37857     tabPosition : false,
37858     
37859     mgr: false, // points to 'Border'
37860     
37861     
37862     createBody : function(){
37863         /** This region's body element 
37864         * @type Roo.Element */
37865         this.bodyEl = this.el.createChild({
37866                 tag: "div",
37867                 cls: "roo-layout-panel-body tab-content" // bootstrap added...
37868         });
37869     },
37870
37871     onRender: function(ctr, pos)
37872     {
37873         var dh = Roo.DomHelper;
37874         /** This region's container element 
37875         * @type Roo.Element */
37876         this.el = dh.append(ctr.dom, {
37877                 tag: "div",
37878                 cls: (this.config.cls || '') + " roo-layout-region roo-layout-panel roo-layout-panel-" + this.position
37879             }, true);
37880         /** This region's title element 
37881         * @type Roo.Element */
37882     
37883         this.titleEl = dh.append(this.el.dom,  {
37884                 tag: "div",
37885                 unselectable: "on",
37886                 cls: "roo-unselectable roo-layout-panel-hd breadcrumb roo-layout-title-" + this.position,
37887                 children:[
37888                     {tag: "span", cls: "roo-unselectable roo-layout-panel-hd-text", unselectable: "on", html: "&#160;"},
37889                     {tag: "div", cls: "roo-unselectable roo-layout-panel-hd-tools", unselectable: "on"}
37890                 ]
37891             }, true);
37892         
37893         this.titleEl.enableDisplayMode();
37894         /** This region's title text element 
37895         * @type HTMLElement */
37896         this.titleTextEl = this.titleEl.dom.firstChild;
37897         this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
37898         /*
37899         this.closeBtn = this.createTool(this.tools.dom, "roo-layout-close");
37900         this.closeBtn.enableDisplayMode();
37901         this.closeBtn.on("click", this.closeClicked, this);
37902         this.closeBtn.hide();
37903     */
37904         this.createBody(this.config);
37905         if(this.config.hideWhenEmpty){
37906             this.hide();
37907             this.on("paneladded", this.validateVisibility, this);
37908             this.on("panelremoved", this.validateVisibility, this);
37909         }
37910         if(this.autoScroll){
37911             this.bodyEl.setStyle("overflow", "auto");
37912         }else{
37913             this.bodyEl.setStyle("overflow", this.config.overflow || 'hidden');
37914         }
37915         //if(c.titlebar !== false){
37916             if((!this.config.titlebar && !this.config.title) || this.config.titlebar === false){
37917                 this.titleEl.hide();
37918             }else{
37919                 this.titleEl.show();
37920                 if(this.config.title){
37921                     this.titleTextEl.innerHTML = this.config.title;
37922                 }
37923             }
37924         //}
37925         if(this.config.collapsed){
37926             this.collapse(true);
37927         }
37928         if(this.config.hidden){
37929             this.hide();
37930         }
37931         
37932         if (this.unrendered_panels && this.unrendered_panels.length) {
37933             for (var i =0;i< this.unrendered_panels.length; i++) {
37934                 this.add(this.unrendered_panels[i]);
37935             }
37936             this.unrendered_panels = null;
37937             
37938         }
37939         
37940     },
37941     
37942     applyConfig : function(c)
37943     {
37944         /*
37945          *if(c.collapsible && this.position != "center" && !this.collapsedEl){
37946             var dh = Roo.DomHelper;
37947             if(c.titlebar !== false){
37948                 this.collapseBtn = this.createTool(this.tools.dom, "roo-layout-collapse-"+this.position);
37949                 this.collapseBtn.on("click", this.collapse, this);
37950                 this.collapseBtn.enableDisplayMode();
37951                 /*
37952                 if(c.showPin === true || this.showPin){
37953                     this.stickBtn = this.createTool(this.tools.dom, "roo-layout-stick");
37954                     this.stickBtn.enableDisplayMode();
37955                     this.stickBtn.on("click", this.expand, this);
37956                     this.stickBtn.hide();
37957                 }
37958                 
37959             }
37960             */
37961             /** This region's collapsed element
37962             * @type Roo.Element */
37963             /*
37964              *
37965             this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
37966                 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
37967             ]}, true);
37968             
37969             if(c.floatable !== false){
37970                this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
37971                this.collapsedEl.on("click", this.collapseClick, this);
37972             }
37973
37974             if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
37975                 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
37976                    id: "message", unselectable: "on", style:{"float":"left"}});
37977                this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
37978              }
37979             this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
37980             this.expandBtn.on("click", this.expand, this);
37981             
37982         }
37983         
37984         if(this.collapseBtn){
37985             this.collapseBtn.setVisible(c.collapsible == true);
37986         }
37987         
37988         this.cmargins = c.cmargins || this.cmargins ||
37989                          (this.position == "west" || this.position == "east" ?
37990                              {top: 0, left: 2, right:2, bottom: 0} :
37991                              {top: 2, left: 0, right:0, bottom: 2});
37992         */
37993         this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
37994         
37995         
37996         this.tabPosition = [ 'top','bottom', 'west'].indexOf(c.tabPosition) > -1 ? c.tabPosition : "top";
37997         
37998         this.autoScroll = c.autoScroll || false;
37999         
38000         
38001        
38002         
38003         this.duration = c.duration || .30;
38004         this.slideDuration = c.slideDuration || .45;
38005         this.config = c;
38006        
38007     },
38008     /**
38009      * Returns true if this region is currently visible.
38010      * @return {Boolean}
38011      */
38012     isVisible : function(){
38013         return this.visible;
38014     },
38015
38016     /**
38017      * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
38018      * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&amp;#160;")
38019      */
38020     //setCollapsedTitle : function(title){
38021     //    title = title || "&#160;";
38022      //   if(this.collapsedTitleTextEl){
38023       //      this.collapsedTitleTextEl.innerHTML = title;
38024        // }
38025     //},
38026
38027     getBox : function(){
38028         var b;
38029       //  if(!this.collapsed){
38030             b = this.el.getBox(false, true);
38031        // }else{
38032           //  b = this.collapsedEl.getBox(false, true);
38033         //}
38034         return b;
38035     },
38036
38037     getMargins : function(){
38038         return this.margins;
38039         //return this.collapsed ? this.cmargins : this.margins;
38040     },
38041 /*
38042     highlight : function(){
38043         this.el.addClass("x-layout-panel-dragover");
38044     },
38045
38046     unhighlight : function(){
38047         this.el.removeClass("x-layout-panel-dragover");
38048     },
38049 */
38050     updateBox : function(box)
38051     {
38052         if (!this.bodyEl) {
38053             return; // not rendered yet..
38054         }
38055         
38056         this.box = box;
38057         if(!this.collapsed){
38058             this.el.dom.style.left = box.x + "px";
38059             this.el.dom.style.top = box.y + "px";
38060             this.updateBody(box.width, box.height);
38061         }else{
38062             this.collapsedEl.dom.style.left = box.x + "px";
38063             this.collapsedEl.dom.style.top = box.y + "px";
38064             this.collapsedEl.setSize(box.width, box.height);
38065         }
38066         if(this.tabs){
38067             this.tabs.autoSizeTabs();
38068         }
38069     },
38070
38071     updateBody : function(w, h)
38072     {
38073         if(w !== null){
38074             this.el.setWidth(w);
38075             w -= this.el.getBorderWidth("rl");
38076             if(this.config.adjustments){
38077                 w += this.config.adjustments[0];
38078             }
38079         }
38080         if(h !== null && h > 0){
38081             this.el.setHeight(h);
38082             h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
38083             h -= this.el.getBorderWidth("tb");
38084             if(this.config.adjustments){
38085                 h += this.config.adjustments[1];
38086             }
38087             this.bodyEl.setHeight(h);
38088             if(this.tabs){
38089                 h = this.tabs.syncHeight(h);
38090             }
38091         }
38092         if(this.panelSize){
38093             w = w !== null ? w : this.panelSize.width;
38094             h = h !== null ? h : this.panelSize.height;
38095         }
38096         if(this.activePanel){
38097             var el = this.activePanel.getEl();
38098             w = w !== null ? w : el.getWidth();
38099             h = h !== null ? h : el.getHeight();
38100             this.panelSize = {width: w, height: h};
38101             this.activePanel.setSize(w, h);
38102         }
38103         if(Roo.isIE && this.tabs){
38104             this.tabs.el.repaint();
38105         }
38106     },
38107
38108     /**
38109      * Returns the container element for this region.
38110      * @return {Roo.Element}
38111      */
38112     getEl : function(){
38113         return this.el;
38114     },
38115
38116     /**
38117      * Hides this region.
38118      */
38119     hide : function(){
38120         //if(!this.collapsed){
38121             this.el.dom.style.left = "-2000px";
38122             this.el.hide();
38123         //}else{
38124          //   this.collapsedEl.dom.style.left = "-2000px";
38125          //   this.collapsedEl.hide();
38126        // }
38127         this.visible = false;
38128         this.fireEvent("visibilitychange", this, false);
38129     },
38130
38131     /**
38132      * Shows this region if it was previously hidden.
38133      */
38134     show : function(){
38135         //if(!this.collapsed){
38136             this.el.show();
38137         //}else{
38138         //    this.collapsedEl.show();
38139        // }
38140         this.visible = true;
38141         this.fireEvent("visibilitychange", this, true);
38142     },
38143 /*
38144     closeClicked : function(){
38145         if(this.activePanel){
38146             this.remove(this.activePanel);
38147         }
38148     },
38149
38150     collapseClick : function(e){
38151         if(this.isSlid){
38152            e.stopPropagation();
38153            this.slideIn();
38154         }else{
38155            e.stopPropagation();
38156            this.slideOut();
38157         }
38158     },
38159 */
38160     /**
38161      * Collapses this region.
38162      * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
38163      */
38164     /*
38165     collapse : function(skipAnim, skipCheck = false){
38166         if(this.collapsed) {
38167             return;
38168         }
38169         
38170         if(skipCheck || this.fireEvent("beforecollapse", this) != false){
38171             
38172             this.collapsed = true;
38173             if(this.split){
38174                 this.split.el.hide();
38175             }
38176             if(this.config.animate && skipAnim !== true){
38177                 this.fireEvent("invalidated", this);
38178                 this.animateCollapse();
38179             }else{
38180                 this.el.setLocation(-20000,-20000);
38181                 this.el.hide();
38182                 this.collapsedEl.show();
38183                 this.fireEvent("collapsed", this);
38184                 this.fireEvent("invalidated", this);
38185             }
38186         }
38187         
38188     },
38189 */
38190     animateCollapse : function(){
38191         // overridden
38192     },
38193
38194     /**
38195      * Expands this region if it was previously collapsed.
38196      * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
38197      * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
38198      */
38199     /*
38200     expand : function(e, skipAnim){
38201         if(e) {
38202             e.stopPropagation();
38203         }
38204         if(!this.collapsed || this.el.hasActiveFx()) {
38205             return;
38206         }
38207         if(this.isSlid){
38208             this.afterSlideIn();
38209             skipAnim = true;
38210         }
38211         this.collapsed = false;
38212         if(this.config.animate && skipAnim !== true){
38213             this.animateExpand();
38214         }else{
38215             this.el.show();
38216             if(this.split){
38217                 this.split.el.show();
38218             }
38219             this.collapsedEl.setLocation(-2000,-2000);
38220             this.collapsedEl.hide();
38221             this.fireEvent("invalidated", this);
38222             this.fireEvent("expanded", this);
38223         }
38224     },
38225 */
38226     animateExpand : function(){
38227         // overridden
38228     },
38229
38230     initTabs : function()
38231     {
38232         //this.bodyEl.setStyle("overflow", "hidden"); -- this is set in render?
38233         
38234         var ts = new Roo.bootstrap.panel.Tabs({
38235             el: this.bodyEl.dom,
38236             region : this,
38237             tabPosition: this.tabPosition ? this.tabPosition  : 'top',
38238             disableTooltips: this.config.disableTabTips,
38239             toolbar : this.config.toolbar
38240         });
38241         
38242         if(this.config.hideTabs){
38243             ts.stripWrap.setDisplayed(false);
38244         }
38245         this.tabs = ts;
38246         ts.resizeTabs = this.config.resizeTabs === true;
38247         ts.minTabWidth = this.config.minTabWidth || 40;
38248         ts.maxTabWidth = this.config.maxTabWidth || 250;
38249         ts.preferredTabWidth = this.config.preferredTabWidth || 150;
38250         ts.monitorResize = false;
38251         //ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden"); // this is set in render?
38252         ts.bodyEl.addClass('roo-layout-tabs-body');
38253         this.panels.each(this.initPanelAsTab, this);
38254     },
38255
38256     initPanelAsTab : function(panel){
38257         var ti = this.tabs.addTab(
38258             panel.getEl().id,
38259             panel.getTitle(),
38260             null,
38261             this.config.closeOnTab && panel.isClosable(),
38262             panel.tpl
38263         );
38264         if(panel.tabTip !== undefined){
38265             ti.setTooltip(panel.tabTip);
38266         }
38267         ti.on("activate", function(){
38268               this.setActivePanel(panel);
38269         }, this);
38270         
38271         if(this.config.closeOnTab){
38272             ti.on("beforeclose", function(t, e){
38273                 e.cancel = true;
38274                 this.remove(panel);
38275             }, this);
38276         }
38277         
38278         panel.tabItem = ti;
38279         
38280         return ti;
38281     },
38282
38283     updatePanelTitle : function(panel, title)
38284     {
38285         if(this.activePanel == panel){
38286             this.updateTitle(title);
38287         }
38288         if(this.tabs){
38289             var ti = this.tabs.getTab(panel.getEl().id);
38290             ti.setText(title);
38291             if(panel.tabTip !== undefined){
38292                 ti.setTooltip(panel.tabTip);
38293             }
38294         }
38295     },
38296
38297     updateTitle : function(title){
38298         if(this.titleTextEl && !this.config.title){
38299             this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : "&#160;");
38300         }
38301     },
38302
38303     setActivePanel : function(panel)
38304     {
38305         panel = this.getPanel(panel);
38306         if(this.activePanel && this.activePanel != panel){
38307             if(this.activePanel.setActiveState(false) === false){
38308                 return;
38309             }
38310         }
38311         this.activePanel = panel;
38312         panel.setActiveState(true);
38313         if(this.panelSize){
38314             panel.setSize(this.panelSize.width, this.panelSize.height);
38315         }
38316         if(this.closeBtn){
38317             this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
38318         }
38319         this.updateTitle(panel.getTitle());
38320         if(this.tabs){
38321             this.fireEvent("invalidated", this);
38322         }
38323         this.fireEvent("panelactivated", this, panel);
38324     },
38325
38326     /**
38327      * Shows the specified panel.
38328      * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
38329      * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
38330      */
38331     showPanel : function(panel)
38332     {
38333         panel = this.getPanel(panel);
38334         if(panel){
38335             if(this.tabs){
38336                 var tab = this.tabs.getTab(panel.getEl().id);
38337                 if(tab.isHidden()){
38338                     this.tabs.unhideTab(tab.id);
38339                 }
38340                 tab.activate();
38341             }else{
38342                 this.setActivePanel(panel);
38343             }
38344         }
38345         return panel;
38346     },
38347
38348     /**
38349      * Get the active panel for this region.
38350      * @return {Roo.ContentPanel} The active panel or null
38351      */
38352     getActivePanel : function(){
38353         return this.activePanel;
38354     },
38355
38356     validateVisibility : function(){
38357         if(this.panels.getCount() < 1){
38358             this.updateTitle("&#160;");
38359             this.closeBtn.hide();
38360             this.hide();
38361         }else{
38362             if(!this.isVisible()){
38363                 this.show();
38364             }
38365         }
38366     },
38367
38368     /**
38369      * Adds the passed ContentPanel(s) to this region.
38370      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
38371      * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
38372      */
38373     add : function(panel)
38374     {
38375         if(arguments.length > 1){
38376             for(var i = 0, len = arguments.length; i < len; i++) {
38377                 this.add(arguments[i]);
38378             }
38379             return null;
38380         }
38381         
38382         // if we have not been rendered yet, then we can not really do much of this..
38383         if (!this.bodyEl) {
38384             this.unrendered_panels.push(panel);
38385             return panel;
38386         }
38387         
38388         
38389         
38390         
38391         if(this.hasPanel(panel)){
38392             this.showPanel(panel);
38393             return panel;
38394         }
38395         panel.setRegion(this);
38396         this.panels.add(panel);
38397        /* if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
38398             // sinle panel - no tab...?? would it not be better to render it with the tabs,
38399             // and hide them... ???
38400             this.bodyEl.dom.appendChild(panel.getEl().dom);
38401             if(panel.background !== true){
38402                 this.setActivePanel(panel);
38403             }
38404             this.fireEvent("paneladded", this, panel);
38405             return panel;
38406         }
38407         */
38408         if(!this.tabs){
38409             this.initTabs();
38410         }else{
38411             this.initPanelAsTab(panel);
38412         }
38413         
38414         
38415         if(panel.background !== true){
38416             this.tabs.activate(panel.getEl().id);
38417         }
38418         this.fireEvent("paneladded", this, panel);
38419         return panel;
38420     },
38421
38422     /**
38423      * Hides the tab for the specified panel.
38424      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
38425      */
38426     hidePanel : function(panel){
38427         if(this.tabs && (panel = this.getPanel(panel))){
38428             this.tabs.hideTab(panel.getEl().id);
38429         }
38430     },
38431
38432     /**
38433      * Unhides the tab for a previously hidden panel.
38434      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
38435      */
38436     unhidePanel : function(panel){
38437         if(this.tabs && (panel = this.getPanel(panel))){
38438             this.tabs.unhideTab(panel.getEl().id);
38439         }
38440     },
38441
38442     clearPanels : function(){
38443         while(this.panels.getCount() > 0){
38444              this.remove(this.panels.first());
38445         }
38446     },
38447
38448     /**
38449      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
38450      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
38451      * @param {Boolean} preservePanel Overrides the config preservePanel option
38452      * @return {Roo.ContentPanel} The panel that was removed
38453      */
38454     remove : function(panel, preservePanel)
38455     {
38456         panel = this.getPanel(panel);
38457         if(!panel){
38458             return null;
38459         }
38460         var e = {};
38461         this.fireEvent("beforeremove", this, panel, e);
38462         if(e.cancel === true){
38463             return null;
38464         }
38465         preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
38466         var panelId = panel.getId();
38467         this.panels.removeKey(panelId);
38468         if(preservePanel){
38469             document.body.appendChild(panel.getEl().dom);
38470         }
38471         if(this.tabs){
38472             this.tabs.removeTab(panel.getEl().id);
38473         }else if (!preservePanel){
38474             this.bodyEl.dom.removeChild(panel.getEl().dom);
38475         }
38476         if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
38477             var p = this.panels.first();
38478             var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
38479             tempEl.appendChild(p.getEl().dom);
38480             this.bodyEl.update("");
38481             this.bodyEl.dom.appendChild(p.getEl().dom);
38482             tempEl = null;
38483             this.updateTitle(p.getTitle());
38484             this.tabs = null;
38485             this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
38486             this.setActivePanel(p);
38487         }
38488         panel.setRegion(null);
38489         if(this.activePanel == panel){
38490             this.activePanel = null;
38491         }
38492         if(this.config.autoDestroy !== false && preservePanel !== true){
38493             try{panel.destroy();}catch(e){}
38494         }
38495         this.fireEvent("panelremoved", this, panel);
38496         return panel;
38497     },
38498
38499     /**
38500      * Returns the TabPanel component used by this region
38501      * @return {Roo.TabPanel}
38502      */
38503     getTabs : function(){
38504         return this.tabs;
38505     },
38506
38507     createTool : function(parentEl, className){
38508         var btn = Roo.DomHelper.append(parentEl, {
38509             tag: "div",
38510             cls: "x-layout-tools-button",
38511             children: [ {
38512                 tag: "div",
38513                 cls: "roo-layout-tools-button-inner " + className,
38514                 html: "&#160;"
38515             }]
38516         }, true);
38517         btn.addClassOnOver("roo-layout-tools-button-over");
38518         return btn;
38519     }
38520 });/*
38521  * Based on:
38522  * Ext JS Library 1.1.1
38523  * Copyright(c) 2006-2007, Ext JS, LLC.
38524  *
38525  * Originally Released Under LGPL - original licence link has changed is not relivant.
38526  *
38527  * Fork - LGPL
38528  * <script type="text/javascript">
38529  */
38530  
38531
38532
38533 /**
38534  * @class Roo.SplitLayoutRegion
38535  * @extends Roo.LayoutRegion
38536  * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
38537  */
38538 Roo.bootstrap.layout.Split = function(config){
38539     this.cursor = config.cursor;
38540     Roo.bootstrap.layout.Split.superclass.constructor.call(this, config);
38541 };
38542
38543 Roo.extend(Roo.bootstrap.layout.Split, Roo.bootstrap.layout.Region,
38544 {
38545     splitTip : "Drag to resize.",
38546     collapsibleSplitTip : "Drag to resize. Double click to hide.",
38547     useSplitTips : false,
38548
38549     applyConfig : function(config){
38550         Roo.bootstrap.layout.Split.superclass.applyConfig.call(this, config);
38551     },
38552     
38553     onRender : function(ctr,pos) {
38554         
38555         Roo.bootstrap.layout.Split.superclass.onRender.call(this, ctr,pos);
38556         if(!this.config.split){
38557             return;
38558         }
38559         if(!this.split){
38560             
38561             var splitEl = Roo.DomHelper.append(ctr.dom,  {
38562                             tag: "div",
38563                             id: this.el.id + "-split",
38564                             cls: "roo-layout-split roo-layout-split-"+this.position,
38565                             html: "&#160;"
38566             });
38567             /** The SplitBar for this region 
38568             * @type Roo.SplitBar */
38569             // does not exist yet...
38570             Roo.log([this.position, this.orientation]);
38571             
38572             this.split = new Roo.bootstrap.SplitBar({
38573                 dragElement : splitEl,
38574                 resizingElement: this.el,
38575                 orientation : this.orientation
38576             });
38577             
38578             this.split.on("moved", this.onSplitMove, this);
38579             this.split.useShim = this.config.useShim === true;
38580             this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
38581             if(this.useSplitTips){
38582                 this.split.el.dom.title = this.config.collapsible ? this.collapsibleSplitTip : this.splitTip;
38583             }
38584             //if(config.collapsible){
38585             //    this.split.el.on("dblclick", this.collapse,  this);
38586             //}
38587         }
38588         if(typeof this.config.minSize != "undefined"){
38589             this.split.minSize = this.config.minSize;
38590         }
38591         if(typeof this.config.maxSize != "undefined"){
38592             this.split.maxSize = this.config.maxSize;
38593         }
38594         if(this.config.hideWhenEmpty || this.config.hidden || this.config.collapsed){
38595             this.hideSplitter();
38596         }
38597         
38598     },
38599
38600     getHMaxSize : function(){
38601          var cmax = this.config.maxSize || 10000;
38602          var center = this.mgr.getRegion("center");
38603          return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
38604     },
38605
38606     getVMaxSize : function(){
38607          var cmax = this.config.maxSize || 10000;
38608          var center = this.mgr.getRegion("center");
38609          return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
38610     },
38611
38612     onSplitMove : function(split, newSize){
38613         this.fireEvent("resized", this, newSize);
38614     },
38615     
38616     /** 
38617      * Returns the {@link Roo.SplitBar} for this region.
38618      * @return {Roo.SplitBar}
38619      */
38620     getSplitBar : function(){
38621         return this.split;
38622     },
38623     
38624     hide : function(){
38625         this.hideSplitter();
38626         Roo.bootstrap.layout.Split.superclass.hide.call(this);
38627     },
38628
38629     hideSplitter : function(){
38630         if(this.split){
38631             this.split.el.setLocation(-2000,-2000);
38632             this.split.el.hide();
38633         }
38634     },
38635
38636     show : function(){
38637         if(this.split){
38638             this.split.el.show();
38639         }
38640         Roo.bootstrap.layout.Split.superclass.show.call(this);
38641     },
38642     
38643     beforeSlide: function(){
38644         if(Roo.isGecko){// firefox overflow auto bug workaround
38645             this.bodyEl.clip();
38646             if(this.tabs) {
38647                 this.tabs.bodyEl.clip();
38648             }
38649             if(this.activePanel){
38650                 this.activePanel.getEl().clip();
38651                 
38652                 if(this.activePanel.beforeSlide){
38653                     this.activePanel.beforeSlide();
38654                 }
38655             }
38656         }
38657     },
38658     
38659     afterSlide : function(){
38660         if(Roo.isGecko){// firefox overflow auto bug workaround
38661             this.bodyEl.unclip();
38662             if(this.tabs) {
38663                 this.tabs.bodyEl.unclip();
38664             }
38665             if(this.activePanel){
38666                 this.activePanel.getEl().unclip();
38667                 if(this.activePanel.afterSlide){
38668                     this.activePanel.afterSlide();
38669                 }
38670             }
38671         }
38672     },
38673
38674     initAutoHide : function(){
38675         if(this.autoHide !== false){
38676             if(!this.autoHideHd){
38677                 var st = new Roo.util.DelayedTask(this.slideIn, this);
38678                 this.autoHideHd = {
38679                     "mouseout": function(e){
38680                         if(!e.within(this.el, true)){
38681                             st.delay(500);
38682                         }
38683                     },
38684                     "mouseover" : function(e){
38685                         st.cancel();
38686                     },
38687                     scope : this
38688                 };
38689             }
38690             this.el.on(this.autoHideHd);
38691         }
38692     },
38693
38694     clearAutoHide : function(){
38695         if(this.autoHide !== false){
38696             this.el.un("mouseout", this.autoHideHd.mouseout);
38697             this.el.un("mouseover", this.autoHideHd.mouseover);
38698         }
38699     },
38700
38701     clearMonitor : function(){
38702         Roo.get(document).un("click", this.slideInIf, this);
38703     },
38704
38705     // these names are backwards but not changed for compat
38706     slideOut : function(){
38707         if(this.isSlid || this.el.hasActiveFx()){
38708             return;
38709         }
38710         this.isSlid = true;
38711         if(this.collapseBtn){
38712             this.collapseBtn.hide();
38713         }
38714         this.closeBtnState = this.closeBtn.getStyle('display');
38715         this.closeBtn.hide();
38716         if(this.stickBtn){
38717             this.stickBtn.show();
38718         }
38719         this.el.show();
38720         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
38721         this.beforeSlide();
38722         this.el.setStyle("z-index", 10001);
38723         this.el.slideIn(this.getSlideAnchor(), {
38724             callback: function(){
38725                 this.afterSlide();
38726                 this.initAutoHide();
38727                 Roo.get(document).on("click", this.slideInIf, this);
38728                 this.fireEvent("slideshow", this);
38729             },
38730             scope: this,
38731             block: true
38732         });
38733     },
38734
38735     afterSlideIn : function(){
38736         this.clearAutoHide();
38737         this.isSlid = false;
38738         this.clearMonitor();
38739         this.el.setStyle("z-index", "");
38740         if(this.collapseBtn){
38741             this.collapseBtn.show();
38742         }
38743         this.closeBtn.setStyle('display', this.closeBtnState);
38744         if(this.stickBtn){
38745             this.stickBtn.hide();
38746         }
38747         this.fireEvent("slidehide", this);
38748     },
38749
38750     slideIn : function(cb){
38751         if(!this.isSlid || this.el.hasActiveFx()){
38752             Roo.callback(cb);
38753             return;
38754         }
38755         this.isSlid = false;
38756         this.beforeSlide();
38757         this.el.slideOut(this.getSlideAnchor(), {
38758             callback: function(){
38759                 this.el.setLeftTop(-10000, -10000);
38760                 this.afterSlide();
38761                 this.afterSlideIn();
38762                 Roo.callback(cb);
38763             },
38764             scope: this,
38765             block: true
38766         });
38767     },
38768     
38769     slideInIf : function(e){
38770         if(!e.within(this.el)){
38771             this.slideIn();
38772         }
38773     },
38774
38775     animateCollapse : function(){
38776         this.beforeSlide();
38777         this.el.setStyle("z-index", 20000);
38778         var anchor = this.getSlideAnchor();
38779         this.el.slideOut(anchor, {
38780             callback : function(){
38781                 this.el.setStyle("z-index", "");
38782                 this.collapsedEl.slideIn(anchor, {duration:.3});
38783                 this.afterSlide();
38784                 this.el.setLocation(-10000,-10000);
38785                 this.el.hide();
38786                 this.fireEvent("collapsed", this);
38787             },
38788             scope: this,
38789             block: true
38790         });
38791     },
38792
38793     animateExpand : function(){
38794         this.beforeSlide();
38795         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
38796         this.el.setStyle("z-index", 20000);
38797         this.collapsedEl.hide({
38798             duration:.1
38799         });
38800         this.el.slideIn(this.getSlideAnchor(), {
38801             callback : function(){
38802                 this.el.setStyle("z-index", "");
38803                 this.afterSlide();
38804                 if(this.split){
38805                     this.split.el.show();
38806                 }
38807                 this.fireEvent("invalidated", this);
38808                 this.fireEvent("expanded", this);
38809             },
38810             scope: this,
38811             block: true
38812         });
38813     },
38814
38815     anchors : {
38816         "west" : "left",
38817         "east" : "right",
38818         "north" : "top",
38819         "south" : "bottom"
38820     },
38821
38822     sanchors : {
38823         "west" : "l",
38824         "east" : "r",
38825         "north" : "t",
38826         "south" : "b"
38827     },
38828
38829     canchors : {
38830         "west" : "tl-tr",
38831         "east" : "tr-tl",
38832         "north" : "tl-bl",
38833         "south" : "bl-tl"
38834     },
38835
38836     getAnchor : function(){
38837         return this.anchors[this.position];
38838     },
38839
38840     getCollapseAnchor : function(){
38841         return this.canchors[this.position];
38842     },
38843
38844     getSlideAnchor : function(){
38845         return this.sanchors[this.position];
38846     },
38847
38848     getAlignAdj : function(){
38849         var cm = this.cmargins;
38850         switch(this.position){
38851             case "west":
38852                 return [0, 0];
38853             break;
38854             case "east":
38855                 return [0, 0];
38856             break;
38857             case "north":
38858                 return [0, 0];
38859             break;
38860             case "south":
38861                 return [0, 0];
38862             break;
38863         }
38864     },
38865
38866     getExpandAdj : function(){
38867         var c = this.collapsedEl, cm = this.cmargins;
38868         switch(this.position){
38869             case "west":
38870                 return [-(cm.right+c.getWidth()+cm.left), 0];
38871             break;
38872             case "east":
38873                 return [cm.right+c.getWidth()+cm.left, 0];
38874             break;
38875             case "north":
38876                 return [0, -(cm.top+cm.bottom+c.getHeight())];
38877             break;
38878             case "south":
38879                 return [0, cm.top+cm.bottom+c.getHeight()];
38880             break;
38881         }
38882     }
38883 });/*
38884  * Based on:
38885  * Ext JS Library 1.1.1
38886  * Copyright(c) 2006-2007, Ext JS, LLC.
38887  *
38888  * Originally Released Under LGPL - original licence link has changed is not relivant.
38889  *
38890  * Fork - LGPL
38891  * <script type="text/javascript">
38892  */
38893 /*
38894  * These classes are private internal classes
38895  */
38896 Roo.bootstrap.layout.Center = function(config){
38897     config.region = "center";
38898     Roo.bootstrap.layout.Region.call(this, config);
38899     this.visible = true;
38900     this.minWidth = config.minWidth || 20;
38901     this.minHeight = config.minHeight || 20;
38902 };
38903
38904 Roo.extend(Roo.bootstrap.layout.Center, Roo.bootstrap.layout.Region, {
38905     hide : function(){
38906         // center panel can't be hidden
38907     },
38908     
38909     show : function(){
38910         // center panel can't be hidden
38911     },
38912     
38913     getMinWidth: function(){
38914         return this.minWidth;
38915     },
38916     
38917     getMinHeight: function(){
38918         return this.minHeight;
38919     }
38920 });
38921
38922
38923
38924
38925  
38926
38927
38928
38929
38930
38931
38932 Roo.bootstrap.layout.North = function(config)
38933 {
38934     config.region = 'north';
38935     config.cursor = 'n-resize';
38936     
38937     Roo.bootstrap.layout.Split.call(this, config);
38938     
38939     
38940     if(this.split){
38941         this.split.placement = Roo.bootstrap.SplitBar.TOP;
38942         this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
38943         this.split.el.addClass("roo-layout-split-v");
38944     }
38945     var size = config.initialSize || config.height;
38946     if(typeof size != "undefined"){
38947         this.el.setHeight(size);
38948     }
38949 };
38950 Roo.extend(Roo.bootstrap.layout.North, Roo.bootstrap.layout.Split,
38951 {
38952     orientation: Roo.bootstrap.SplitBar.VERTICAL,
38953     
38954     
38955     
38956     getBox : function(){
38957         if(this.collapsed){
38958             return this.collapsedEl.getBox();
38959         }
38960         var box = this.el.getBox();
38961         if(this.split){
38962             box.height += this.split.el.getHeight();
38963         }
38964         return box;
38965     },
38966     
38967     updateBox : function(box){
38968         if(this.split && !this.collapsed){
38969             box.height -= this.split.el.getHeight();
38970             this.split.el.setLeft(box.x);
38971             this.split.el.setTop(box.y+box.height);
38972             this.split.el.setWidth(box.width);
38973         }
38974         if(this.collapsed){
38975             this.updateBody(box.width, null);
38976         }
38977         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
38978     }
38979 });
38980
38981
38982
38983
38984
38985 Roo.bootstrap.layout.South = function(config){
38986     config.region = 'south';
38987     config.cursor = 's-resize';
38988     Roo.bootstrap.layout.Split.call(this, config);
38989     if(this.split){
38990         this.split.placement = Roo.bootstrap.SplitBar.BOTTOM;
38991         this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
38992         this.split.el.addClass("roo-layout-split-v");
38993     }
38994     var size = config.initialSize || config.height;
38995     if(typeof size != "undefined"){
38996         this.el.setHeight(size);
38997     }
38998 };
38999
39000 Roo.extend(Roo.bootstrap.layout.South, Roo.bootstrap.layout.Split, {
39001     orientation: Roo.bootstrap.SplitBar.VERTICAL,
39002     getBox : function(){
39003         if(this.collapsed){
39004             return this.collapsedEl.getBox();
39005         }
39006         var box = this.el.getBox();
39007         if(this.split){
39008             var sh = this.split.el.getHeight();
39009             box.height += sh;
39010             box.y -= sh;
39011         }
39012         return box;
39013     },
39014     
39015     updateBox : function(box){
39016         if(this.split && !this.collapsed){
39017             var sh = this.split.el.getHeight();
39018             box.height -= sh;
39019             box.y += sh;
39020             this.split.el.setLeft(box.x);
39021             this.split.el.setTop(box.y-sh);
39022             this.split.el.setWidth(box.width);
39023         }
39024         if(this.collapsed){
39025             this.updateBody(box.width, null);
39026         }
39027         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
39028     }
39029 });
39030
39031 Roo.bootstrap.layout.East = function(config){
39032     config.region = "east";
39033     config.cursor = "e-resize";
39034     Roo.bootstrap.layout.Split.call(this, config);
39035     if(this.split){
39036         this.split.placement = Roo.bootstrap.SplitBar.RIGHT;
39037         this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
39038         this.split.el.addClass("roo-layout-split-h");
39039     }
39040     var size = config.initialSize || config.width;
39041     if(typeof size != "undefined"){
39042         this.el.setWidth(size);
39043     }
39044 };
39045 Roo.extend(Roo.bootstrap.layout.East, Roo.bootstrap.layout.Split, {
39046     orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
39047     getBox : function(){
39048         if(this.collapsed){
39049             return this.collapsedEl.getBox();
39050         }
39051         var box = this.el.getBox();
39052         if(this.split){
39053             var sw = this.split.el.getWidth();
39054             box.width += sw;
39055             box.x -= sw;
39056         }
39057         return box;
39058     },
39059
39060     updateBox : function(box){
39061         if(this.split && !this.collapsed){
39062             var sw = this.split.el.getWidth();
39063             box.width -= sw;
39064             this.split.el.setLeft(box.x);
39065             this.split.el.setTop(box.y);
39066             this.split.el.setHeight(box.height);
39067             box.x += sw;
39068         }
39069         if(this.collapsed){
39070             this.updateBody(null, box.height);
39071         }
39072         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
39073     }
39074 });
39075
39076 Roo.bootstrap.layout.West = function(config){
39077     config.region = "west";
39078     config.cursor = "w-resize";
39079     
39080     Roo.bootstrap.layout.Split.call(this, config);
39081     if(this.split){
39082         this.split.placement = Roo.bootstrap.SplitBar.LEFT;
39083         this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
39084         this.split.el.addClass("roo-layout-split-h");
39085     }
39086     
39087 };
39088 Roo.extend(Roo.bootstrap.layout.West, Roo.bootstrap.layout.Split, {
39089     orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
39090     
39091     onRender: function(ctr, pos)
39092     {
39093         Roo.bootstrap.layout.West.superclass.onRender.call(this, ctr,pos);
39094         var size = this.config.initialSize || this.config.width;
39095         if(typeof size != "undefined"){
39096             this.el.setWidth(size);
39097         }
39098     },
39099     
39100     getBox : function(){
39101         if(this.collapsed){
39102             return this.collapsedEl.getBox();
39103         }
39104         var box = this.el.getBox();
39105         if(this.split){
39106             box.width += this.split.el.getWidth();
39107         }
39108         return box;
39109     },
39110     
39111     updateBox : function(box){
39112         if(this.split && !this.collapsed){
39113             var sw = this.split.el.getWidth();
39114             box.width -= sw;
39115             this.split.el.setLeft(box.x+box.width);
39116             this.split.el.setTop(box.y);
39117             this.split.el.setHeight(box.height);
39118         }
39119         if(this.collapsed){
39120             this.updateBody(null, box.height);
39121         }
39122         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
39123     }
39124 });Roo.namespace("Roo.bootstrap.panel");/*
39125  * Based on:
39126  * Ext JS Library 1.1.1
39127  * Copyright(c) 2006-2007, Ext JS, LLC.
39128  *
39129  * Originally Released Under LGPL - original licence link has changed is not relivant.
39130  *
39131  * Fork - LGPL
39132  * <script type="text/javascript">
39133  */
39134 /**
39135  * @class Roo.ContentPanel
39136  * @extends Roo.util.Observable
39137  * A basic ContentPanel element.
39138  * @cfg {Boolean}   fitToFrame    True for this panel to adjust its size to fit when the region resizes  (defaults to false)
39139  * @cfg {Boolean}   fitContainer   When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container  (defaults to false)
39140  * @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
39141  * @cfg {Boolean}   closable      True if the panel can be closed/removed
39142  * @cfg {Boolean}   background    True if the panel should not be activated when it is added (defaults to false)
39143  * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
39144  * @cfg {Toolbar}   toolbar       A toolbar for this panel
39145  * @cfg {Boolean} autoScroll    True to scroll overflow in this panel (use with {@link #fitToFrame})
39146  * @cfg {String} title          The title for this panel
39147  * @cfg {Array} adjustments     Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
39148  * @cfg {String} url            Calls {@link #setUrl} with this value
39149  * @cfg {String} region         (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
39150  * @cfg {String/Object} params  When used with {@link #url}, calls {@link #setUrl} with this value
39151  * @cfg {Boolean} loadOnce      When used with {@link #url}, calls {@link #setUrl} with this value
39152  * @cfg {String}    content        Raw content to fill content panel with (uses setContent on construction.)
39153  * @cfg {Boolean} badges render the badges
39154
39155  * @constructor
39156  * Create a new ContentPanel.
39157  * @param {String/HTMLElement/Roo.Element} el The container element for this panel
39158  * @param {String/Object} config A string to set only the title or a config object
39159  * @param {String} content (optional) Set the HTML content for this panel
39160  * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
39161  */
39162 Roo.bootstrap.panel.Content = function( config){
39163     
39164     this.tpl = config.tpl || false;
39165     
39166     var el = config.el;
39167     var content = config.content;
39168
39169     if(config.autoCreate){ // xtype is available if this is called from factory
39170         el = Roo.id();
39171     }
39172     this.el = Roo.get(el);
39173     if(!this.el && config && config.autoCreate){
39174         if(typeof config.autoCreate == "object"){
39175             if(!config.autoCreate.id){
39176                 config.autoCreate.id = config.id||el;
39177             }
39178             this.el = Roo.DomHelper.append(document.body,
39179                         config.autoCreate, true);
39180         }else{
39181             var elcfg =  {   tag: "div",
39182                             cls: "roo-layout-inactive-content",
39183                             id: config.id||el
39184                             };
39185             if (config.html) {
39186                 elcfg.html = config.html;
39187                 
39188             }
39189                         
39190             this.el = Roo.DomHelper.append(document.body, elcfg , true);
39191         }
39192     } 
39193     this.closable = false;
39194     this.loaded = false;
39195     this.active = false;
39196    
39197       
39198     if (config.toolbar && !config.toolbar.el && config.toolbar.xtype) {
39199         
39200         this.toolbar = new config.toolbar.xns[config.toolbar.xtype](config.toolbar);
39201         
39202         this.wrapEl = this.el; //this.el.wrap();
39203         var ti = [];
39204         if (config.toolbar.items) {
39205             ti = config.toolbar.items ;
39206             delete config.toolbar.items ;
39207         }
39208         
39209         var nitems = [];
39210         this.toolbar.render(this.wrapEl, 'before');
39211         for(var i =0;i < ti.length;i++) {
39212           //  Roo.log(['add child', items[i]]);
39213             nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
39214         }
39215         this.toolbar.items = nitems;
39216         this.toolbar.el.insertBefore(this.wrapEl.dom.firstChild);
39217         delete config.toolbar;
39218         
39219     }
39220     /*
39221     // xtype created footer. - not sure if will work as we normally have to render first..
39222     if (this.footer && !this.footer.el && this.footer.xtype) {
39223         if (!this.wrapEl) {
39224             this.wrapEl = this.el.wrap();
39225         }
39226     
39227         this.footer.container = this.wrapEl.createChild();
39228          
39229         this.footer = Roo.factory(this.footer, Roo);
39230         
39231     }
39232     */
39233     
39234      if(typeof config == "string"){
39235         this.title = config;
39236     }else{
39237         Roo.apply(this, config);
39238     }
39239     
39240     if(this.resizeEl){
39241         this.resizeEl = Roo.get(this.resizeEl, true);
39242     }else{
39243         this.resizeEl = this.el;
39244     }
39245     // handle view.xtype
39246     
39247  
39248     
39249     
39250     this.addEvents({
39251         /**
39252          * @event activate
39253          * Fires when this panel is activated. 
39254          * @param {Roo.ContentPanel} this
39255          */
39256         "activate" : true,
39257         /**
39258          * @event deactivate
39259          * Fires when this panel is activated. 
39260          * @param {Roo.ContentPanel} this
39261          */
39262         "deactivate" : true,
39263
39264         /**
39265          * @event resize
39266          * Fires when this panel is resized if fitToFrame is true.
39267          * @param {Roo.ContentPanel} this
39268          * @param {Number} width The width after any component adjustments
39269          * @param {Number} height The height after any component adjustments
39270          */
39271         "resize" : true,
39272         
39273          /**
39274          * @event render
39275          * Fires when this tab is created
39276          * @param {Roo.ContentPanel} this
39277          */
39278         "render" : true
39279         
39280         
39281         
39282     });
39283     
39284
39285     
39286     
39287     if(this.autoScroll){
39288         this.resizeEl.setStyle("overflow", "auto");
39289     } else {
39290         // fix randome scrolling
39291         //this.el.on('scroll', function() {
39292         //    Roo.log('fix random scolling');
39293         //    this.scrollTo('top',0); 
39294         //});
39295     }
39296     content = content || this.content;
39297     if(content){
39298         this.setContent(content);
39299     }
39300     if(config && config.url){
39301         this.setUrl(this.url, this.params, this.loadOnce);
39302     }
39303     
39304     
39305     
39306     Roo.bootstrap.panel.Content.superclass.constructor.call(this);
39307     
39308     if (this.view && typeof(this.view.xtype) != 'undefined') {
39309         this.view.el = this.el.appendChild(document.createElement("div"));
39310         this.view = Roo.factory(this.view); 
39311         this.view.render  &&  this.view.render(false, '');  
39312     }
39313     
39314     
39315     this.fireEvent('render', this);
39316 };
39317
39318 Roo.extend(Roo.bootstrap.panel.Content, Roo.bootstrap.Component, {
39319     
39320     tabTip : '',
39321     
39322     setRegion : function(region){
39323         this.region = region;
39324         this.setActiveClass(region && !this.background);
39325     },
39326     
39327     
39328     setActiveClass: function(state)
39329     {
39330         if(state){
39331            this.el.replaceClass("roo-layout-inactive-content", "roo-layout-active-content");
39332            this.el.setStyle('position','relative');
39333         }else{
39334            this.el.replaceClass("roo-layout-active-content", "roo-layout-inactive-content");
39335            this.el.setStyle('position', 'absolute');
39336         } 
39337     },
39338     
39339     /**
39340      * Returns the toolbar for this Panel if one was configured. 
39341      * @return {Roo.Toolbar} 
39342      */
39343     getToolbar : function(){
39344         return this.toolbar;
39345     },
39346     
39347     setActiveState : function(active)
39348     {
39349         this.active = active;
39350         this.setActiveClass(active);
39351         if(!active){
39352             if(this.fireEvent("deactivate", this) === false){
39353                 return false;
39354             }
39355             return true;
39356         }
39357         this.fireEvent("activate", this);
39358         return true;
39359     },
39360     /**
39361      * Updates this panel's element
39362      * @param {String} content The new content
39363      * @param {Boolean} loadScripts (optional) true to look for and process scripts
39364     */
39365     setContent : function(content, loadScripts){
39366         this.el.update(content, loadScripts);
39367     },
39368
39369     ignoreResize : function(w, h){
39370         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
39371             return true;
39372         }else{
39373             this.lastSize = {width: w, height: h};
39374             return false;
39375         }
39376     },
39377     /**
39378      * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
39379      * @return {Roo.UpdateManager} The UpdateManager
39380      */
39381     getUpdateManager : function(){
39382         return this.el.getUpdateManager();
39383     },
39384      /**
39385      * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
39386      * @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:
39387 <pre><code>
39388 panel.load({
39389     url: "your-url.php",
39390     params: {param1: "foo", param2: "bar"}, // or a URL encoded string
39391     callback: yourFunction,
39392     scope: yourObject, //(optional scope)
39393     discardUrl: false,
39394     nocache: false,
39395     text: "Loading...",
39396     timeout: 30,
39397     scripts: false
39398 });
39399 </code></pre>
39400      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
39401      * 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.
39402      * @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}
39403      * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
39404      * @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.
39405      * @return {Roo.ContentPanel} this
39406      */
39407     load : function(){
39408         var um = this.el.getUpdateManager();
39409         um.update.apply(um, arguments);
39410         return this;
39411     },
39412
39413
39414     /**
39415      * 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.
39416      * @param {String/Function} url The URL to load the content from or a function to call to get the URL
39417      * @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)
39418      * @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)
39419      * @return {Roo.UpdateManager} The UpdateManager
39420      */
39421     setUrl : function(url, params, loadOnce){
39422         if(this.refreshDelegate){
39423             this.removeListener("activate", this.refreshDelegate);
39424         }
39425         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
39426         this.on("activate", this.refreshDelegate);
39427         return this.el.getUpdateManager();
39428     },
39429     
39430     _handleRefresh : function(url, params, loadOnce){
39431         if(!loadOnce || !this.loaded){
39432             var updater = this.el.getUpdateManager();
39433             updater.update(url, params, this._setLoaded.createDelegate(this));
39434         }
39435     },
39436     
39437     _setLoaded : function(){
39438         this.loaded = true;
39439     }, 
39440     
39441     /**
39442      * Returns this panel's id
39443      * @return {String} 
39444      */
39445     getId : function(){
39446         return this.el.id;
39447     },
39448     
39449     /** 
39450      * Returns this panel's element - used by regiosn to add.
39451      * @return {Roo.Element} 
39452      */
39453     getEl : function(){
39454         return this.wrapEl || this.el;
39455     },
39456     
39457    
39458     
39459     adjustForComponents : function(width, height)
39460     {
39461         //Roo.log('adjustForComponents ');
39462         if(this.resizeEl != this.el){
39463             width -= this.el.getFrameWidth('lr');
39464             height -= this.el.getFrameWidth('tb');
39465         }
39466         if(this.toolbar){
39467             var te = this.toolbar.getEl();
39468             te.setWidth(width);
39469             height -= te.getHeight();
39470         }
39471         if(this.footer){
39472             var te = this.footer.getEl();
39473             te.setWidth(width);
39474             height -= te.getHeight();
39475         }
39476         
39477         
39478         if(this.adjustments){
39479             width += this.adjustments[0];
39480             height += this.adjustments[1];
39481         }
39482         return {"width": width, "height": height};
39483     },
39484     
39485     setSize : function(width, height){
39486         if(this.fitToFrame && !this.ignoreResize(width, height)){
39487             if(this.fitContainer && this.resizeEl != this.el){
39488                 this.el.setSize(width, height);
39489             }
39490             var size = this.adjustForComponents(width, height);
39491             this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
39492             this.fireEvent('resize', this, size.width, size.height);
39493         }
39494     },
39495     
39496     /**
39497      * Returns this panel's title
39498      * @return {String} 
39499      */
39500     getTitle : function(){
39501         
39502         if (typeof(this.title) != 'object') {
39503             return this.title;
39504         }
39505         
39506         var t = '';
39507         for (var k in this.title) {
39508             if (!this.title.hasOwnProperty(k)) {
39509                 continue;
39510             }
39511             
39512             if (k.indexOf('-') >= 0) {
39513                 var s = k.split('-');
39514                 for (var i = 0; i<s.length; i++) {
39515                     t += "<span class='visible-"+s[i]+"'>"+this.title[k]+"</span>";
39516                 }
39517             } else {
39518                 t += "<span class='visible-"+k+"'>"+this.title[k]+"</span>";
39519             }
39520         }
39521         return t;
39522     },
39523     
39524     /**
39525      * Set this panel's title
39526      * @param {String} title
39527      */
39528     setTitle : function(title){
39529         this.title = title;
39530         if(this.region){
39531             this.region.updatePanelTitle(this, title);
39532         }
39533     },
39534     
39535     /**
39536      * Returns true is this panel was configured to be closable
39537      * @return {Boolean} 
39538      */
39539     isClosable : function(){
39540         return this.closable;
39541     },
39542     
39543     beforeSlide : function(){
39544         this.el.clip();
39545         this.resizeEl.clip();
39546     },
39547     
39548     afterSlide : function(){
39549         this.el.unclip();
39550         this.resizeEl.unclip();
39551     },
39552     
39553     /**
39554      *   Force a content refresh from the URL specified in the {@link #setUrl} method.
39555      *   Will fail silently if the {@link #setUrl} method has not been called.
39556      *   This does not activate the panel, just updates its content.
39557      */
39558     refresh : function(){
39559         if(this.refreshDelegate){
39560            this.loaded = false;
39561            this.refreshDelegate();
39562         }
39563     },
39564     
39565     /**
39566      * Destroys this panel
39567      */
39568     destroy : function(){
39569         this.el.removeAllListeners();
39570         var tempEl = document.createElement("span");
39571         tempEl.appendChild(this.el.dom);
39572         tempEl.innerHTML = "";
39573         this.el.remove();
39574         this.el = null;
39575     },
39576     
39577     /**
39578      * form - if the content panel contains a form - this is a reference to it.
39579      * @type {Roo.form.Form}
39580      */
39581     form : false,
39582     /**
39583      * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
39584      *    This contains a reference to it.
39585      * @type {Roo.View}
39586      */
39587     view : false,
39588     
39589       /**
39590      * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
39591      * <pre><code>
39592
39593 layout.addxtype({
39594        xtype : 'Form',
39595        items: [ .... ]
39596    }
39597 );
39598
39599 </code></pre>
39600      * @param {Object} cfg Xtype definition of item to add.
39601      */
39602     
39603     
39604     getChildContainer: function () {
39605         return this.getEl();
39606     }
39607     
39608     
39609     /*
39610         var  ret = new Roo.factory(cfg);
39611         return ret;
39612         
39613         
39614         // add form..
39615         if (cfg.xtype.match(/^Form$/)) {
39616             
39617             var el;
39618             //if (this.footer) {
39619             //    el = this.footer.container.insertSibling(false, 'before');
39620             //} else {
39621                 el = this.el.createChild();
39622             //}
39623
39624             this.form = new  Roo.form.Form(cfg);
39625             
39626             
39627             if ( this.form.allItems.length) {
39628                 this.form.render(el.dom);
39629             }
39630             return this.form;
39631         }
39632         // should only have one of theses..
39633         if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
39634             // views.. should not be just added - used named prop 'view''
39635             
39636             cfg.el = this.el.appendChild(document.createElement("div"));
39637             // factory?
39638             
39639             var ret = new Roo.factory(cfg);
39640              
39641              ret.render && ret.render(false, ''); // render blank..
39642             this.view = ret;
39643             return ret;
39644         }
39645         return false;
39646     }
39647     \*/
39648 });
39649  
39650 /**
39651  * @class Roo.bootstrap.panel.Grid
39652  * @extends Roo.bootstrap.panel.Content
39653  * @constructor
39654  * Create a new GridPanel.
39655  * @cfg {Roo.bootstrap.Table} grid The grid for this panel
39656  * @param {Object} config A the config object
39657   
39658  */
39659
39660
39661
39662 Roo.bootstrap.panel.Grid = function(config)
39663 {
39664     
39665       
39666     this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
39667         {tag: "div", cls: "roo-layout-grid-wrapper roo-layout-inactive-content"}, true);
39668
39669     config.el = this.wrapper;
39670     //this.el = this.wrapper;
39671     
39672       if (config.container) {
39673         // ctor'ed from a Border/panel.grid
39674         
39675         
39676         this.wrapper.setStyle("overflow", "hidden");
39677         this.wrapper.addClass('roo-grid-container');
39678
39679     }
39680     
39681     
39682     if(config.toolbar){
39683         var tool_el = this.wrapper.createChild();    
39684         this.toolbar = Roo.factory(config.toolbar);
39685         var ti = [];
39686         if (config.toolbar.items) {
39687             ti = config.toolbar.items ;
39688             delete config.toolbar.items ;
39689         }
39690         
39691         var nitems = [];
39692         this.toolbar.render(tool_el);
39693         for(var i =0;i < ti.length;i++) {
39694           //  Roo.log(['add child', items[i]]);
39695             nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
39696         }
39697         this.toolbar.items = nitems;
39698         
39699         delete config.toolbar;
39700     }
39701     
39702     Roo.bootstrap.panel.Grid.superclass.constructor.call(this, config);
39703     config.grid.scrollBody = true;;
39704     config.grid.monitorWindowResize = false; // turn off autosizing
39705     config.grid.autoHeight = false;
39706     config.grid.autoWidth = false;
39707     
39708     this.grid = new config.grid.xns[config.grid.xtype](config.grid);
39709     
39710     if (config.background) {
39711         // render grid on panel activation (if panel background)
39712         this.on('activate', function(gp) {
39713             if (!gp.grid.rendered) {
39714                 gp.grid.render(this.wrapper);
39715                 gp.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");   
39716             }
39717         });
39718             
39719     } else {
39720         this.grid.render(this.wrapper);
39721         this.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");               
39722
39723     }
39724     //this.wrapper.dom.appendChild(config.grid.getGridEl().dom);
39725     // ??? needed ??? config.el = this.wrapper;
39726     
39727     
39728     
39729   
39730     // xtype created footer. - not sure if will work as we normally have to render first..
39731     if (this.footer && !this.footer.el && this.footer.xtype) {
39732         
39733         var ctr = this.grid.getView().getFooterPanel(true);
39734         this.footer.dataSource = this.grid.dataSource;
39735         this.footer = Roo.factory(this.footer, Roo);
39736         this.footer.render(ctr);
39737         
39738     }
39739     
39740     
39741     
39742     
39743      
39744 };
39745
39746 Roo.extend(Roo.bootstrap.panel.Grid, Roo.bootstrap.panel.Content, {
39747     getId : function(){
39748         return this.grid.id;
39749     },
39750     
39751     /**
39752      * Returns the grid for this panel
39753      * @return {Roo.bootstrap.Table} 
39754      */
39755     getGrid : function(){
39756         return this.grid;    
39757     },
39758     
39759     setSize : function(width, height){
39760         if(!this.ignoreResize(width, height)){
39761             var grid = this.grid;
39762             var size = this.adjustForComponents(width, height);
39763             // tfoot is not a footer?
39764           
39765             
39766             var gridel = grid.getGridEl();
39767             gridel.setSize(size.width, size.height);
39768             
39769             var tbd = grid.getGridEl().select('tbody', true).first();
39770             var thd = grid.getGridEl().select('thead',true).first();
39771             var tbf= grid.getGridEl().select('tfoot', true).first();
39772
39773             if (tbf) {
39774                 size.height -= thd.getHeight();
39775             }
39776             if (thd) {
39777                 size.height -= thd.getHeight();
39778             }
39779             
39780             tbd.setSize(size.width, size.height );
39781             // this is for the account management tab -seems to work there.
39782             var thd = grid.getGridEl().select('thead',true).first();
39783             //if (tbd) {
39784             //    tbd.setSize(size.width, size.height - thd.getHeight());
39785             //}
39786              
39787             grid.autoSize();
39788         }
39789     },
39790      
39791     
39792     
39793     beforeSlide : function(){
39794         this.grid.getView().scroller.clip();
39795     },
39796     
39797     afterSlide : function(){
39798         this.grid.getView().scroller.unclip();
39799     },
39800     
39801     destroy : function(){
39802         this.grid.destroy();
39803         delete this.grid;
39804         Roo.bootstrap.panel.Grid.superclass.destroy.call(this); 
39805     }
39806 });
39807
39808 /**
39809  * @class Roo.bootstrap.panel.Nest
39810  * @extends Roo.bootstrap.panel.Content
39811  * @constructor
39812  * Create a new Panel, that can contain a layout.Border.
39813  * 
39814  * 
39815  * @param {Roo.BorderLayout} layout The layout for this panel
39816  * @param {String/Object} config A string to set only the title or a config object
39817  */
39818 Roo.bootstrap.panel.Nest = function(config)
39819 {
39820     // construct with only one argument..
39821     /* FIXME - implement nicer consturctors
39822     if (layout.layout) {
39823         config = layout;
39824         layout = config.layout;
39825         delete config.layout;
39826     }
39827     if (layout.xtype && !layout.getEl) {
39828         // then layout needs constructing..
39829         layout = Roo.factory(layout, Roo);
39830     }
39831     */
39832     
39833     config.el =  config.layout.getEl();
39834     
39835     Roo.bootstrap.panel.Nest.superclass.constructor.call(this, config);
39836     
39837     config.layout.monitorWindowResize = false; // turn off autosizing
39838     this.layout = config.layout;
39839     this.layout.getEl().addClass("roo-layout-nested-layout");
39840     this.layout.parent = this;
39841     
39842     
39843     
39844     
39845 };
39846
39847 Roo.extend(Roo.bootstrap.panel.Nest, Roo.bootstrap.panel.Content, {
39848
39849     setSize : function(width, height){
39850         if(!this.ignoreResize(width, height)){
39851             var size = this.adjustForComponents(width, height);
39852             var el = this.layout.getEl();
39853             if (size.height < 1) {
39854                 el.setWidth(size.width);   
39855             } else {
39856                 el.setSize(size.width, size.height);
39857             }
39858             var touch = el.dom.offsetWidth;
39859             this.layout.layout();
39860             // ie requires a double layout on the first pass
39861             if(Roo.isIE && !this.initialized){
39862                 this.initialized = true;
39863                 this.layout.layout();
39864             }
39865         }
39866     },
39867     
39868     // activate all subpanels if not currently active..
39869     
39870     setActiveState : function(active){
39871         this.active = active;
39872         this.setActiveClass(active);
39873         
39874         if(!active){
39875             this.fireEvent("deactivate", this);
39876             return;
39877         }
39878         
39879         this.fireEvent("activate", this);
39880         // not sure if this should happen before or after..
39881         if (!this.layout) {
39882             return; // should not happen..
39883         }
39884         var reg = false;
39885         for (var r in this.layout.regions) {
39886             reg = this.layout.getRegion(r);
39887             if (reg.getActivePanel()) {
39888                 //reg.showPanel(reg.getActivePanel()); // force it to activate.. 
39889                 reg.setActivePanel(reg.getActivePanel());
39890                 continue;
39891             }
39892             if (!reg.panels.length) {
39893                 continue;
39894             }
39895             reg.showPanel(reg.getPanel(0));
39896         }
39897         
39898         
39899         
39900         
39901     },
39902     
39903     /**
39904      * Returns the nested BorderLayout for this panel
39905      * @return {Roo.BorderLayout} 
39906      */
39907     getLayout : function(){
39908         return this.layout;
39909     },
39910     
39911      /**
39912      * Adds a xtype elements to the layout of the nested panel
39913      * <pre><code>
39914
39915 panel.addxtype({
39916        xtype : 'ContentPanel',
39917        region: 'west',
39918        items: [ .... ]
39919    }
39920 );
39921
39922 panel.addxtype({
39923         xtype : 'NestedLayoutPanel',
39924         region: 'west',
39925         layout: {
39926            center: { },
39927            west: { }   
39928         },
39929         items : [ ... list of content panels or nested layout panels.. ]
39930    }
39931 );
39932 </code></pre>
39933      * @param {Object} cfg Xtype definition of item to add.
39934      */
39935     addxtype : function(cfg) {
39936         return this.layout.addxtype(cfg);
39937     
39938     }
39939 });/*
39940  * Based on:
39941  * Ext JS Library 1.1.1
39942  * Copyright(c) 2006-2007, Ext JS, LLC.
39943  *
39944  * Originally Released Under LGPL - original licence link has changed is not relivant.
39945  *
39946  * Fork - LGPL
39947  * <script type="text/javascript">
39948  */
39949 /**
39950  * @class Roo.TabPanel
39951  * @extends Roo.util.Observable
39952  * A lightweight tab container.
39953  * <br><br>
39954  * Usage:
39955  * <pre><code>
39956 // basic tabs 1, built from existing content
39957 var tabs = new Roo.TabPanel("tabs1");
39958 tabs.addTab("script", "View Script");
39959 tabs.addTab("markup", "View Markup");
39960 tabs.activate("script");
39961
39962 // more advanced tabs, built from javascript
39963 var jtabs = new Roo.TabPanel("jtabs");
39964 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
39965
39966 // set up the UpdateManager
39967 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
39968 var updater = tab2.getUpdateManager();
39969 updater.setDefaultUrl("ajax1.htm");
39970 tab2.on('activate', updater.refresh, updater, true);
39971
39972 // Use setUrl for Ajax loading
39973 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
39974 tab3.setUrl("ajax2.htm", null, true);
39975
39976 // Disabled tab
39977 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
39978 tab4.disable();
39979
39980 jtabs.activate("jtabs-1");
39981  * </code></pre>
39982  * @constructor
39983  * Create a new TabPanel.
39984  * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
39985  * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
39986  */
39987 Roo.bootstrap.panel.Tabs = function(config){
39988     /**
39989     * The container element for this TabPanel.
39990     * @type Roo.Element
39991     */
39992     this.el = Roo.get(config.el);
39993     delete config.el;
39994     if(config){
39995         if(typeof config == "boolean"){
39996             this.tabPosition = config ? "bottom" : "top";
39997         }else{
39998             Roo.apply(this, config);
39999         }
40000     }
40001     
40002     if(this.tabPosition == "bottom"){
40003         // if tabs are at the bottom = create the body first.
40004         this.bodyEl = Roo.get(this.createBody(this.el.dom));
40005         this.el.addClass("roo-tabs-bottom");
40006     }
40007     // next create the tabs holders
40008     
40009     if (this.tabPosition == "west"){
40010         
40011         var reg = this.region; // fake it..
40012         while (reg) {
40013             if (!reg.mgr.parent) {
40014                 break;
40015             }
40016             reg = reg.mgr.parent.region;
40017         }
40018         Roo.log("got nest?");
40019         Roo.log(reg);
40020         if (reg.mgr.getRegion('west')) {
40021             var ctrdom = reg.mgr.getRegion('west').bodyEl.dom;
40022             this.stripWrap = Roo.get(this.createStrip(ctrdom ), true);
40023             this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
40024             this.stripEl.setVisibilityMode(Roo.Element.DISPLAY);
40025             this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
40026         
40027             
40028         }
40029         
40030         
40031     } else {
40032      
40033         this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
40034         this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
40035         this.stripEl.setVisibilityMode(Roo.Element.DISPLAY);
40036         this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
40037     }
40038     
40039     
40040     if(Roo.isIE){
40041         Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
40042     }
40043     
40044     // finally - if tabs are at the top, then create the body last..
40045     if(this.tabPosition != "bottom"){
40046         /** The body element that contains {@link Roo.TabPanelItem} bodies. +
40047          * @type Roo.Element
40048          */
40049         this.bodyEl = Roo.get(this.createBody(this.el.dom));
40050         this.el.addClass("roo-tabs-top");
40051     }
40052     this.items = [];
40053
40054     this.bodyEl.setStyle("position", "relative");
40055
40056     this.active = null;
40057     this.activateDelegate = this.activate.createDelegate(this);
40058
40059     this.addEvents({
40060         /**
40061          * @event tabchange
40062          * Fires when the active tab changes
40063          * @param {Roo.TabPanel} this
40064          * @param {Roo.TabPanelItem} activePanel The new active tab
40065          */
40066         "tabchange": true,
40067         /**
40068          * @event beforetabchange
40069          * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
40070          * @param {Roo.TabPanel} this
40071          * @param {Object} e Set cancel to true on this object to cancel the tab change
40072          * @param {Roo.TabPanelItem} tab The tab being changed to
40073          */
40074         "beforetabchange" : true
40075     });
40076
40077     Roo.EventManager.onWindowResize(this.onResize, this);
40078     this.cpad = this.el.getPadding("lr");
40079     this.hiddenCount = 0;
40080
40081
40082     // toolbar on the tabbar support...
40083     if (this.toolbar) {
40084         alert("no toolbar support yet");
40085         this.toolbar  = false;
40086         /*
40087         var tcfg = this.toolbar;
40088         tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');  
40089         this.toolbar = new Roo.Toolbar(tcfg);
40090         if (Roo.isSafari) {
40091             var tbl = tcfg.container.child('table', true);
40092             tbl.setAttribute('width', '100%');
40093         }
40094         */
40095         
40096     }
40097    
40098
40099
40100     Roo.bootstrap.panel.Tabs.superclass.constructor.call(this);
40101 };
40102
40103 Roo.extend(Roo.bootstrap.panel.Tabs, Roo.util.Observable, {
40104     /*
40105      *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
40106      */
40107     tabPosition : "top",
40108     /*
40109      *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
40110      */
40111     currentTabWidth : 0,
40112     /*
40113      *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
40114      */
40115     minTabWidth : 40,
40116     /*
40117      *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
40118      */
40119     maxTabWidth : 250,
40120     /*
40121      *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
40122      */
40123     preferredTabWidth : 175,
40124     /*
40125      *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
40126      */
40127     resizeTabs : false,
40128     /*
40129      *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
40130      */
40131     monitorResize : true,
40132     /*
40133      *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar. 
40134      */
40135     toolbar : false,  // set by caller..
40136     
40137     region : false, /// set by caller
40138     
40139     disableTooltips : true, // not used yet...
40140
40141     /**
40142      * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
40143      * @param {String} id The id of the div to use <b>or create</b>
40144      * @param {String} text The text for the tab
40145      * @param {String} content (optional) Content to put in the TabPanelItem body
40146      * @param {Boolean} closable (optional) True to create a close icon on the tab
40147      * @return {Roo.TabPanelItem} The created TabPanelItem
40148      */
40149     addTab : function(id, text, content, closable, tpl)
40150     {
40151         var item = new Roo.bootstrap.panel.TabItem({
40152             panel: this,
40153             id : id,
40154             text : text,
40155             closable : closable,
40156             tpl : tpl
40157         });
40158         this.addTabItem(item);
40159         if(content){
40160             item.setContent(content);
40161         }
40162         return item;
40163     },
40164
40165     /**
40166      * Returns the {@link Roo.TabPanelItem} with the specified id/index
40167      * @param {String/Number} id The id or index of the TabPanelItem to fetch.
40168      * @return {Roo.TabPanelItem}
40169      */
40170     getTab : function(id){
40171         return this.items[id];
40172     },
40173
40174     /**
40175      * Hides the {@link Roo.TabPanelItem} with the specified id/index
40176      * @param {String/Number} id The id or index of the TabPanelItem to hide.
40177      */
40178     hideTab : function(id){
40179         var t = this.items[id];
40180         if(!t.isHidden()){
40181            t.setHidden(true);
40182            this.hiddenCount++;
40183            this.autoSizeTabs();
40184         }
40185     },
40186
40187     /**
40188      * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
40189      * @param {String/Number} id The id or index of the TabPanelItem to unhide.
40190      */
40191     unhideTab : function(id){
40192         var t = this.items[id];
40193         if(t.isHidden()){
40194            t.setHidden(false);
40195            this.hiddenCount--;
40196            this.autoSizeTabs();
40197         }
40198     },
40199
40200     /**
40201      * Adds an existing {@link Roo.TabPanelItem}.
40202      * @param {Roo.TabPanelItem} item The TabPanelItem to add
40203      */
40204     addTabItem : function(item)
40205     {
40206         this.items[item.id] = item;
40207         this.items.push(item);
40208         this.autoSizeTabs();
40209       //  if(this.resizeTabs){
40210     //       item.setWidth(this.currentTabWidth || this.preferredTabWidth);
40211   //         this.autoSizeTabs();
40212 //        }else{
40213 //            item.autoSize();
40214        // }
40215     },
40216
40217     /**
40218      * Removes a {@link Roo.TabPanelItem}.
40219      * @param {String/Number} id The id or index of the TabPanelItem to remove.
40220      */
40221     removeTab : function(id){
40222         var items = this.items;
40223         var tab = items[id];
40224         if(!tab) { return; }
40225         var index = items.indexOf(tab);
40226         if(this.active == tab && items.length > 1){
40227             var newTab = this.getNextAvailable(index);
40228             if(newTab) {
40229                 newTab.activate();
40230             }
40231         }
40232         this.stripEl.dom.removeChild(tab.pnode.dom);
40233         if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
40234             this.bodyEl.dom.removeChild(tab.bodyEl.dom);
40235         }
40236         items.splice(index, 1);
40237         delete this.items[tab.id];
40238         tab.fireEvent("close", tab);
40239         tab.purgeListeners();
40240         this.autoSizeTabs();
40241     },
40242
40243     getNextAvailable : function(start){
40244         var items = this.items;
40245         var index = start;
40246         // look for a next tab that will slide over to
40247         // replace the one being removed
40248         while(index < items.length){
40249             var item = items[++index];
40250             if(item && !item.isHidden()){
40251                 return item;
40252             }
40253         }
40254         // if one isn't found select the previous tab (on the left)
40255         index = start;
40256         while(index >= 0){
40257             var item = items[--index];
40258             if(item && !item.isHidden()){
40259                 return item;
40260             }
40261         }
40262         return null;
40263     },
40264
40265     /**
40266      * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
40267      * @param {String/Number} id The id or index of the TabPanelItem to disable.
40268      */
40269     disableTab : function(id){
40270         var tab = this.items[id];
40271         if(tab && this.active != tab){
40272             tab.disable();
40273         }
40274     },
40275
40276     /**
40277      * Enables a {@link Roo.TabPanelItem} that is disabled.
40278      * @param {String/Number} id The id or index of the TabPanelItem to enable.
40279      */
40280     enableTab : function(id){
40281         var tab = this.items[id];
40282         tab.enable();
40283     },
40284
40285     /**
40286      * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
40287      * @param {String/Number} id The id or index of the TabPanelItem to activate.
40288      * @return {Roo.TabPanelItem} The TabPanelItem.
40289      */
40290     activate : function(id)
40291     {
40292         //Roo.log('activite:'  + id);
40293         
40294         var tab = this.items[id];
40295         if(!tab){
40296             return null;
40297         }
40298         if(tab == this.active || tab.disabled){
40299             return tab;
40300         }
40301         var e = {};
40302         this.fireEvent("beforetabchange", this, e, tab);
40303         if(e.cancel !== true && !tab.disabled){
40304             if(this.active){
40305                 this.active.hide();
40306             }
40307             this.active = this.items[id];
40308             this.active.show();
40309             this.fireEvent("tabchange", this, this.active);
40310         }
40311         return tab;
40312     },
40313
40314     /**
40315      * Gets the active {@link Roo.TabPanelItem}.
40316      * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
40317      */
40318     getActiveTab : function(){
40319         return this.active;
40320     },
40321
40322     /**
40323      * Updates the tab body element to fit the height of the container element
40324      * for overflow scrolling
40325      * @param {Number} targetHeight (optional) Override the starting height from the elements height
40326      */
40327     syncHeight : function(targetHeight){
40328         var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
40329         var bm = this.bodyEl.getMargins();
40330         var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
40331         this.bodyEl.setHeight(newHeight);
40332         return newHeight;
40333     },
40334
40335     onResize : function(){
40336         if(this.monitorResize){
40337             this.autoSizeTabs();
40338         }
40339     },
40340
40341     /**
40342      * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
40343      */
40344     beginUpdate : function(){
40345         this.updating = true;
40346     },
40347
40348     /**
40349      * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
40350      */
40351     endUpdate : function(){
40352         this.updating = false;
40353         this.autoSizeTabs();
40354     },
40355
40356     /**
40357      * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
40358      */
40359     autoSizeTabs : function()
40360     {
40361         var count = this.items.length;
40362         var vcount = count - this.hiddenCount;
40363         
40364         if (vcount < 2) {
40365             this.stripEl.hide();
40366         } else {
40367             this.stripEl.show();
40368         }
40369         
40370         if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) {
40371             return;
40372         }
40373         
40374         
40375         var w = Math.max(this.el.getWidth() - this.cpad, 10);
40376         var availWidth = Math.floor(w / vcount);
40377         var b = this.stripBody;
40378         if(b.getWidth() > w){
40379             var tabs = this.items;
40380             this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
40381             if(availWidth < this.minTabWidth){
40382                 /*if(!this.sleft){    // incomplete scrolling code
40383                     this.createScrollButtons();
40384                 }
40385                 this.showScroll();
40386                 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
40387             }
40388         }else{
40389             if(this.currentTabWidth < this.preferredTabWidth){
40390                 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
40391             }
40392         }
40393     },
40394
40395     /**
40396      * Returns the number of tabs in this TabPanel.
40397      * @return {Number}
40398      */
40399      getCount : function(){
40400          return this.items.length;
40401      },
40402
40403     /**
40404      * Resizes all the tabs to the passed width
40405      * @param {Number} The new width
40406      */
40407     setTabWidth : function(width){
40408         this.currentTabWidth = width;
40409         for(var i = 0, len = this.items.length; i < len; i++) {
40410                 if(!this.items[i].isHidden()) {
40411                 this.items[i].setWidth(width);
40412             }
40413         }
40414     },
40415
40416     /**
40417      * Destroys this TabPanel
40418      * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
40419      */
40420     destroy : function(removeEl){
40421         Roo.EventManager.removeResizeListener(this.onResize, this);
40422         for(var i = 0, len = this.items.length; i < len; i++){
40423             this.items[i].purgeListeners();
40424         }
40425         if(removeEl === true){
40426             this.el.update("");
40427             this.el.remove();
40428         }
40429     },
40430     
40431     createStrip : function(container)
40432     {
40433         var strip = document.createElement("nav");
40434         strip.className = Roo.bootstrap.version == 4 ?
40435             "navbar-light bg-light" : 
40436             "navbar navbar-default"; //"x-tabs-wrap";
40437         container.appendChild(strip);
40438         return strip;
40439     },
40440     
40441     createStripList : function(strip)
40442     {
40443         // div wrapper for retard IE
40444         // returns the "tr" element.
40445         strip.innerHTML = '<ul class="nav nav-tabs" role="tablist"></ul>';
40446         //'<div class="x-tabs-strip-wrap">'+
40447           //  '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
40448           //  '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
40449         return strip.firstChild; //.firstChild.firstChild.firstChild;
40450     },
40451     createBody : function(container)
40452     {
40453         var body = document.createElement("div");
40454         Roo.id(body, "tab-body");
40455         //Roo.fly(body).addClass("x-tabs-body");
40456         Roo.fly(body).addClass("tab-content");
40457         container.appendChild(body);
40458         return body;
40459     },
40460     createItemBody :function(bodyEl, id){
40461         var body = Roo.getDom(id);
40462         if(!body){
40463             body = document.createElement("div");
40464             body.id = id;
40465         }
40466         //Roo.fly(body).addClass("x-tabs-item-body");
40467         Roo.fly(body).addClass("tab-pane");
40468          bodyEl.insertBefore(body, bodyEl.firstChild);
40469         return body;
40470     },
40471     /** @private */
40472     createStripElements :  function(stripEl, text, closable, tpl)
40473     {
40474         var td = document.createElement("li"); // was td..
40475         td.className = 'nav-item';
40476         
40477         //stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
40478         
40479         
40480         stripEl.appendChild(td);
40481         /*if(closable){
40482             td.className = "x-tabs-closable";
40483             if(!this.closeTpl){
40484                 this.closeTpl = new Roo.Template(
40485                    '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
40486                    '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
40487                    '<div unselectable="on" class="close-icon">&#160;</div></em></span></a>'
40488                 );
40489             }
40490             var el = this.closeTpl.overwrite(td, {"text": text});
40491             var close = el.getElementsByTagName("div")[0];
40492             var inner = el.getElementsByTagName("em")[0];
40493             return {"el": el, "close": close, "inner": inner};
40494         } else {
40495         */
40496         // not sure what this is..
40497 //            if(!this.tabTpl){
40498                 //this.tabTpl = new Roo.Template(
40499                 //   '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
40500                 //   '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
40501                 //);
40502 //                this.tabTpl = new Roo.Template(
40503 //                   '<a href="#">' +
40504 //                   '<span unselectable="on"' +
40505 //                            (this.disableTooltips ? '' : ' title="{text}"') +
40506 //                            ' >{text}</span></a>'
40507 //                );
40508 //                
40509 //            }
40510
40511
40512             var template = tpl || this.tabTpl || false;
40513             
40514             if(!template){
40515                 template =  new Roo.Template(
40516                         Roo.bootstrap.version == 4 ? 
40517                             (
40518                                 '<a class="nav-link" href="#" unselectable="on"' +
40519                                      (this.disableTooltips ? '' : ' title="{text}"') +
40520                                      ' >{text}</a>'
40521                             ) : (
40522                                 '<a class="nav-link" href="#">' +
40523                                 '<span unselectable="on"' +
40524                                          (this.disableTooltips ? '' : ' title="{text}"') +
40525                                     ' >{text}</span></a>'
40526                             )
40527                 );
40528             }
40529             
40530             switch (typeof(template)) {
40531                 case 'object' :
40532                     break;
40533                 case 'string' :
40534                     template = new Roo.Template(template);
40535                     break;
40536                 default :
40537                     break;
40538             }
40539             
40540             var el = template.overwrite(td, {"text": text});
40541             
40542             var inner = el.getElementsByTagName("span")[0];
40543             
40544             return {"el": el, "inner": inner};
40545             
40546     }
40547         
40548     
40549 });
40550
40551 /**
40552  * @class Roo.TabPanelItem
40553  * @extends Roo.util.Observable
40554  * Represents an individual item (tab plus body) in a TabPanel.
40555  * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
40556  * @param {String} id The id of this TabPanelItem
40557  * @param {String} text The text for the tab of this TabPanelItem
40558  * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
40559  */
40560 Roo.bootstrap.panel.TabItem = function(config){
40561     /**
40562      * The {@link Roo.TabPanel} this TabPanelItem belongs to
40563      * @type Roo.TabPanel
40564      */
40565     this.tabPanel = config.panel;
40566     /**
40567      * The id for this TabPanelItem
40568      * @type String
40569      */
40570     this.id = config.id;
40571     /** @private */
40572     this.disabled = false;
40573     /** @private */
40574     this.text = config.text;
40575     /** @private */
40576     this.loaded = false;
40577     this.closable = config.closable;
40578
40579     /**
40580      * The body element for this TabPanelItem.
40581      * @type Roo.Element
40582      */
40583     this.bodyEl = Roo.get(this.tabPanel.createItemBody(this.tabPanel.bodyEl.dom, config.id));
40584     this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
40585     this.bodyEl.setStyle("display", "block");
40586     this.bodyEl.setStyle("zoom", "1");
40587     //this.hideAction();
40588
40589     var els = this.tabPanel.createStripElements(this.tabPanel.stripEl.dom, config.text, config.closable, config.tpl);
40590     /** @private */
40591     this.el = Roo.get(els.el);
40592     this.inner = Roo.get(els.inner, true);
40593      this.textEl = Roo.bootstrap.version == 4 ?
40594         this.el : Roo.get(this.el.dom.firstChild, true);
40595
40596     this.pnode = this.linode = Roo.get(els.el.parentNode, true);
40597     this.status_node = Roo.bootstrap.version == 4 ? this.el : this.linode;
40598
40599     
40600 //    this.el.on("mousedown", this.onTabMouseDown, this);
40601     this.el.on("click", this.onTabClick, this);
40602     /** @private */
40603     if(config.closable){
40604         var c = Roo.get(els.close, true);
40605         c.dom.title = this.closeText;
40606         c.addClassOnOver("close-over");
40607         c.on("click", this.closeClick, this);
40608      }
40609
40610     this.addEvents({
40611          /**
40612          * @event activate
40613          * Fires when this tab becomes the active tab.
40614          * @param {Roo.TabPanel} tabPanel The parent TabPanel
40615          * @param {Roo.TabPanelItem} this
40616          */
40617         "activate": true,
40618         /**
40619          * @event beforeclose
40620          * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
40621          * @param {Roo.TabPanelItem} this
40622          * @param {Object} e Set cancel to true on this object to cancel the close.
40623          */
40624         "beforeclose": true,
40625         /**
40626          * @event close
40627          * Fires when this tab is closed.
40628          * @param {Roo.TabPanelItem} this
40629          */
40630          "close": true,
40631         /**
40632          * @event deactivate
40633          * Fires when this tab is no longer the active tab.
40634          * @param {Roo.TabPanel} tabPanel The parent TabPanel
40635          * @param {Roo.TabPanelItem} this
40636          */
40637          "deactivate" : true
40638     });
40639     this.hidden = false;
40640
40641     Roo.bootstrap.panel.TabItem.superclass.constructor.call(this);
40642 };
40643
40644 Roo.extend(Roo.bootstrap.panel.TabItem, Roo.util.Observable,
40645            {
40646     purgeListeners : function(){
40647        Roo.util.Observable.prototype.purgeListeners.call(this);
40648        this.el.removeAllListeners();
40649     },
40650     /**
40651      * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
40652      */
40653     show : function(){
40654         this.status_node.addClass("active");
40655         this.showAction();
40656         if(Roo.isOpera){
40657             this.tabPanel.stripWrap.repaint();
40658         }
40659         this.fireEvent("activate", this.tabPanel, this);
40660     },
40661
40662     /**
40663      * Returns true if this tab is the active tab.
40664      * @return {Boolean}
40665      */
40666     isActive : function(){
40667         return this.tabPanel.getActiveTab() == this;
40668     },
40669
40670     /**
40671      * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
40672      */
40673     hide : function(){
40674         this.status_node.removeClass("active");
40675         this.hideAction();
40676         this.fireEvent("deactivate", this.tabPanel, this);
40677     },
40678
40679     hideAction : function(){
40680         this.bodyEl.hide();
40681         this.bodyEl.setStyle("position", "absolute");
40682         this.bodyEl.setLeft("-20000px");
40683         this.bodyEl.setTop("-20000px");
40684     },
40685
40686     showAction : function(){
40687         this.bodyEl.setStyle("position", "relative");
40688         this.bodyEl.setTop("");
40689         this.bodyEl.setLeft("");
40690         this.bodyEl.show();
40691     },
40692
40693     /**
40694      * Set the tooltip for the tab.
40695      * @param {String} tooltip The tab's tooltip
40696      */
40697     setTooltip : function(text){
40698         if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
40699             this.textEl.dom.qtip = text;
40700             this.textEl.dom.removeAttribute('title');
40701         }else{
40702             this.textEl.dom.title = text;
40703         }
40704     },
40705
40706     onTabClick : function(e){
40707         e.preventDefault();
40708         this.tabPanel.activate(this.id);
40709     },
40710
40711     onTabMouseDown : function(e){
40712         e.preventDefault();
40713         this.tabPanel.activate(this.id);
40714     },
40715 /*
40716     getWidth : function(){
40717         return this.inner.getWidth();
40718     },
40719
40720     setWidth : function(width){
40721         var iwidth = width - this.linode.getPadding("lr");
40722         this.inner.setWidth(iwidth);
40723         this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
40724         this.linode.setWidth(width);
40725     },
40726 */
40727     /**
40728      * Show or hide the tab
40729      * @param {Boolean} hidden True to hide or false to show.
40730      */
40731     setHidden : function(hidden){
40732         this.hidden = hidden;
40733         this.linode.setStyle("display", hidden ? "none" : "");
40734     },
40735
40736     /**
40737      * Returns true if this tab is "hidden"
40738      * @return {Boolean}
40739      */
40740     isHidden : function(){
40741         return this.hidden;
40742     },
40743
40744     /**
40745      * Returns the text for this tab
40746      * @return {String}
40747      */
40748     getText : function(){
40749         return this.text;
40750     },
40751     /*
40752     autoSize : function(){
40753         //this.el.beginMeasure();
40754         this.textEl.setWidth(1);
40755         /*
40756          *  #2804 [new] Tabs in Roojs
40757          *  increase the width by 2-4 pixels to prevent the ellipssis showing in chrome
40758          */
40759         //this.setWidth(this.textEl.dom.scrollWidth+this.linode.getPadding("lr")+this.inner.getPadding("lr") + 2);
40760         //this.el.endMeasure();
40761     //},
40762
40763     /**
40764      * Sets the text for the tab (Note: this also sets the tooltip text)
40765      * @param {String} text The tab's text and tooltip
40766      */
40767     setText : function(text){
40768         this.text = text;
40769         this.textEl.update(text);
40770         this.setTooltip(text);
40771         //if(!this.tabPanel.resizeTabs){
40772         //    this.autoSize();
40773         //}
40774     },
40775     /**
40776      * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
40777      */
40778     activate : function(){
40779         this.tabPanel.activate(this.id);
40780     },
40781
40782     /**
40783      * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
40784      */
40785     disable : function(){
40786         if(this.tabPanel.active != this){
40787             this.disabled = true;
40788             this.status_node.addClass("disabled");
40789         }
40790     },
40791
40792     /**
40793      * Enables this TabPanelItem if it was previously disabled.
40794      */
40795     enable : function(){
40796         this.disabled = false;
40797         this.status_node.removeClass("disabled");
40798     },
40799
40800     /**
40801      * Sets the content for this TabPanelItem.
40802      * @param {String} content The content
40803      * @param {Boolean} loadScripts true to look for and load scripts
40804      */
40805     setContent : function(content, loadScripts){
40806         this.bodyEl.update(content, loadScripts);
40807     },
40808
40809     /**
40810      * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
40811      * @return {Roo.UpdateManager} The UpdateManager
40812      */
40813     getUpdateManager : function(){
40814         return this.bodyEl.getUpdateManager();
40815     },
40816
40817     /**
40818      * Set a URL to be used to load the content for this TabPanelItem.
40819      * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
40820      * @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)
40821      * @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)
40822      * @return {Roo.UpdateManager} The UpdateManager
40823      */
40824     setUrl : function(url, params, loadOnce){
40825         if(this.refreshDelegate){
40826             this.un('activate', this.refreshDelegate);
40827         }
40828         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
40829         this.on("activate", this.refreshDelegate);
40830         return this.bodyEl.getUpdateManager();
40831     },
40832
40833     /** @private */
40834     _handleRefresh : function(url, params, loadOnce){
40835         if(!loadOnce || !this.loaded){
40836             var updater = this.bodyEl.getUpdateManager();
40837             updater.update(url, params, this._setLoaded.createDelegate(this));
40838         }
40839     },
40840
40841     /**
40842      *   Forces a content refresh from the URL specified in the {@link #setUrl} method.
40843      *   Will fail silently if the setUrl method has not been called.
40844      *   This does not activate the panel, just updates its content.
40845      */
40846     refresh : function(){
40847         if(this.refreshDelegate){
40848            this.loaded = false;
40849            this.refreshDelegate();
40850         }
40851     },
40852
40853     /** @private */
40854     _setLoaded : function(){
40855         this.loaded = true;
40856     },
40857
40858     /** @private */
40859     closeClick : function(e){
40860         var o = {};
40861         e.stopEvent();
40862         this.fireEvent("beforeclose", this, o);
40863         if(o.cancel !== true){
40864             this.tabPanel.removeTab(this.id);
40865         }
40866     },
40867     /**
40868      * The text displayed in the tooltip for the close icon.
40869      * @type String
40870      */
40871     closeText : "Close this tab"
40872 });
40873 /**
40874 *    This script refer to:
40875 *    Title: International Telephone Input
40876 *    Author: Jack O'Connor
40877 *    Code version:  v12.1.12
40878 *    Availability: https://github.com/jackocnr/intl-tel-input.git
40879 **/
40880
40881 Roo.bootstrap.PhoneInputData = function() {
40882     var d = [
40883       [
40884         "Afghanistan (‫افغانستان‬‎)",
40885         "af",
40886         "93"
40887       ],
40888       [
40889         "Albania (Shqipëri)",
40890         "al",
40891         "355"
40892       ],
40893       [
40894         "Algeria (‫الجزائر‬‎)",
40895         "dz",
40896         "213"
40897       ],
40898       [
40899         "American Samoa",
40900         "as",
40901         "1684"
40902       ],
40903       [
40904         "Andorra",
40905         "ad",
40906         "376"
40907       ],
40908       [
40909         "Angola",
40910         "ao",
40911         "244"
40912       ],
40913       [
40914         "Anguilla",
40915         "ai",
40916         "1264"
40917       ],
40918       [
40919         "Antigua and Barbuda",
40920         "ag",
40921         "1268"
40922       ],
40923       [
40924         "Argentina",
40925         "ar",
40926         "54"
40927       ],
40928       [
40929         "Armenia (Հայաստան)",
40930         "am",
40931         "374"
40932       ],
40933       [
40934         "Aruba",
40935         "aw",
40936         "297"
40937       ],
40938       [
40939         "Australia",
40940         "au",
40941         "61",
40942         0
40943       ],
40944       [
40945         "Austria (Österreich)",
40946         "at",
40947         "43"
40948       ],
40949       [
40950         "Azerbaijan (Azərbaycan)",
40951         "az",
40952         "994"
40953       ],
40954       [
40955         "Bahamas",
40956         "bs",
40957         "1242"
40958       ],
40959       [
40960         "Bahrain (‫البحرين‬‎)",
40961         "bh",
40962         "973"
40963       ],
40964       [
40965         "Bangladesh (বাংলাদেশ)",
40966         "bd",
40967         "880"
40968       ],
40969       [
40970         "Barbados",
40971         "bb",
40972         "1246"
40973       ],
40974       [
40975         "Belarus (Беларусь)",
40976         "by",
40977         "375"
40978       ],
40979       [
40980         "Belgium (België)",
40981         "be",
40982         "32"
40983       ],
40984       [
40985         "Belize",
40986         "bz",
40987         "501"
40988       ],
40989       [
40990         "Benin (Bénin)",
40991         "bj",
40992         "229"
40993       ],
40994       [
40995         "Bermuda",
40996         "bm",
40997         "1441"
40998       ],
40999       [
41000         "Bhutan (འབྲུག)",
41001         "bt",
41002         "975"
41003       ],
41004       [
41005         "Bolivia",
41006         "bo",
41007         "591"
41008       ],
41009       [
41010         "Bosnia and Herzegovina (Босна и Херцеговина)",
41011         "ba",
41012         "387"
41013       ],
41014       [
41015         "Botswana",
41016         "bw",
41017         "267"
41018       ],
41019       [
41020         "Brazil (Brasil)",
41021         "br",
41022         "55"
41023       ],
41024       [
41025         "British Indian Ocean Territory",
41026         "io",
41027         "246"
41028       ],
41029       [
41030         "British Virgin Islands",
41031         "vg",
41032         "1284"
41033       ],
41034       [
41035         "Brunei",
41036         "bn",
41037         "673"
41038       ],
41039       [
41040         "Bulgaria (България)",
41041         "bg",
41042         "359"
41043       ],
41044       [
41045         "Burkina Faso",
41046         "bf",
41047         "226"
41048       ],
41049       [
41050         "Burundi (Uburundi)",
41051         "bi",
41052         "257"
41053       ],
41054       [
41055         "Cambodia (កម្ពុជា)",
41056         "kh",
41057         "855"
41058       ],
41059       [
41060         "Cameroon (Cameroun)",
41061         "cm",
41062         "237"
41063       ],
41064       [
41065         "Canada",
41066         "ca",
41067         "1",
41068         1,
41069         ["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"]
41070       ],
41071       [
41072         "Cape Verde (Kabu Verdi)",
41073         "cv",
41074         "238"
41075       ],
41076       [
41077         "Caribbean Netherlands",
41078         "bq",
41079         "599",
41080         1
41081       ],
41082       [
41083         "Cayman Islands",
41084         "ky",
41085         "1345"
41086       ],
41087       [
41088         "Central African Republic (République centrafricaine)",
41089         "cf",
41090         "236"
41091       ],
41092       [
41093         "Chad (Tchad)",
41094         "td",
41095         "235"
41096       ],
41097       [
41098         "Chile",
41099         "cl",
41100         "56"
41101       ],
41102       [
41103         "China (中国)",
41104         "cn",
41105         "86"
41106       ],
41107       [
41108         "Christmas Island",
41109         "cx",
41110         "61",
41111         2
41112       ],
41113       [
41114         "Cocos (Keeling) Islands",
41115         "cc",
41116         "61",
41117         1
41118       ],
41119       [
41120         "Colombia",
41121         "co",
41122         "57"
41123       ],
41124       [
41125         "Comoros (‫جزر القمر‬‎)",
41126         "km",
41127         "269"
41128       ],
41129       [
41130         "Congo (DRC) (Jamhuri ya Kidemokrasia ya Kongo)",
41131         "cd",
41132         "243"
41133       ],
41134       [
41135         "Congo (Republic) (Congo-Brazzaville)",
41136         "cg",
41137         "242"
41138       ],
41139       [
41140         "Cook Islands",
41141         "ck",
41142         "682"
41143       ],
41144       [
41145         "Costa Rica",
41146         "cr",
41147         "506"
41148       ],
41149       [
41150         "Côte d’Ivoire",
41151         "ci",
41152         "225"
41153       ],
41154       [
41155         "Croatia (Hrvatska)",
41156         "hr",
41157         "385"
41158       ],
41159       [
41160         "Cuba",
41161         "cu",
41162         "53"
41163       ],
41164       [
41165         "Curaçao",
41166         "cw",
41167         "599",
41168         0
41169       ],
41170       [
41171         "Cyprus (Κύπρος)",
41172         "cy",
41173         "357"
41174       ],
41175       [
41176         "Czech Republic (Česká republika)",
41177         "cz",
41178         "420"
41179       ],
41180       [
41181         "Denmark (Danmark)",
41182         "dk",
41183         "45"
41184       ],
41185       [
41186         "Djibouti",
41187         "dj",
41188         "253"
41189       ],
41190       [
41191         "Dominica",
41192         "dm",
41193         "1767"
41194       ],
41195       [
41196         "Dominican Republic (República Dominicana)",
41197         "do",
41198         "1",
41199         2,
41200         ["809", "829", "849"]
41201       ],
41202       [
41203         "Ecuador",
41204         "ec",
41205         "593"
41206       ],
41207       [
41208         "Egypt (‫مصر‬‎)",
41209         "eg",
41210         "20"
41211       ],
41212       [
41213         "El Salvador",
41214         "sv",
41215         "503"
41216       ],
41217       [
41218         "Equatorial Guinea (Guinea Ecuatorial)",
41219         "gq",
41220         "240"
41221       ],
41222       [
41223         "Eritrea",
41224         "er",
41225         "291"
41226       ],
41227       [
41228         "Estonia (Eesti)",
41229         "ee",
41230         "372"
41231       ],
41232       [
41233         "Ethiopia",
41234         "et",
41235         "251"
41236       ],
41237       [
41238         "Falkland Islands (Islas Malvinas)",
41239         "fk",
41240         "500"
41241       ],
41242       [
41243         "Faroe Islands (Føroyar)",
41244         "fo",
41245         "298"
41246       ],
41247       [
41248         "Fiji",
41249         "fj",
41250         "679"
41251       ],
41252       [
41253         "Finland (Suomi)",
41254         "fi",
41255         "358",
41256         0
41257       ],
41258       [
41259         "France",
41260         "fr",
41261         "33"
41262       ],
41263       [
41264         "French Guiana (Guyane française)",
41265         "gf",
41266         "594"
41267       ],
41268       [
41269         "French Polynesia (Polynésie française)",
41270         "pf",
41271         "689"
41272       ],
41273       [
41274         "Gabon",
41275         "ga",
41276         "241"
41277       ],
41278       [
41279         "Gambia",
41280         "gm",
41281         "220"
41282       ],
41283       [
41284         "Georgia (საქართველო)",
41285         "ge",
41286         "995"
41287       ],
41288       [
41289         "Germany (Deutschland)",
41290         "de",
41291         "49"
41292       ],
41293       [
41294         "Ghana (Gaana)",
41295         "gh",
41296         "233"
41297       ],
41298       [
41299         "Gibraltar",
41300         "gi",
41301         "350"
41302       ],
41303       [
41304         "Greece (Ελλάδα)",
41305         "gr",
41306         "30"
41307       ],
41308       [
41309         "Greenland (Kalaallit Nunaat)",
41310         "gl",
41311         "299"
41312       ],
41313       [
41314         "Grenada",
41315         "gd",
41316         "1473"
41317       ],
41318       [
41319         "Guadeloupe",
41320         "gp",
41321         "590",
41322         0
41323       ],
41324       [
41325         "Guam",
41326         "gu",
41327         "1671"
41328       ],
41329       [
41330         "Guatemala",
41331         "gt",
41332         "502"
41333       ],
41334       [
41335         "Guernsey",
41336         "gg",
41337         "44",
41338         1
41339       ],
41340       [
41341         "Guinea (Guinée)",
41342         "gn",
41343         "224"
41344       ],
41345       [
41346         "Guinea-Bissau (Guiné Bissau)",
41347         "gw",
41348         "245"
41349       ],
41350       [
41351         "Guyana",
41352         "gy",
41353         "592"
41354       ],
41355       [
41356         "Haiti",
41357         "ht",
41358         "509"
41359       ],
41360       [
41361         "Honduras",
41362         "hn",
41363         "504"
41364       ],
41365       [
41366         "Hong Kong (香港)",
41367         "hk",
41368         "852"
41369       ],
41370       [
41371         "Hungary (Magyarország)",
41372         "hu",
41373         "36"
41374       ],
41375       [
41376         "Iceland (Ísland)",
41377         "is",
41378         "354"
41379       ],
41380       [
41381         "India (भारत)",
41382         "in",
41383         "91"
41384       ],
41385       [
41386         "Indonesia",
41387         "id",
41388         "62"
41389       ],
41390       [
41391         "Iran (‫ایران‬‎)",
41392         "ir",
41393         "98"
41394       ],
41395       [
41396         "Iraq (‫العراق‬‎)",
41397         "iq",
41398         "964"
41399       ],
41400       [
41401         "Ireland",
41402         "ie",
41403         "353"
41404       ],
41405       [
41406         "Isle of Man",
41407         "im",
41408         "44",
41409         2
41410       ],
41411       [
41412         "Israel (‫ישראל‬‎)",
41413         "il",
41414         "972"
41415       ],
41416       [
41417         "Italy (Italia)",
41418         "it",
41419         "39",
41420         0
41421       ],
41422       [
41423         "Jamaica",
41424         "jm",
41425         "1876"
41426       ],
41427       [
41428         "Japan (日本)",
41429         "jp",
41430         "81"
41431       ],
41432       [
41433         "Jersey",
41434         "je",
41435         "44",
41436         3
41437       ],
41438       [
41439         "Jordan (‫الأردن‬‎)",
41440         "jo",
41441         "962"
41442       ],
41443       [
41444         "Kazakhstan (Казахстан)",
41445         "kz",
41446         "7",
41447         1
41448       ],
41449       [
41450         "Kenya",
41451         "ke",
41452         "254"
41453       ],
41454       [
41455         "Kiribati",
41456         "ki",
41457         "686"
41458       ],
41459       [
41460         "Kosovo",
41461         "xk",
41462         "383"
41463       ],
41464       [
41465         "Kuwait (‫الكويت‬‎)",
41466         "kw",
41467         "965"
41468       ],
41469       [
41470         "Kyrgyzstan (Кыргызстан)",
41471         "kg",
41472         "996"
41473       ],
41474       [
41475         "Laos (ລາວ)",
41476         "la",
41477         "856"
41478       ],
41479       [
41480         "Latvia (Latvija)",
41481         "lv",
41482         "371"
41483       ],
41484       [
41485         "Lebanon (‫لبنان‬‎)",
41486         "lb",
41487         "961"
41488       ],
41489       [
41490         "Lesotho",
41491         "ls",
41492         "266"
41493       ],
41494       [
41495         "Liberia",
41496         "lr",
41497         "231"
41498       ],
41499       [
41500         "Libya (‫ليبيا‬‎)",
41501         "ly",
41502         "218"
41503       ],
41504       [
41505         "Liechtenstein",
41506         "li",
41507         "423"
41508       ],
41509       [
41510         "Lithuania (Lietuva)",
41511         "lt",
41512         "370"
41513       ],
41514       [
41515         "Luxembourg",
41516         "lu",
41517         "352"
41518       ],
41519       [
41520         "Macau (澳門)",
41521         "mo",
41522         "853"
41523       ],
41524       [
41525         "Macedonia (FYROM) (Македонија)",
41526         "mk",
41527         "389"
41528       ],
41529       [
41530         "Madagascar (Madagasikara)",
41531         "mg",
41532         "261"
41533       ],
41534       [
41535         "Malawi",
41536         "mw",
41537         "265"
41538       ],
41539       [
41540         "Malaysia",
41541         "my",
41542         "60"
41543       ],
41544       [
41545         "Maldives",
41546         "mv",
41547         "960"
41548       ],
41549       [
41550         "Mali",
41551         "ml",
41552         "223"
41553       ],
41554       [
41555         "Malta",
41556         "mt",
41557         "356"
41558       ],
41559       [
41560         "Marshall Islands",
41561         "mh",
41562         "692"
41563       ],
41564       [
41565         "Martinique",
41566         "mq",
41567         "596"
41568       ],
41569       [
41570         "Mauritania (‫موريتانيا‬‎)",
41571         "mr",
41572         "222"
41573       ],
41574       [
41575         "Mauritius (Moris)",
41576         "mu",
41577         "230"
41578       ],
41579       [
41580         "Mayotte",
41581         "yt",
41582         "262",
41583         1
41584       ],
41585       [
41586         "Mexico (México)",
41587         "mx",
41588         "52"
41589       ],
41590       [
41591         "Micronesia",
41592         "fm",
41593         "691"
41594       ],
41595       [
41596         "Moldova (Republica Moldova)",
41597         "md",
41598         "373"
41599       ],
41600       [
41601         "Monaco",
41602         "mc",
41603         "377"
41604       ],
41605       [
41606         "Mongolia (Монгол)",
41607         "mn",
41608         "976"
41609       ],
41610       [
41611         "Montenegro (Crna Gora)",
41612         "me",
41613         "382"
41614       ],
41615       [
41616         "Montserrat",
41617         "ms",
41618         "1664"
41619       ],
41620       [
41621         "Morocco (‫المغرب‬‎)",
41622         "ma",
41623         "212",
41624         0
41625       ],
41626       [
41627         "Mozambique (Moçambique)",
41628         "mz",
41629         "258"
41630       ],
41631       [
41632         "Myanmar (Burma) (မြန်မာ)",
41633         "mm",
41634         "95"
41635       ],
41636       [
41637         "Namibia (Namibië)",
41638         "na",
41639         "264"
41640       ],
41641       [
41642         "Nauru",
41643         "nr",
41644         "674"
41645       ],
41646       [
41647         "Nepal (नेपाल)",
41648         "np",
41649         "977"
41650       ],
41651       [
41652         "Netherlands (Nederland)",
41653         "nl",
41654         "31"
41655       ],
41656       [
41657         "New Caledonia (Nouvelle-Calédonie)",
41658         "nc",
41659         "687"
41660       ],
41661       [
41662         "New Zealand",
41663         "nz",
41664         "64"
41665       ],
41666       [
41667         "Nicaragua",
41668         "ni",
41669         "505"
41670       ],
41671       [
41672         "Niger (Nijar)",
41673         "ne",
41674         "227"
41675       ],
41676       [
41677         "Nigeria",
41678         "ng",
41679         "234"
41680       ],
41681       [
41682         "Niue",
41683         "nu",
41684         "683"
41685       ],
41686       [
41687         "Norfolk Island",
41688         "nf",
41689         "672"
41690       ],
41691       [
41692         "North Korea (조선 민주주의 인민 공화국)",
41693         "kp",
41694         "850"
41695       ],
41696       [
41697         "Northern Mariana Islands",
41698         "mp",
41699         "1670"
41700       ],
41701       [
41702         "Norway (Norge)",
41703         "no",
41704         "47",
41705         0
41706       ],
41707       [
41708         "Oman (‫عُمان‬‎)",
41709         "om",
41710         "968"
41711       ],
41712       [
41713         "Pakistan (‫پاکستان‬‎)",
41714         "pk",
41715         "92"
41716       ],
41717       [
41718         "Palau",
41719         "pw",
41720         "680"
41721       ],
41722       [
41723         "Palestine (‫فلسطين‬‎)",
41724         "ps",
41725         "970"
41726       ],
41727       [
41728         "Panama (Panamá)",
41729         "pa",
41730         "507"
41731       ],
41732       [
41733         "Papua New Guinea",
41734         "pg",
41735         "675"
41736       ],
41737       [
41738         "Paraguay",
41739         "py",
41740         "595"
41741       ],
41742       [
41743         "Peru (Perú)",
41744         "pe",
41745         "51"
41746       ],
41747       [
41748         "Philippines",
41749         "ph",
41750         "63"
41751       ],
41752       [
41753         "Poland (Polska)",
41754         "pl",
41755         "48"
41756       ],
41757       [
41758         "Portugal",
41759         "pt",
41760         "351"
41761       ],
41762       [
41763         "Puerto Rico",
41764         "pr",
41765         "1",
41766         3,
41767         ["787", "939"]
41768       ],
41769       [
41770         "Qatar (‫قطر‬‎)",
41771         "qa",
41772         "974"
41773       ],
41774       [
41775         "Réunion (La Réunion)",
41776         "re",
41777         "262",
41778         0
41779       ],
41780       [
41781         "Romania (România)",
41782         "ro",
41783         "40"
41784       ],
41785       [
41786         "Russia (Россия)",
41787         "ru",
41788         "7",
41789         0
41790       ],
41791       [
41792         "Rwanda",
41793         "rw",
41794         "250"
41795       ],
41796       [
41797         "Saint Barthélemy",
41798         "bl",
41799         "590",
41800         1
41801       ],
41802       [
41803         "Saint Helena",
41804         "sh",
41805         "290"
41806       ],
41807       [
41808         "Saint Kitts and Nevis",
41809         "kn",
41810         "1869"
41811       ],
41812       [
41813         "Saint Lucia",
41814         "lc",
41815         "1758"
41816       ],
41817       [
41818         "Saint Martin (Saint-Martin (partie française))",
41819         "mf",
41820         "590",
41821         2
41822       ],
41823       [
41824         "Saint Pierre and Miquelon (Saint-Pierre-et-Miquelon)",
41825         "pm",
41826         "508"
41827       ],
41828       [
41829         "Saint Vincent and the Grenadines",
41830         "vc",
41831         "1784"
41832       ],
41833       [
41834         "Samoa",
41835         "ws",
41836         "685"
41837       ],
41838       [
41839         "San Marino",
41840         "sm",
41841         "378"
41842       ],
41843       [
41844         "São Tomé and Príncipe (São Tomé e Príncipe)",
41845         "st",
41846         "239"
41847       ],
41848       [
41849         "Saudi Arabia (‫المملكة العربية السعودية‬‎)",
41850         "sa",
41851         "966"
41852       ],
41853       [
41854         "Senegal (Sénégal)",
41855         "sn",
41856         "221"
41857       ],
41858       [
41859         "Serbia (Србија)",
41860         "rs",
41861         "381"
41862       ],
41863       [
41864         "Seychelles",
41865         "sc",
41866         "248"
41867       ],
41868       [
41869         "Sierra Leone",
41870         "sl",
41871         "232"
41872       ],
41873       [
41874         "Singapore",
41875         "sg",
41876         "65"
41877       ],
41878       [
41879         "Sint Maarten",
41880         "sx",
41881         "1721"
41882       ],
41883       [
41884         "Slovakia (Slovensko)",
41885         "sk",
41886         "421"
41887       ],
41888       [
41889         "Slovenia (Slovenija)",
41890         "si",
41891         "386"
41892       ],
41893       [
41894         "Solomon Islands",
41895         "sb",
41896         "677"
41897       ],
41898       [
41899         "Somalia (Soomaaliya)",
41900         "so",
41901         "252"
41902       ],
41903       [
41904         "South Africa",
41905         "za",
41906         "27"
41907       ],
41908       [
41909         "South Korea (대한민국)",
41910         "kr",
41911         "82"
41912       ],
41913       [
41914         "South Sudan (‫جنوب السودان‬‎)",
41915         "ss",
41916         "211"
41917       ],
41918       [
41919         "Spain (España)",
41920         "es",
41921         "34"
41922       ],
41923       [
41924         "Sri Lanka (ශ්‍රී ලංකාව)",
41925         "lk",
41926         "94"
41927       ],
41928       [
41929         "Sudan (‫السودان‬‎)",
41930         "sd",
41931         "249"
41932       ],
41933       [
41934         "Suriname",
41935         "sr",
41936         "597"
41937       ],
41938       [
41939         "Svalbard and Jan Mayen",
41940         "sj",
41941         "47",
41942         1
41943       ],
41944       [
41945         "Swaziland",
41946         "sz",
41947         "268"
41948       ],
41949       [
41950         "Sweden (Sverige)",
41951         "se",
41952         "46"
41953       ],
41954       [
41955         "Switzerland (Schweiz)",
41956         "ch",
41957         "41"
41958       ],
41959       [
41960         "Syria (‫سوريا‬‎)",
41961         "sy",
41962         "963"
41963       ],
41964       [
41965         "Taiwan (台灣)",
41966         "tw",
41967         "886"
41968       ],
41969       [
41970         "Tajikistan",
41971         "tj",
41972         "992"
41973       ],
41974       [
41975         "Tanzania",
41976         "tz",
41977         "255"
41978       ],
41979       [
41980         "Thailand (ไทย)",
41981         "th",
41982         "66"
41983       ],
41984       [
41985         "Timor-Leste",
41986         "tl",
41987         "670"
41988       ],
41989       [
41990         "Togo",
41991         "tg",
41992         "228"
41993       ],
41994       [
41995         "Tokelau",
41996         "tk",
41997         "690"
41998       ],
41999       [
42000         "Tonga",
42001         "to",
42002         "676"
42003       ],
42004       [
42005         "Trinidad and Tobago",
42006         "tt",
42007         "1868"
42008       ],
42009       [
42010         "Tunisia (‫تونس‬‎)",
42011         "tn",
42012         "216"
42013       ],
42014       [
42015         "Turkey (Türkiye)",
42016         "tr",
42017         "90"
42018       ],
42019       [
42020         "Turkmenistan",
42021         "tm",
42022         "993"
42023       ],
42024       [
42025         "Turks and Caicos Islands",
42026         "tc",
42027         "1649"
42028       ],
42029       [
42030         "Tuvalu",
42031         "tv",
42032         "688"
42033       ],
42034       [
42035         "U.S. Virgin Islands",
42036         "vi",
42037         "1340"
42038       ],
42039       [
42040         "Uganda",
42041         "ug",
42042         "256"
42043       ],
42044       [
42045         "Ukraine (Україна)",
42046         "ua",
42047         "380"
42048       ],
42049       [
42050         "United Arab Emirates (‫الإمارات العربية المتحدة‬‎)",
42051         "ae",
42052         "971"
42053       ],
42054       [
42055         "United Kingdom",
42056         "gb",
42057         "44",
42058         0
42059       ],
42060       [
42061         "United States",
42062         "us",
42063         "1",
42064         0
42065       ],
42066       [
42067         "Uruguay",
42068         "uy",
42069         "598"
42070       ],
42071       [
42072         "Uzbekistan (Oʻzbekiston)",
42073         "uz",
42074         "998"
42075       ],
42076       [
42077         "Vanuatu",
42078         "vu",
42079         "678"
42080       ],
42081       [
42082         "Vatican City (Città del Vaticano)",
42083         "va",
42084         "39",
42085         1
42086       ],
42087       [
42088         "Venezuela",
42089         "ve",
42090         "58"
42091       ],
42092       [
42093         "Vietnam (Việt Nam)",
42094         "vn",
42095         "84"
42096       ],
42097       [
42098         "Wallis and Futuna (Wallis-et-Futuna)",
42099         "wf",
42100         "681"
42101       ],
42102       [
42103         "Western Sahara (‫الصحراء الغربية‬‎)",
42104         "eh",
42105         "212",
42106         1
42107       ],
42108       [
42109         "Yemen (‫اليمن‬‎)",
42110         "ye",
42111         "967"
42112       ],
42113       [
42114         "Zambia",
42115         "zm",
42116         "260"
42117       ],
42118       [
42119         "Zimbabwe",
42120         "zw",
42121         "263"
42122       ],
42123       [
42124         "Åland Islands",
42125         "ax",
42126         "358",
42127         1
42128       ]
42129   ];
42130   
42131   return d;
42132 }/**
42133 *    This script refer to:
42134 *    Title: International Telephone Input
42135 *    Author: Jack O'Connor
42136 *    Code version:  v12.1.12
42137 *    Availability: https://github.com/jackocnr/intl-tel-input.git
42138 **/
42139
42140 /**
42141  * @class Roo.bootstrap.PhoneInput
42142  * @extends Roo.bootstrap.TriggerField
42143  * An input with International dial-code selection
42144  
42145  * @cfg {String} defaultDialCode default '+852'
42146  * @cfg {Array} preferedCountries default []
42147   
42148  * @constructor
42149  * Create a new PhoneInput.
42150  * @param {Object} config Configuration options
42151  */
42152
42153 Roo.bootstrap.PhoneInput = function(config) {
42154     Roo.bootstrap.PhoneInput.superclass.constructor.call(this, config);
42155 };
42156
42157 Roo.extend(Roo.bootstrap.PhoneInput, Roo.bootstrap.TriggerField, {
42158         
42159         listWidth: undefined,
42160         
42161         selectedClass: 'active',
42162         
42163         invalidClass : "has-warning",
42164         
42165         validClass: 'has-success',
42166         
42167         allowed: '0123456789',
42168         
42169         max_length: 15,
42170         
42171         /**
42172          * @cfg {String} defaultDialCode The default dial code when initializing the input
42173          */
42174         defaultDialCode: '+852',
42175         
42176         /**
42177          * @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
42178          */
42179         preferedCountries: false,
42180         
42181         getAutoCreate : function()
42182         {
42183             var data = Roo.bootstrap.PhoneInputData();
42184             var align = this.labelAlign || this.parentLabelAlign();
42185             var id = Roo.id();
42186             
42187             this.allCountries = [];
42188             this.dialCodeMapping = [];
42189             
42190             for (var i = 0; i < data.length; i++) {
42191               var c = data[i];
42192               this.allCountries[i] = {
42193                 name: c[0],
42194                 iso2: c[1],
42195                 dialCode: c[2],
42196                 priority: c[3] || 0,
42197                 areaCodes: c[4] || null
42198               };
42199               this.dialCodeMapping[c[2]] = {
42200                   name: c[0],
42201                   iso2: c[1],
42202                   priority: c[3] || 0,
42203                   areaCodes: c[4] || null
42204               };
42205             }
42206             
42207             var cfg = {
42208                 cls: 'form-group',
42209                 cn: []
42210             };
42211             
42212             var input =  {
42213                 tag: 'input',
42214                 id : id,
42215                 // type: 'number', -- do not use number - we get the flaky up/down arrows.
42216                 maxlength: this.max_length,
42217                 cls : 'form-control tel-input',
42218                 autocomplete: 'new-password'
42219             };
42220             
42221             var hiddenInput = {
42222                 tag: 'input',
42223                 type: 'hidden',
42224                 cls: 'hidden-tel-input'
42225             };
42226             
42227             if (this.name) {
42228                 hiddenInput.name = this.name;
42229             }
42230             
42231             if (this.disabled) {
42232                 input.disabled = true;
42233             }
42234             
42235             var flag_container = {
42236                 tag: 'div',
42237                 cls: 'flag-box',
42238                 cn: [
42239                     {
42240                         tag: 'div',
42241                         cls: 'flag'
42242                     },
42243                     {
42244                         tag: 'div',
42245                         cls: 'caret'
42246                     }
42247                 ]
42248             };
42249             
42250             var box = {
42251                 tag: 'div',
42252                 cls: this.hasFeedback ? 'has-feedback' : '',
42253                 cn: [
42254                     hiddenInput,
42255                     input,
42256                     {
42257                         tag: 'input',
42258                         cls: 'dial-code-holder',
42259                         disabled: true
42260                     }
42261                 ]
42262             };
42263             
42264             var container = {
42265                 cls: 'roo-select2-container input-group',
42266                 cn: [
42267                     flag_container,
42268                     box
42269                 ]
42270             };
42271             
42272             if (this.fieldLabel.length) {
42273                 var indicator = {
42274                     tag: 'i',
42275                     tooltip: 'This field is required'
42276                 };
42277                 
42278                 var label = {
42279                     tag: 'label',
42280                     'for':  id,
42281                     cls: 'control-label',
42282                     cn: []
42283                 };
42284                 
42285                 var label_text = {
42286                     tag: 'span',
42287                     html: this.fieldLabel
42288                 };
42289                 
42290                 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
42291                 label.cn = [
42292                     indicator,
42293                     label_text
42294                 ];
42295                 
42296                 if(this.indicatorpos == 'right') {
42297                     indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
42298                     label.cn = [
42299                         label_text,
42300                         indicator
42301                     ];
42302                 }
42303                 
42304                 if(align == 'left') {
42305                     container = {
42306                         tag: 'div',
42307                         cn: [
42308                             container
42309                         ]
42310                     };
42311                     
42312                     if(this.labelWidth > 12){
42313                         label.style = "width: " + this.labelWidth + 'px';
42314                     }
42315                     if(this.labelWidth < 13 && this.labelmd == 0){
42316                         this.labelmd = this.labelWidth;
42317                     }
42318                     if(this.labellg > 0){
42319                         label.cls += ' col-lg-' + this.labellg;
42320                         input.cls += ' col-lg-' + (12 - this.labellg);
42321                     }
42322                     if(this.labelmd > 0){
42323                         label.cls += ' col-md-' + this.labelmd;
42324                         container.cls += ' col-md-' + (12 - this.labelmd);
42325                     }
42326                     if(this.labelsm > 0){
42327                         label.cls += ' col-sm-' + this.labelsm;
42328                         container.cls += ' col-sm-' + (12 - this.labelsm);
42329                     }
42330                     if(this.labelxs > 0){
42331                         label.cls += ' col-xs-' + this.labelxs;
42332                         container.cls += ' col-xs-' + (12 - this.labelxs);
42333                     }
42334                 }
42335             }
42336             
42337             cfg.cn = [
42338                 label,
42339                 container
42340             ];
42341             
42342             var settings = this;
42343             
42344             ['xs','sm','md','lg'].map(function(size){
42345                 if (settings[size]) {
42346                     cfg.cls += ' col-' + size + '-' + settings[size];
42347                 }
42348             });
42349             
42350             this.store = new Roo.data.Store({
42351                 proxy : new Roo.data.MemoryProxy({}),
42352                 reader : new Roo.data.JsonReader({
42353                     fields : [
42354                         {
42355                             'name' : 'name',
42356                             'type' : 'string'
42357                         },
42358                         {
42359                             'name' : 'iso2',
42360                             'type' : 'string'
42361                         },
42362                         {
42363                             'name' : 'dialCode',
42364                             'type' : 'string'
42365                         },
42366                         {
42367                             'name' : 'priority',
42368                             'type' : 'string'
42369                         },
42370                         {
42371                             'name' : 'areaCodes',
42372                             'type' : 'string'
42373                         }
42374                     ]
42375                 })
42376             });
42377             
42378             if(!this.preferedCountries) {
42379                 this.preferedCountries = [
42380                     'hk',
42381                     'gb',
42382                     'us'
42383                 ];
42384             }
42385             
42386             var p = this.preferedCountries.reverse();
42387             
42388             if(p) {
42389                 for (var i = 0; i < p.length; i++) {
42390                     for (var j = 0; j < this.allCountries.length; j++) {
42391                         if(this.allCountries[j].iso2 == p[i]) {
42392                             var t = this.allCountries[j];
42393                             this.allCountries.splice(j,1);
42394                             this.allCountries.unshift(t);
42395                         }
42396                     } 
42397                 }
42398             }
42399             
42400             this.store.proxy.data = {
42401                 success: true,
42402                 data: this.allCountries
42403             };
42404             
42405             return cfg;
42406         },
42407         
42408         initEvents : function()
42409         {
42410             this.createList();
42411             Roo.bootstrap.PhoneInput.superclass.initEvents.call(this);
42412             
42413             this.indicator = this.indicatorEl();
42414             this.flag = this.flagEl();
42415             this.dialCodeHolder = this.dialCodeHolderEl();
42416             
42417             this.trigger = this.el.select('div.flag-box',true).first();
42418             this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
42419             
42420             var _this = this;
42421             
42422             (function(){
42423                 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
42424                 _this.list.setWidth(lw);
42425             }).defer(100);
42426             
42427             this.list.on('mouseover', this.onViewOver, this);
42428             this.list.on('mousemove', this.onViewMove, this);
42429             this.inputEl().on("keyup", this.onKeyUp, this);
42430             this.inputEl().on("keypress", this.onKeyPress, this);
42431             
42432             this.tpl = '<li><a href="#"><div class="flag {iso2}"></div>{name} <span class="dial-code">+{dialCode}</span></a></li>';
42433
42434             this.view = new Roo.View(this.list, this.tpl, {
42435                 singleSelect:true, store: this.store, selectedClass: this.selectedClass
42436             });
42437             
42438             this.view.on('click', this.onViewClick, this);
42439             this.setValue(this.defaultDialCode);
42440         },
42441         
42442         onTriggerClick : function(e)
42443         {
42444             Roo.log('trigger click');
42445             if(this.disabled){
42446                 return;
42447             }
42448             
42449             if(this.isExpanded()){
42450                 this.collapse();
42451                 this.hasFocus = false;
42452             }else {
42453                 this.store.load({});
42454                 this.hasFocus = true;
42455                 this.expand();
42456             }
42457         },
42458         
42459         isExpanded : function()
42460         {
42461             return this.list.isVisible();
42462         },
42463         
42464         collapse : function()
42465         {
42466             if(!this.isExpanded()){
42467                 return;
42468             }
42469             this.list.hide();
42470             Roo.get(document).un('mousedown', this.collapseIf, this);
42471             Roo.get(document).un('mousewheel', this.collapseIf, this);
42472             this.fireEvent('collapse', this);
42473             this.validate();
42474         },
42475         
42476         expand : function()
42477         {
42478             Roo.log('expand');
42479
42480             if(this.isExpanded() || !this.hasFocus){
42481                 return;
42482             }
42483             
42484             var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
42485             this.list.setWidth(lw);
42486             
42487             this.list.show();
42488             this.restrictHeight();
42489             
42490             Roo.get(document).on('mousedown', this.collapseIf, this);
42491             Roo.get(document).on('mousewheel', this.collapseIf, this);
42492             
42493             this.fireEvent('expand', this);
42494         },
42495         
42496         restrictHeight : function()
42497         {
42498             this.list.alignTo(this.inputEl(), this.listAlign);
42499             this.list.alignTo(this.inputEl(), this.listAlign);
42500         },
42501         
42502         onViewOver : function(e, t)
42503         {
42504             if(this.inKeyMode){
42505                 return;
42506             }
42507             var item = this.view.findItemFromChild(t);
42508             
42509             if(item){
42510                 var index = this.view.indexOf(item);
42511                 this.select(index, false);
42512             }
42513         },
42514
42515         // private
42516         onViewClick : function(view, doFocus, el, e)
42517         {
42518             var index = this.view.getSelectedIndexes()[0];
42519             
42520             var r = this.store.getAt(index);
42521             
42522             if(r){
42523                 this.onSelect(r, index);
42524             }
42525             if(doFocus !== false && !this.blockFocus){
42526                 this.inputEl().focus();
42527             }
42528         },
42529         
42530         onViewMove : function(e, t)
42531         {
42532             this.inKeyMode = false;
42533         },
42534         
42535         select : function(index, scrollIntoView)
42536         {
42537             this.selectedIndex = index;
42538             this.view.select(index);
42539             if(scrollIntoView !== false){
42540                 var el = this.view.getNode(index);
42541                 if(el){
42542                     this.list.scrollChildIntoView(el, false);
42543                 }
42544             }
42545         },
42546         
42547         createList : function()
42548         {
42549             this.list = Roo.get(document.body).createChild({
42550                 tag: 'ul',
42551                 cls: 'typeahead typeahead-long dropdown-menu tel-list',
42552                 style: 'display:none'
42553             });
42554             
42555             this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
42556         },
42557         
42558         collapseIf : function(e)
42559         {
42560             var in_combo  = e.within(this.el);
42561             var in_list =  e.within(this.list);
42562             var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
42563             
42564             if (in_combo || in_list || is_list) {
42565                 return;
42566             }
42567             this.collapse();
42568         },
42569         
42570         onSelect : function(record, index)
42571         {
42572             if(this.fireEvent('beforeselect', this, record, index) !== false){
42573                 
42574                 this.setFlagClass(record.data.iso2);
42575                 this.setDialCode(record.data.dialCode);
42576                 this.hasFocus = false;
42577                 this.collapse();
42578                 this.fireEvent('select', this, record, index);
42579             }
42580         },
42581         
42582         flagEl : function()
42583         {
42584             var flag = this.el.select('div.flag',true).first();
42585             if(!flag){
42586                 return false;
42587             }
42588             return flag;
42589         },
42590         
42591         dialCodeHolderEl : function()
42592         {
42593             var d = this.el.select('input.dial-code-holder',true).first();
42594             if(!d){
42595                 return false;
42596             }
42597             return d;
42598         },
42599         
42600         setDialCode : function(v)
42601         {
42602             this.dialCodeHolder.dom.value = '+'+v;
42603         },
42604         
42605         setFlagClass : function(n)
42606         {
42607             this.flag.dom.className = 'flag '+n;
42608         },
42609         
42610         getValue : function()
42611         {
42612             var v = this.inputEl().getValue();
42613             if(this.dialCodeHolder) {
42614                 v = this.dialCodeHolder.dom.value+this.inputEl().getValue();
42615             }
42616             return v;
42617         },
42618         
42619         setValue : function(v)
42620         {
42621             var d = this.getDialCode(v);
42622             
42623             //invalid dial code
42624             if(v.length == 0 || !d || d.length == 0) {
42625                 if(this.rendered){
42626                     this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
42627                     this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
42628                 }
42629                 return;
42630             }
42631             
42632             //valid dial code
42633             this.setFlagClass(this.dialCodeMapping[d].iso2);
42634             this.setDialCode(d);
42635             this.inputEl().dom.value = v.replace('+'+d,'');
42636             this.hiddenEl().dom.value = this.getValue();
42637             
42638             this.validate();
42639         },
42640         
42641         getDialCode : function(v)
42642         {
42643             v = v ||  '';
42644             
42645             if (v.length == 0) {
42646                 return this.dialCodeHolder.dom.value;
42647             }
42648             
42649             var dialCode = "";
42650             if (v.charAt(0) != "+") {
42651                 return false;
42652             }
42653             var numericChars = "";
42654             for (var i = 1; i < v.length; i++) {
42655               var c = v.charAt(i);
42656               if (!isNaN(c)) {
42657                 numericChars += c;
42658                 if (this.dialCodeMapping[numericChars]) {
42659                   dialCode = v.substr(1, i);
42660                 }
42661                 if (numericChars.length == 4) {
42662                   break;
42663                 }
42664               }
42665             }
42666             return dialCode;
42667         },
42668         
42669         reset : function()
42670         {
42671             this.setValue(this.defaultDialCode);
42672             this.validate();
42673         },
42674         
42675         hiddenEl : function()
42676         {
42677             return this.el.select('input.hidden-tel-input',true).first();
42678         },
42679         
42680         // after setting val
42681         onKeyUp : function(e){
42682             this.setValue(this.getValue());
42683         },
42684         
42685         onKeyPress : function(e){
42686             if(this.allowed.indexOf(String.fromCharCode(e.getCharCode())) === -1){
42687                 e.stopEvent();
42688             }
42689         }
42690         
42691 });
42692 /**
42693  * @class Roo.bootstrap.MoneyField
42694  * @extends Roo.bootstrap.ComboBox
42695  * Bootstrap MoneyField class
42696  * 
42697  * @constructor
42698  * Create a new MoneyField.
42699  * @param {Object} config Configuration options
42700  */
42701
42702 Roo.bootstrap.MoneyField = function(config) {
42703     
42704     Roo.bootstrap.MoneyField.superclass.constructor.call(this, config);
42705     
42706 };
42707
42708 Roo.extend(Roo.bootstrap.MoneyField, Roo.bootstrap.ComboBox, {
42709     
42710     /**
42711      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
42712      */
42713     allowDecimals : true,
42714     /**
42715      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
42716      */
42717     decimalSeparator : ".",
42718     /**
42719      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
42720      */
42721     decimalPrecision : 0,
42722     /**
42723      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
42724      */
42725     allowNegative : true,
42726     /**
42727      * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
42728      */
42729     allowZero: true,
42730     /**
42731      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
42732      */
42733     minValue : Number.NEGATIVE_INFINITY,
42734     /**
42735      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
42736      */
42737     maxValue : Number.MAX_VALUE,
42738     /**
42739      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
42740      */
42741     minText : "The minimum value for this field is {0}",
42742     /**
42743      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
42744      */
42745     maxText : "The maximum value for this field is {0}",
42746     /**
42747      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
42748      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
42749      */
42750     nanText : "{0} is not a valid number",
42751     /**
42752      * @cfg {Boolean} castInt (true|false) cast int if true (defalut true)
42753      */
42754     castInt : true,
42755     /**
42756      * @cfg {String} defaults currency of the MoneyField
42757      * value should be in lkey
42758      */
42759     defaultCurrency : false,
42760     /**
42761      * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
42762      */
42763     thousandsDelimiter : false,
42764     /**
42765      * @cfg {Number} max_length Maximum input field length allowed (defaults to Number.MAX_VALUE)
42766      */
42767     max_length: false,
42768     
42769     inputlg : 9,
42770     inputmd : 9,
42771     inputsm : 9,
42772     inputxs : 6,
42773     
42774     store : false,
42775     
42776     getAutoCreate : function()
42777     {
42778         var align = this.labelAlign || this.parentLabelAlign();
42779         
42780         var id = Roo.id();
42781
42782         var cfg = {
42783             cls: 'form-group',
42784             cn: []
42785         };
42786
42787         var input =  {
42788             tag: 'input',
42789             id : id,
42790             cls : 'form-control roo-money-amount-input',
42791             autocomplete: 'new-password'
42792         };
42793         
42794         var hiddenInput = {
42795             tag: 'input',
42796             type: 'hidden',
42797             id: Roo.id(),
42798             cls: 'hidden-number-input'
42799         };
42800         
42801         if(this.max_length) {
42802             input.maxlength = this.max_length; 
42803         }
42804         
42805         if (this.name) {
42806             hiddenInput.name = this.name;
42807         }
42808
42809         if (this.disabled) {
42810             input.disabled = true;
42811         }
42812
42813         var clg = 12 - this.inputlg;
42814         var cmd = 12 - this.inputmd;
42815         var csm = 12 - this.inputsm;
42816         var cxs = 12 - this.inputxs;
42817         
42818         var container = {
42819             tag : 'div',
42820             cls : 'row roo-money-field',
42821             cn : [
42822                 {
42823                     tag : 'div',
42824                     cls : 'roo-money-currency column col-lg-' + clg + ' col-md-' + cmd + ' col-sm-' + csm + ' col-xs-' + cxs,
42825                     cn : [
42826                         {
42827                             tag : 'div',
42828                             cls: 'roo-select2-container input-group',
42829                             cn: [
42830                                 {
42831                                     tag : 'input',
42832                                     cls : 'form-control roo-money-currency-input',
42833                                     autocomplete: 'new-password',
42834                                     readOnly : 1,
42835                                     name : this.currencyName
42836                                 },
42837                                 {
42838                                     tag :'span',
42839                                     cls : 'input-group-addon',
42840                                     cn : [
42841                                         {
42842                                             tag: 'span',
42843                                             cls: 'caret'
42844                                         }
42845                                     ]
42846                                 }
42847                             ]
42848                         }
42849                     ]
42850                 },
42851                 {
42852                     tag : 'div',
42853                     cls : 'roo-money-amount column col-lg-' + this.inputlg + ' col-md-' + this.inputmd + ' col-sm-' + this.inputsm + ' col-xs-' + this.inputxs,
42854                     cn : [
42855                         {
42856                             tag: 'div',
42857                             cls: this.hasFeedback ? 'has-feedback' : '',
42858                             cn: [
42859                                 input
42860                             ]
42861                         }
42862                     ]
42863                 }
42864             ]
42865             
42866         };
42867         
42868         if (this.fieldLabel.length) {
42869             var indicator = {
42870                 tag: 'i',
42871                 tooltip: 'This field is required'
42872             };
42873
42874             var label = {
42875                 tag: 'label',
42876                 'for':  id,
42877                 cls: 'control-label',
42878                 cn: []
42879             };
42880
42881             var label_text = {
42882                 tag: 'span',
42883                 html: this.fieldLabel
42884             };
42885
42886             indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
42887             label.cn = [
42888                 indicator,
42889                 label_text
42890             ];
42891
42892             if(this.indicatorpos == 'right') {
42893                 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
42894                 label.cn = [
42895                     label_text,
42896                     indicator
42897                 ];
42898             }
42899
42900             if(align == 'left') {
42901                 container = {
42902                     tag: 'div',
42903                     cn: [
42904                         container
42905                     ]
42906                 };
42907
42908                 if(this.labelWidth > 12){
42909                     label.style = "width: " + this.labelWidth + 'px';
42910                 }
42911                 if(this.labelWidth < 13 && this.labelmd == 0){
42912                     this.labelmd = this.labelWidth;
42913                 }
42914                 if(this.labellg > 0){
42915                     label.cls += ' col-lg-' + this.labellg;
42916                     input.cls += ' col-lg-' + (12 - this.labellg);
42917                 }
42918                 if(this.labelmd > 0){
42919                     label.cls += ' col-md-' + this.labelmd;
42920                     container.cls += ' col-md-' + (12 - this.labelmd);
42921                 }
42922                 if(this.labelsm > 0){
42923                     label.cls += ' col-sm-' + this.labelsm;
42924                     container.cls += ' col-sm-' + (12 - this.labelsm);
42925                 }
42926                 if(this.labelxs > 0){
42927                     label.cls += ' col-xs-' + this.labelxs;
42928                     container.cls += ' col-xs-' + (12 - this.labelxs);
42929                 }
42930             }
42931         }
42932
42933         cfg.cn = [
42934             label,
42935             container,
42936             hiddenInput
42937         ];
42938         
42939         var settings = this;
42940
42941         ['xs','sm','md','lg'].map(function(size){
42942             if (settings[size]) {
42943                 cfg.cls += ' col-' + size + '-' + settings[size];
42944             }
42945         });
42946         
42947         return cfg;
42948     },
42949     
42950     initEvents : function()
42951     {
42952         this.indicator = this.indicatorEl();
42953         
42954         this.initCurrencyEvent();
42955         
42956         this.initNumberEvent();
42957     },
42958     
42959     initCurrencyEvent : function()
42960     {
42961         if (!this.store) {
42962             throw "can not find store for combo";
42963         }
42964         
42965         this.store = Roo.factory(this.store, Roo.data);
42966         this.store.parent = this;
42967         
42968         this.createList();
42969         
42970         this.triggerEl = this.el.select('.input-group-addon', true).first();
42971         
42972         this.triggerEl.on("click", this.onTriggerClick, this, { preventDefault : true });
42973         
42974         var _this = this;
42975         
42976         (function(){
42977             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
42978             _this.list.setWidth(lw);
42979         }).defer(100);
42980         
42981         this.list.on('mouseover', this.onViewOver, this);
42982         this.list.on('mousemove', this.onViewMove, this);
42983         this.list.on('scroll', this.onViewScroll, this);
42984         
42985         if(!this.tpl){
42986             this.tpl = '<li><a href="#">{' + this.currencyField + '}</a></li>';
42987         }
42988         
42989         this.view = new Roo.View(this.list, this.tpl, {
42990             singleSelect:true, store: this.store, selectedClass: this.selectedClass
42991         });
42992         
42993         this.view.on('click', this.onViewClick, this);
42994         
42995         this.store.on('beforeload', this.onBeforeLoad, this);
42996         this.store.on('load', this.onLoad, this);
42997         this.store.on('loadexception', this.onLoadException, this);
42998         
42999         this.keyNav = new Roo.KeyNav(this.currencyEl(), {
43000             "up" : function(e){
43001                 this.inKeyMode = true;
43002                 this.selectPrev();
43003             },
43004
43005             "down" : function(e){
43006                 if(!this.isExpanded()){
43007                     this.onTriggerClick();
43008                 }else{
43009                     this.inKeyMode = true;
43010                     this.selectNext();
43011                 }
43012             },
43013
43014             "enter" : function(e){
43015                 this.collapse();
43016                 
43017                 if(this.fireEvent("specialkey", this, e)){
43018                     this.onViewClick(false);
43019                 }
43020                 
43021                 return true;
43022             },
43023
43024             "esc" : function(e){
43025                 this.collapse();
43026             },
43027
43028             "tab" : function(e){
43029                 this.collapse();
43030                 
43031                 if(this.fireEvent("specialkey", this, e)){
43032                     this.onViewClick(false);
43033                 }
43034                 
43035                 return true;
43036             },
43037
43038             scope : this,
43039
43040             doRelay : function(foo, bar, hname){
43041                 if(hname == 'down' || this.scope.isExpanded()){
43042                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
43043                 }
43044                 return true;
43045             },
43046
43047             forceKeyDown: true
43048         });
43049         
43050         this.currencyEl().on("click", this.onTriggerClick, this, { preventDefault : true });
43051         
43052     },
43053     
43054     initNumberEvent : function(e)
43055     {
43056         this.inputEl().on("keydown" , this.fireKey,  this);
43057         this.inputEl().on("focus", this.onFocus,  this);
43058         this.inputEl().on("blur", this.onBlur,  this);
43059         
43060         this.inputEl().relayEvent('keyup', this);
43061         
43062         if(this.indicator){
43063             this.indicator.addClass('invisible');
43064         }
43065  
43066         this.originalValue = this.getValue();
43067         
43068         if(this.validationEvent == 'keyup'){
43069             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
43070             this.inputEl().on('keyup', this.filterValidation, this);
43071         }
43072         else if(this.validationEvent !== false){
43073             this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
43074         }
43075         
43076         if(this.selectOnFocus){
43077             this.on("focus", this.preFocus, this);
43078             
43079         }
43080         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
43081             this.inputEl().on("keypress", this.filterKeys, this);
43082         } else {
43083             this.inputEl().relayEvent('keypress', this);
43084         }
43085         
43086         var allowed = "0123456789";
43087         
43088         if(this.allowDecimals){
43089             allowed += this.decimalSeparator;
43090         }
43091         
43092         if(this.allowNegative){
43093             allowed += "-";
43094         }
43095         
43096         if(this.thousandsDelimiter) {
43097             allowed += ",";
43098         }
43099         
43100         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
43101         
43102         var keyPress = function(e){
43103             
43104             var k = e.getKey();
43105             
43106             var c = e.getCharCode();
43107             
43108             if(
43109                     (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
43110                     allowed.indexOf(String.fromCharCode(c)) === -1
43111             ){
43112                 e.stopEvent();
43113                 return;
43114             }
43115             
43116             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
43117                 return;
43118             }
43119             
43120             if(allowed.indexOf(String.fromCharCode(c)) === -1){
43121                 e.stopEvent();
43122             }
43123         };
43124         
43125         this.inputEl().on("keypress", keyPress, this);
43126         
43127     },
43128     
43129     onTriggerClick : function(e)
43130     {   
43131         if(this.disabled){
43132             return;
43133         }
43134         
43135         this.page = 0;
43136         this.loadNext = false;
43137         
43138         if(this.isExpanded()){
43139             this.collapse();
43140             return;
43141         }
43142         
43143         this.hasFocus = true;
43144         
43145         if(this.triggerAction == 'all') {
43146             this.doQuery(this.allQuery, true);
43147             return;
43148         }
43149         
43150         this.doQuery(this.getRawValue());
43151     },
43152     
43153     getCurrency : function()
43154     {   
43155         var v = this.currencyEl().getValue();
43156         
43157         return v;
43158     },
43159     
43160     restrictHeight : function()
43161     {
43162         this.list.alignTo(this.currencyEl(), this.listAlign);
43163         this.list.alignTo(this.currencyEl(), this.listAlign);
43164     },
43165     
43166     onViewClick : function(view, doFocus, el, e)
43167     {
43168         var index = this.view.getSelectedIndexes()[0];
43169         
43170         var r = this.store.getAt(index);
43171         
43172         if(r){
43173             this.onSelect(r, index);
43174         }
43175     },
43176     
43177     onSelect : function(record, index){
43178         
43179         if(this.fireEvent('beforeselect', this, record, index) !== false){
43180         
43181             this.setFromCurrencyData(index > -1 ? record.data : false);
43182             
43183             this.collapse();
43184             
43185             this.fireEvent('select', this, record, index);
43186         }
43187     },
43188     
43189     setFromCurrencyData : function(o)
43190     {
43191         var currency = '';
43192         
43193         this.lastCurrency = o;
43194         
43195         if (this.currencyField) {
43196             currency = !o || typeof(o[this.currencyField]) == 'undefined' ? '' : o[this.currencyField];
43197         } else {
43198             Roo.log('no  currencyField value set for '+ (this.name ? this.name : this.id));
43199         }
43200         
43201         this.lastSelectionText = currency;
43202         
43203         //setting default currency
43204         if(o[this.currencyField] * 1 == 0 && this.defaultCurrency) {
43205             this.setCurrency(this.defaultCurrency);
43206             return;
43207         }
43208         
43209         this.setCurrency(currency);
43210     },
43211     
43212     setFromData : function(o)
43213     {
43214         var c = {};
43215         
43216         c[this.currencyField] = !o || typeof(o[this.currencyName]) == 'undefined' ? '' : o[this.currencyName];
43217         
43218         this.setFromCurrencyData(c);
43219         
43220         var value = '';
43221         
43222         if (this.name) {
43223             value = !o || typeof(o[this.name]) == 'undefined' ? '' : o[this.name];
43224         } else {
43225             Roo.log('no value set for '+ (this.name ? this.name : this.id));
43226         }
43227         
43228         this.setValue(value);
43229         
43230     },
43231     
43232     setCurrency : function(v)
43233     {   
43234         this.currencyValue = v;
43235         
43236         if(this.rendered){
43237             this.currencyEl().dom.value = (v === null || v === undefined ? '' : v);
43238             this.validate();
43239         }
43240     },
43241     
43242     setValue : function(v)
43243     {
43244         v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
43245         
43246         this.value = v;
43247         
43248         if(this.rendered){
43249             
43250             this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
43251             
43252             this.inputEl().dom.value = (v == '') ? '' :
43253                 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
43254             
43255             if(!this.allowZero && v === '0') {
43256                 this.hiddenEl().dom.value = '';
43257                 this.inputEl().dom.value = '';
43258             }
43259             
43260             this.validate();
43261         }
43262     },
43263     
43264     getRawValue : function()
43265     {
43266         var v = this.inputEl().getValue();
43267         
43268         return v;
43269     },
43270     
43271     getValue : function()
43272     {
43273         return this.fixPrecision(this.parseValue(this.getRawValue()));
43274     },
43275     
43276     parseValue : function(value)
43277     {
43278         if(this.thousandsDelimiter) {
43279             value += "";
43280             r = new RegExp(",", "g");
43281             value = value.replace(r, "");
43282         }
43283         
43284         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
43285         return isNaN(value) ? '' : value;
43286         
43287     },
43288     
43289     fixPrecision : function(value)
43290     {
43291         if(this.thousandsDelimiter) {
43292             value += "";
43293             r = new RegExp(",", "g");
43294             value = value.replace(r, "");
43295         }
43296         
43297         var nan = isNaN(value);
43298         
43299         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
43300             return nan ? '' : value;
43301         }
43302         return parseFloat(value).toFixed(this.decimalPrecision);
43303     },
43304     
43305     decimalPrecisionFcn : function(v)
43306     {
43307         return Math.floor(v);
43308     },
43309     
43310     validateValue : function(value)
43311     {
43312         if(!Roo.bootstrap.MoneyField.superclass.validateValue.call(this, value)){
43313             return false;
43314         }
43315         
43316         var num = this.parseValue(value);
43317         
43318         if(isNaN(num)){
43319             this.markInvalid(String.format(this.nanText, value));
43320             return false;
43321         }
43322         
43323         if(num < this.minValue){
43324             this.markInvalid(String.format(this.minText, this.minValue));
43325             return false;
43326         }
43327         
43328         if(num > this.maxValue){
43329             this.markInvalid(String.format(this.maxText, this.maxValue));
43330             return false;
43331         }
43332         
43333         return true;
43334     },
43335     
43336     validate : function()
43337     {
43338         if(this.disabled || this.allowBlank){
43339             this.markValid();
43340             return true;
43341         }
43342         
43343         var currency = this.getCurrency();
43344         
43345         if(this.validateValue(this.getRawValue()) && currency.length){
43346             this.markValid();
43347             return true;
43348         }
43349         
43350         this.markInvalid();
43351         return false;
43352     },
43353     
43354     getName: function()
43355     {
43356         return this.name;
43357     },
43358     
43359     beforeBlur : function()
43360     {
43361         if(!this.castInt){
43362             return;
43363         }
43364         
43365         var v = this.parseValue(this.getRawValue());
43366         
43367         if(v || v == 0){
43368             this.setValue(v);
43369         }
43370     },
43371     
43372     onBlur : function()
43373     {
43374         this.beforeBlur();
43375         
43376         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
43377             //this.el.removeClass(this.focusClass);
43378         }
43379         
43380         this.hasFocus = false;
43381         
43382         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
43383             this.validate();
43384         }
43385         
43386         var v = this.getValue();
43387         
43388         if(String(v) !== String(this.startValue)){
43389             this.fireEvent('change', this, v, this.startValue);
43390         }
43391         
43392         this.fireEvent("blur", this);
43393     },
43394     
43395     inputEl : function()
43396     {
43397         return this.el.select('.roo-money-amount-input', true).first();
43398     },
43399     
43400     currencyEl : function()
43401     {
43402         return this.el.select('.roo-money-currency-input', true).first();
43403     },
43404     
43405     hiddenEl : function()
43406     {
43407         return this.el.select('input.hidden-number-input',true).first();
43408     }
43409     
43410 });/**
43411  * @class Roo.bootstrap.BezierSignature
43412  * @extends Roo.bootstrap.Component
43413  * Bootstrap BezierSignature class
43414  * This script refer to:
43415  *    Title: Signature Pad
43416  *    Author: szimek
43417  *    Availability: https://github.com/szimek/signature_pad
43418  *
43419  * @constructor
43420  * Create a new BezierSignature
43421  * @param {Object} config The config object
43422  */
43423
43424 Roo.bootstrap.BezierSignature = function(config){
43425     Roo.bootstrap.BezierSignature.superclass.constructor.call(this, config);
43426     this.addEvents({
43427         "resize" : true
43428     });
43429 };
43430
43431 Roo.extend(Roo.bootstrap.BezierSignature, Roo.bootstrap.Component,
43432 {
43433      
43434     curve_data: [],
43435     
43436     is_empty: true,
43437     
43438     mouse_btn_down: true,
43439     
43440     /**
43441      * @cfg {int} canvas height
43442      */
43443     canvas_height: '200px',
43444     
43445     /**
43446      * @cfg {float|function} Radius of a single dot.
43447      */ 
43448     dot_size: false,
43449     
43450     /**
43451      * @cfg {float} Minimum width of a line. Defaults to 0.5.
43452      */
43453     min_width: 0.5,
43454     
43455     /**
43456      * @cfg {float} Maximum width of a line. Defaults to 2.5.
43457      */
43458     max_width: 2.5,
43459     
43460     /**
43461      * @cfg {integer} Draw the next point at most once per every x milliseconds. Set it to 0 to turn off throttling. Defaults to 16.
43462      */
43463     throttle: 16,
43464     
43465     /**
43466      * @cfg {integer} Add the next point only if the previous one is farther than x pixels. Defaults to 5.
43467      */
43468     min_distance: 5,
43469     
43470     /**
43471      * @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.
43472      */
43473     bg_color: 'rgba(0, 0, 0, 0)',
43474     
43475     /**
43476      * @cfg {string} Color used to draw the lines. Can be any color format accepted by context.fillStyle. Defaults to "black".
43477      */
43478     dot_color: 'black',
43479     
43480     /**
43481      * @cfg {float} Weight used to modify new velocity based on the previous velocity. Defaults to 0.7.
43482      */ 
43483     velocity_filter_weight: 0.7,
43484     
43485     /**
43486      * @cfg {function} Callback when stroke begin. 
43487      */
43488     onBegin: false,
43489     
43490     /**
43491      * @cfg {function} Callback when stroke end.
43492      */
43493     onEnd: false,
43494     
43495     getAutoCreate : function()
43496     {
43497         var cls = 'roo-signature column';
43498         
43499         if(this.cls){
43500             cls += ' ' + this.cls;
43501         }
43502         
43503         var col_sizes = [
43504             'lg',
43505             'md',
43506             'sm',
43507             'xs'
43508         ];
43509         
43510         for(var i = 0; i < col_sizes.length; i++) {
43511             if(this[col_sizes[i]]) {
43512                 cls += " col-"+col_sizes[i]+"-"+this[col_sizes[i]];
43513             }
43514         }
43515         
43516         var cfg = {
43517             tag: 'div',
43518             cls: cls,
43519             cn: [
43520                 {
43521                     tag: 'div',
43522                     cls: 'roo-signature-body',
43523                     cn: [
43524                         {
43525                             tag: 'canvas',
43526                             cls: 'roo-signature-body-canvas',
43527                             height: this.canvas_height,
43528                             width: this.canvas_width
43529                         }
43530                     ]
43531                 },
43532                 {
43533                     tag: 'input',
43534                     type: 'file',
43535                     style: 'display: none'
43536                 }
43537             ]
43538         };
43539         
43540         return cfg;
43541     },
43542     
43543     initEvents: function() 
43544     {
43545         Roo.bootstrap.BezierSignature.superclass.initEvents.call(this);
43546         
43547         var canvas = this.canvasEl();
43548         
43549         // mouse && touch event swapping...
43550         canvas.dom.style.touchAction = 'none';
43551         canvas.dom.style.msTouchAction = 'none';
43552         
43553         this.mouse_btn_down = false;
43554         canvas.on('mousedown', this._handleMouseDown, this);
43555         canvas.on('mousemove', this._handleMouseMove, this);
43556         Roo.select('html').first().on('mouseup', this._handleMouseUp, this);
43557         
43558         if (window.PointerEvent) {
43559             canvas.on('pointerdown', this._handleMouseDown, this);
43560             canvas.on('pointermove', this._handleMouseMove, this);
43561             Roo.select('html').first().on('pointerup', this._handleMouseUp, this);
43562         }
43563         
43564         if ('ontouchstart' in window) {
43565             canvas.on('touchstart', this._handleTouchStart, this);
43566             canvas.on('touchmove', this._handleTouchMove, this);
43567             canvas.on('touchend', this._handleTouchEnd, this);
43568         }
43569         
43570         Roo.EventManager.onWindowResize(this.resize, this, true);
43571         
43572         // file input event
43573         this.fileEl().on('change', this.uploadImage, this);
43574         
43575         this.clear();
43576         
43577         this.resize();
43578     },
43579     
43580     resize: function(){
43581         
43582         var canvas = this.canvasEl().dom;
43583         var ctx = this.canvasElCtx();
43584         var img_data = false;
43585         
43586         if(canvas.width > 0) {
43587             var img_data = ctx.getImageData(0, 0, canvas.width, canvas.height);
43588         }
43589         // setting canvas width will clean img data
43590         canvas.width = 0;
43591         
43592         var style = window.getComputedStyle ? 
43593             getComputedStyle(this.el.dom, null) : this.el.dom.currentStyle;
43594             
43595         var padding_left = parseInt(style.paddingLeft) || 0;
43596         var padding_right = parseInt(style.paddingRight) || 0;
43597         
43598         canvas.width = this.el.dom.clientWidth - padding_left - padding_right;
43599         
43600         if(img_data) {
43601             ctx.putImageData(img_data, 0, 0);
43602         }
43603     },
43604     
43605     _handleMouseDown: function(e)
43606     {
43607         if (e.browserEvent.which === 1) {
43608             this.mouse_btn_down = true;
43609             this.strokeBegin(e);
43610         }
43611     },
43612     
43613     _handleMouseMove: function (e)
43614     {
43615         if (this.mouse_btn_down) {
43616             this.strokeMoveUpdate(e);
43617         }
43618     },
43619     
43620     _handleMouseUp: function (e)
43621     {
43622         if (e.browserEvent.which === 1 && this.mouse_btn_down) {
43623             this.mouse_btn_down = false;
43624             this.strokeEnd(e);
43625         }
43626     },
43627     
43628     _handleTouchStart: function (e) {
43629         
43630         e.preventDefault();
43631         if (e.browserEvent.targetTouches.length === 1) {
43632             // var touch = e.browserEvent.changedTouches[0];
43633             // this.strokeBegin(touch);
43634             
43635              this.strokeBegin(e); // assume e catching the correct xy...
43636         }
43637     },
43638     
43639     _handleTouchMove: function (e) {
43640         e.preventDefault();
43641         // var touch = event.targetTouches[0];
43642         // _this._strokeMoveUpdate(touch);
43643         this.strokeMoveUpdate(e);
43644     },
43645     
43646     _handleTouchEnd: function (e) {
43647         var wasCanvasTouched = e.target === this.canvasEl().dom;
43648         if (wasCanvasTouched) {
43649             e.preventDefault();
43650             // var touch = event.changedTouches[0];
43651             // _this._strokeEnd(touch);
43652             this.strokeEnd(e);
43653         }
43654     },
43655     
43656     reset: function () {
43657         this._lastPoints = [];
43658         this._lastVelocity = 0;
43659         this._lastWidth = (this.min_width + this.max_width) / 2;
43660         this.canvasElCtx().fillStyle = this.dot_color;
43661     },
43662     
43663     strokeMoveUpdate: function(e)
43664     {
43665         this.strokeUpdate(e);
43666         
43667         if (this.throttle) {
43668             this.throttleStroke(this.strokeUpdate, this.throttle);
43669         }
43670         else {
43671             this.strokeUpdate(e);
43672         }
43673     },
43674     
43675     strokeBegin: function(e)
43676     {
43677         var newPointGroup = {
43678             color: this.dot_color,
43679             points: []
43680         };
43681         
43682         if (typeof this.onBegin === 'function') {
43683             this.onBegin(e);
43684         }
43685         
43686         this.curve_data.push(newPointGroup);
43687         this.reset();
43688         this.strokeUpdate(e);
43689     },
43690     
43691     strokeUpdate: function(e)
43692     {
43693         var rect = this.canvasEl().dom.getBoundingClientRect();
43694         var point = new this.Point(e.xy[0] - rect.left, e.xy[1] - rect.top, new Date().getTime());
43695         var lastPointGroup = this.curve_data[this.curve_data.length - 1];
43696         var lastPoints = lastPointGroup.points;
43697         var lastPoint = lastPoints.length > 0 && lastPoints[lastPoints.length - 1];
43698         var isLastPointTooClose = lastPoint
43699             ? point.distanceTo(lastPoint) <= this.min_distance
43700             : false;
43701         var color = lastPointGroup.color;
43702         if (!lastPoint || !(lastPoint && isLastPointTooClose)) {
43703             var curve = this.addPoint(point);
43704             if (!lastPoint) {
43705                 this.drawDot({color: color, point: point});
43706             }
43707             else if (curve) {
43708                 this.drawCurve({color: color, curve: curve});
43709             }
43710             lastPoints.push({
43711                 time: point.time,
43712                 x: point.x,
43713                 y: point.y
43714             });
43715         }
43716     },
43717     
43718     strokeEnd: function(e)
43719     {
43720         this.strokeUpdate(e);
43721         if (typeof this.onEnd === 'function') {
43722             this.onEnd(e);
43723         }
43724     },
43725     
43726     addPoint:  function (point) {
43727         var _lastPoints = this._lastPoints;
43728         _lastPoints.push(point);
43729         if (_lastPoints.length > 2) {
43730             if (_lastPoints.length === 3) {
43731                 _lastPoints.unshift(_lastPoints[0]);
43732             }
43733             var widths = this.calculateCurveWidths(_lastPoints[1], _lastPoints[2]);
43734             var curve = this.Bezier.fromPoints(_lastPoints, widths, this);
43735             _lastPoints.shift();
43736             return curve;
43737         }
43738         return null;
43739     },
43740     
43741     calculateCurveWidths: function (startPoint, endPoint) {
43742         var velocity = this.velocity_filter_weight * endPoint.velocityFrom(startPoint) +
43743             (1 - this.velocity_filter_weight) * this._lastVelocity;
43744
43745         var newWidth = Math.max(this.max_width / (velocity + 1), this.min_width);
43746         var widths = {
43747             end: newWidth,
43748             start: this._lastWidth
43749         };
43750         
43751         this._lastVelocity = velocity;
43752         this._lastWidth = newWidth;
43753         return widths;
43754     },
43755     
43756     drawDot: function (_a) {
43757         var color = _a.color, point = _a.point;
43758         var ctx = this.canvasElCtx();
43759         var width = typeof this.dot_size === 'function' ? this.dot_size() : this.dot_size;
43760         ctx.beginPath();
43761         this.drawCurveSegment(point.x, point.y, width);
43762         ctx.closePath();
43763         ctx.fillStyle = color;
43764         ctx.fill();
43765     },
43766     
43767     drawCurve: function (_a) {
43768         var color = _a.color, curve = _a.curve;
43769         var ctx = this.canvasElCtx();
43770         var widthDelta = curve.endWidth - curve.startWidth;
43771         var drawSteps = Math.floor(curve.length()) * 2;
43772         ctx.beginPath();
43773         ctx.fillStyle = color;
43774         for (var i = 0; i < drawSteps; i += 1) {
43775         var t = i / drawSteps;
43776         var tt = t * t;
43777         var ttt = tt * t;
43778         var u = 1 - t;
43779         var uu = u * u;
43780         var uuu = uu * u;
43781         var x = uuu * curve.startPoint.x;
43782         x += 3 * uu * t * curve.control1.x;
43783         x += 3 * u * tt * curve.control2.x;
43784         x += ttt * curve.endPoint.x;
43785         var y = uuu * curve.startPoint.y;
43786         y += 3 * uu * t * curve.control1.y;
43787         y += 3 * u * tt * curve.control2.y;
43788         y += ttt * curve.endPoint.y;
43789         var width = curve.startWidth + ttt * widthDelta;
43790         this.drawCurveSegment(x, y, width);
43791         }
43792         ctx.closePath();
43793         ctx.fill();
43794     },
43795     
43796     drawCurveSegment: function (x, y, width) {
43797         var ctx = this.canvasElCtx();
43798         ctx.moveTo(x, y);
43799         ctx.arc(x, y, width, 0, 2 * Math.PI, false);
43800         this.is_empty = false;
43801     },
43802     
43803     clear: function()
43804     {
43805         var ctx = this.canvasElCtx();
43806         var canvas = this.canvasEl().dom;
43807         ctx.fillStyle = this.bg_color;
43808         ctx.clearRect(0, 0, canvas.width, canvas.height);
43809         ctx.fillRect(0, 0, canvas.width, canvas.height);
43810         this.curve_data = [];
43811         this.reset();
43812         this.is_empty = true;
43813     },
43814     
43815     fileEl: function()
43816     {
43817         return  this.el.select('input',true).first();
43818     },
43819     
43820     canvasEl: function()
43821     {
43822         return this.el.select('canvas',true).first();
43823     },
43824     
43825     canvasElCtx: function()
43826     {
43827         return this.el.select('canvas',true).first().dom.getContext('2d');
43828     },
43829     
43830     getImage: function(type)
43831     {
43832         if(this.is_empty) {
43833             return false;
43834         }
43835         
43836         // encryption ?
43837         return this.canvasEl().dom.toDataURL('image/'+type, 1);
43838     },
43839     
43840     drawFromImage: function(img_src)
43841     {
43842         var img = new Image();
43843         
43844         img.onload = function(){
43845             this.canvasElCtx().drawImage(img, 0, 0);
43846         }.bind(this);
43847         
43848         img.src = img_src;
43849         
43850         this.is_empty = false;
43851     },
43852     
43853     selectImage: function()
43854     {
43855         this.fileEl().dom.click();
43856     },
43857     
43858     uploadImage: function(e)
43859     {
43860         var reader = new FileReader();
43861         
43862         reader.onload = function(e){
43863             var img = new Image();
43864             img.onload = function(){
43865                 this.reset();
43866                 this.canvasElCtx().drawImage(img, 0, 0);
43867             }.bind(this);
43868             img.src = e.target.result;
43869         }.bind(this);
43870         
43871         reader.readAsDataURL(e.target.files[0]);
43872     },
43873     
43874     // Bezier Point Constructor
43875     Point: (function () {
43876         function Point(x, y, time) {
43877             this.x = x;
43878             this.y = y;
43879             this.time = time || Date.now();
43880         }
43881         Point.prototype.distanceTo = function (start) {
43882             return Math.sqrt(Math.pow(this.x - start.x, 2) + Math.pow(this.y - start.y, 2));
43883         };
43884         Point.prototype.equals = function (other) {
43885             return this.x === other.x && this.y === other.y && this.time === other.time;
43886         };
43887         Point.prototype.velocityFrom = function (start) {
43888             return this.time !== start.time
43889             ? this.distanceTo(start) / (this.time - start.time)
43890             : 0;
43891         };
43892         return Point;
43893     }()),
43894     
43895     
43896     // Bezier Constructor
43897     Bezier: (function () {
43898         function Bezier(startPoint, control2, control1, endPoint, startWidth, endWidth) {
43899             this.startPoint = startPoint;
43900             this.control2 = control2;
43901             this.control1 = control1;
43902             this.endPoint = endPoint;
43903             this.startWidth = startWidth;
43904             this.endWidth = endWidth;
43905         }
43906         Bezier.fromPoints = function (points, widths, scope) {
43907             var c2 = this.calculateControlPoints(points[0], points[1], points[2], scope).c2;
43908             var c3 = this.calculateControlPoints(points[1], points[2], points[3], scope).c1;
43909             return new Bezier(points[1], c2, c3, points[2], widths.start, widths.end);
43910         };
43911         Bezier.calculateControlPoints = function (s1, s2, s3, scope) {
43912             var dx1 = s1.x - s2.x;
43913             var dy1 = s1.y - s2.y;
43914             var dx2 = s2.x - s3.x;
43915             var dy2 = s2.y - s3.y;
43916             var m1 = { x: (s1.x + s2.x) / 2.0, y: (s1.y + s2.y) / 2.0 };
43917             var m2 = { x: (s2.x + s3.x) / 2.0, y: (s2.y + s3.y) / 2.0 };
43918             var l1 = Math.sqrt(dx1 * dx1 + dy1 * dy1);
43919             var l2 = Math.sqrt(dx2 * dx2 + dy2 * dy2);
43920             var dxm = m1.x - m2.x;
43921             var dym = m1.y - m2.y;
43922             var k = l2 / (l1 + l2);
43923             var cm = { x: m2.x + dxm * k, y: m2.y + dym * k };
43924             var tx = s2.x - cm.x;
43925             var ty = s2.y - cm.y;
43926             return {
43927                 c1: new scope.Point(m1.x + tx, m1.y + ty),
43928                 c2: new scope.Point(m2.x + tx, m2.y + ty)
43929             };
43930         };
43931         Bezier.prototype.length = function () {
43932             var steps = 10;
43933             var length = 0;
43934             var px;
43935             var py;
43936             for (var i = 0; i <= steps; i += 1) {
43937                 var t = i / steps;
43938                 var cx = this.point(t, this.startPoint.x, this.control1.x, this.control2.x, this.endPoint.x);
43939                 var cy = this.point(t, this.startPoint.y, this.control1.y, this.control2.y, this.endPoint.y);
43940                 if (i > 0) {
43941                     var xdiff = cx - px;
43942                     var ydiff = cy - py;
43943                     length += Math.sqrt(xdiff * xdiff + ydiff * ydiff);
43944                 }
43945                 px = cx;
43946                 py = cy;
43947             }
43948             return length;
43949         };
43950         Bezier.prototype.point = function (t, start, c1, c2, end) {
43951             return (start * (1.0 - t) * (1.0 - t) * (1.0 - t))
43952             + (3.0 * c1 * (1.0 - t) * (1.0 - t) * t)
43953             + (3.0 * c2 * (1.0 - t) * t * t)
43954             + (end * t * t * t);
43955         };
43956         return Bezier;
43957     }()),
43958     
43959     throttleStroke: function(fn, wait) {
43960       if (wait === void 0) { wait = 250; }
43961       var previous = 0;
43962       var timeout = null;
43963       var result;
43964       var storedContext;
43965       var storedArgs;
43966       var later = function () {
43967           previous = Date.now();
43968           timeout = null;
43969           result = fn.apply(storedContext, storedArgs);
43970           if (!timeout) {
43971               storedContext = null;
43972               storedArgs = [];
43973           }
43974       };
43975       return function wrapper() {
43976           var args = [];
43977           for (var _i = 0; _i < arguments.length; _i++) {
43978               args[_i] = arguments[_i];
43979           }
43980           var now = Date.now();
43981           var remaining = wait - (now - previous);
43982           storedContext = this;
43983           storedArgs = args;
43984           if (remaining <= 0 || remaining > wait) {
43985               if (timeout) {
43986                   clearTimeout(timeout);
43987                   timeout = null;
43988               }
43989               previous = now;
43990               result = fn.apply(storedContext, storedArgs);
43991               if (!timeout) {
43992                   storedContext = null;
43993                   storedArgs = [];
43994               }
43995           }
43996           else if (!timeout) {
43997               timeout = window.setTimeout(later, remaining);
43998           }
43999           return result;
44000       };
44001   }
44002   
44003 });
44004
44005  
44006
44007