Roo/bootstrap/breadcrumb
[roojs1] / roojs-bootstrap-debug.js
1 /**
2  * set the version of bootstrap based on the stylesheet...
3  *
4  */
5
6 Roo.bootstrap.version = ( function() {
7     var ret=3;
8     Roo.each(document.styleSheets, function(s) {
9         if ( s.href  && s.href.match(/css-bootstrap4/)) {
10             ret=4;
11         }
12     });
13     if (ret > 3) {
14          Roo.Element.prototype.visibilityMode = Roo.Element.DISPLAY;
15     }
16     return ret;
17 })(); /*
18  * Based on:
19  * Ext JS Library 1.1.1
20  * Copyright(c) 2006-2007, Ext JS, LLC.
21  *
22  * Originally Released Under LGPL - original licence link has changed is not relivant.
23  *
24  * Fork - LGPL
25  * <script type="text/javascript">
26  */
27
28
29 /**
30  * @class Roo.Shadow
31  * Simple class that can provide a shadow effect for any element.  Note that the element MUST be absolutely positioned,
32  * and the shadow does not provide any shimming.  This should be used only in simple cases -- for more advanced
33  * functionality that can also provide the same shadow effect, see the {@link Roo.Layer} class.
34  * @constructor
35  * Create a new Shadow
36  * @param {Object} config The config object
37  */
38 Roo.Shadow = function(config){
39     Roo.apply(this, config);
40     if(typeof this.mode != "string"){
41         this.mode = this.defaultMode;
42     }
43     var o = this.offset, a = {h: 0};
44     var rad = Math.floor(this.offset/2);
45     switch(this.mode.toLowerCase()){ // all this hideous nonsense calculates the various offsets for shadows
46         case "drop":
47             a.w = 0;
48             a.l = a.t = o;
49             a.t -= 1;
50             if(Roo.isIE){
51                 a.l -= this.offset + rad;
52                 a.t -= this.offset + rad;
53                 a.w -= rad;
54                 a.h -= rad;
55                 a.t += 1;
56             }
57         break;
58         case "sides":
59             a.w = (o*2);
60             a.l = -o;
61             a.t = o-1;
62             if(Roo.isIE){
63                 a.l -= (this.offset - rad);
64                 a.t -= this.offset + rad;
65                 a.l += 1;
66                 a.w -= (this.offset - rad)*2;
67                 a.w -= rad + 1;
68                 a.h -= 1;
69             }
70         break;
71         case "frame":
72             a.w = a.h = (o*2);
73             a.l = a.t = -o;
74             a.t += 1;
75             a.h -= 2;
76             if(Roo.isIE){
77                 a.l -= (this.offset - rad);
78                 a.t -= (this.offset - rad);
79                 a.l += 1;
80                 a.w -= (this.offset + rad + 1);
81                 a.h -= (this.offset + rad);
82                 a.h += 1;
83             }
84         break;
85     };
86
87     this.adjusts = a;
88 };
89
90 Roo.Shadow.prototype = {
91     /**
92      * @cfg {String} mode
93      * The shadow display mode.  Supports the following options:<br />
94      * sides: Shadow displays on both sides and bottom only<br />
95      * frame: Shadow displays equally on all four sides<br />
96      * drop: Traditional bottom-right drop shadow (default)
97      */
98     /**
99      * @cfg {String} offset
100      * The number of pixels to offset the shadow from the element (defaults to 4)
101      */
102     offset: 4,
103
104     // private
105     defaultMode: "drop",
106
107     /**
108      * Displays the shadow under the target element
109      * @param {String/HTMLElement/Element} targetEl The id or element under which the shadow should display
110      */
111     show : function(target){
112         target = Roo.get(target);
113         if(!this.el){
114             this.el = Roo.Shadow.Pool.pull();
115             if(this.el.dom.nextSibling != target.dom){
116                 this.el.insertBefore(target);
117             }
118         }
119         this.el.setStyle("z-index", this.zIndex || parseInt(target.getStyle("z-index"), 10)-1);
120         if(Roo.isIE){
121             this.el.dom.style.filter="progid:DXImageTransform.Microsoft.alpha(opacity=50) progid:DXImageTransform.Microsoft.Blur(pixelradius="+(this.offset)+")";
122         }
123         this.realign(
124             target.getLeft(true),
125             target.getTop(true),
126             target.getWidth(),
127             target.getHeight()
128         );
129         this.el.dom.style.display = "block";
130     },
131
132     /**
133      * Returns true if the shadow is visible, else false
134      */
135     isVisible : function(){
136         return this.el ? true : false;  
137     },
138
139     /**
140      * Direct alignment when values are already available. Show must be called at least once before
141      * calling this method to ensure it is initialized.
142      * @param {Number} left The target element left position
143      * @param {Number} top The target element top position
144      * @param {Number} width The target element width
145      * @param {Number} height The target element height
146      */
147     realign : function(l, t, w, h){
148         if(!this.el){
149             return;
150         }
151         var a = this.adjusts, d = this.el.dom, s = d.style;
152         var iea = 0;
153         s.left = (l+a.l)+"px";
154         s.top = (t+a.t)+"px";
155         var sw = (w+a.w), sh = (h+a.h), sws = sw +"px", shs = sh + "px";
156  
157         if(s.width != sws || s.height != shs){
158             s.width = sws;
159             s.height = shs;
160             if(!Roo.isIE){
161                 var cn = d.childNodes;
162                 var sww = Math.max(0, (sw-12))+"px";
163                 cn[0].childNodes[1].style.width = sww;
164                 cn[1].childNodes[1].style.width = sww;
165                 cn[2].childNodes[1].style.width = sww;
166                 cn[1].style.height = Math.max(0, (sh-12))+"px";
167             }
168         }
169     },
170
171     /**
172      * Hides this shadow
173      */
174     hide : function(){
175         if(this.el){
176             this.el.dom.style.display = "none";
177             Roo.Shadow.Pool.push(this.el);
178             delete this.el;
179         }
180     },
181
182     /**
183      * Adjust the z-index of this shadow
184      * @param {Number} zindex The new z-index
185      */
186     setZIndex : function(z){
187         this.zIndex = z;
188         if(this.el){
189             this.el.setStyle("z-index", z);
190         }
191     }
192 };
193
194 // Private utility class that manages the internal Shadow cache
195 Roo.Shadow.Pool = function(){
196     var p = [];
197     var markup = Roo.isIE ?
198                  '<div class="x-ie-shadow"></div>' :
199                  '<div class="x-shadow"><div class="xst"><div class="xstl"></div><div class="xstc"></div><div class="xstr"></div></div><div class="xsc"><div class="xsml"></div><div class="xsmc"></div><div class="xsmr"></div></div><div class="xsb"><div class="xsbl"></div><div class="xsbc"></div><div class="xsbr"></div></div></div>';
200     return {
201         pull : function(){
202             var sh = p.shift();
203             if(!sh){
204                 sh = Roo.get(Roo.DomHelper.insertHtml("beforeBegin", document.body.firstChild, markup));
205                 sh.autoBoxAdjust = false;
206             }
207             return sh;
208         },
209
210         push : function(sh){
211             p.push(sh);
212         }
213     };
214 }();/*
215  * - LGPL
216  *
217  * base class for bootstrap elements.
218  * 
219  */
220
221 Roo.bootstrap = Roo.bootstrap || {};
222 /**
223  * @class Roo.bootstrap.Component
224  * @extends Roo.Component
225  * Bootstrap Component base class
226  * @cfg {String} cls css class
227  * @cfg {String} style any extra css
228  * @cfg {Object} xattr extra attributes to add to 'element' (used by builder to store stuff.)
229  * @cfg {Boolean} can_build_overlaid  True if element can be rebuild from a HTML page
230  * @cfg {string} dataId cutomer id
231  * @cfg {string} name Specifies name attribute
232  * @cfg {string} tooltip  Text for the tooltip
233  * @cfg {string} container_method method to fetch parents container element (used by NavHeaderbar -  getHeaderChildContainer)
234  * @cfg {string|object} visibilityEl (el|parent) What element to use for visibility (@see getVisibilityEl())
235  
236  * @constructor
237  * Do not use directly - it does not do anything..
238  * @param {Object} config The config object
239  */
240
241
242
243 Roo.bootstrap.Component = function(config){
244     Roo.bootstrap.Component.superclass.constructor.call(this, config);
245        
246     this.addEvents({
247         /**
248          * @event childrenrendered
249          * Fires when the children have been rendered..
250          * @param {Roo.bootstrap.Component} this
251          */
252         "childrenrendered" : true
253         
254         
255         
256     });
257     
258     
259 };
260
261 Roo.extend(Roo.bootstrap.Component, Roo.BoxComponent,  {
262     
263     
264     allowDomMove : false, // to stop relocations in parent onRender...
265     
266     cls : false,
267     
268     style : false,
269     
270     autoCreate : false,
271     
272     tooltip : null,
273     /**
274      * Initialize Events for the element
275      */
276     initEvents : function() { },
277     
278     xattr : false,
279     
280     parentId : false,
281     
282     can_build_overlaid : true,
283     
284     container_method : false,
285     
286     dataId : false,
287     
288     name : false,
289     
290     parent: function() {
291         // returns the parent component..
292         return Roo.ComponentMgr.get(this.parentId)
293         
294         
295     },
296     
297     // private
298     onRender : function(ct, position)
299     {
300        // Roo.log("Call onRender: " + this.xtype);
301         
302         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
303         
304         if(this.el){
305             if (this.el.attr('xtype')) {
306                 this.el.attr('xtypex', this.el.attr('xtype'));
307                 this.el.dom.removeAttribute('xtype');
308                 
309                 this.initEvents();
310             }
311             
312             return;
313         }
314         
315          
316         
317         var cfg = Roo.apply({},  this.getAutoCreate());
318         
319         cfg.id = this.id || Roo.id();
320         
321         // fill in the extra attributes 
322         if (this.xattr && typeof(this.xattr) =='object') {
323             for (var i in this.xattr) {
324                 cfg[i] = this.xattr[i];
325             }
326         }
327         
328         if(this.dataId){
329             cfg.dataId = this.dataId;
330         }
331         
332         if (this.cls) {
333             cfg.cls = (typeof(cfg.cls) == 'undefined') ? this.cls : cfg.cls + ' ' + this.cls;
334         }
335         
336         if (this.style) { // fixme needs to support more complex style data.
337             cfg.style = this.style;
338         }
339         
340         if(this.name){
341             cfg.name = this.name;
342         }
343         
344         this.el = ct.createChild(cfg, position);
345         
346         if (this.tooltip) {
347             this.tooltipEl().attr('tooltip', this.tooltip);
348         }
349         
350         if(this.tabIndex !== undefined){
351             this.el.dom.setAttribute('tabIndex', this.tabIndex);
352         }
353         
354         this.initEvents();
355         
356     },
357     /**
358      * Fetch the element to add children to
359      * @return {Roo.Element} defaults to this.el
360      */
361     getChildContainer : function()
362     {
363         return this.el;
364     },
365     /**
366      * Fetch the element to display the tooltip on.
367      * @return {Roo.Element} defaults to this.el
368      */
369     tooltipEl : function()
370     {
371         return this.el;
372     },
373         
374     addxtype  : function(tree,cntr)
375     {
376         var cn = this;
377         
378         cn = Roo.factory(tree);
379         //Roo.log(['addxtype', cn]);
380            
381         cn.parentType = this.xtype; //??
382         cn.parentId = this.id;
383         
384         cntr = (typeof(cntr) == 'undefined' ) ? 'getChildContainer' : cntr;
385         if (typeof(cn.container_method) == 'string') {
386             cntr = cn.container_method;
387         }
388         
389         
390         var has_flexy_each =  (typeof(tree['flexy:foreach']) != 'undefined');
391         
392         var has_flexy_if =  (typeof(tree['flexy:if']) != 'undefined');
393         
394         var build_from_html =  Roo.XComponent.build_from_html;
395           
396         var is_body  = (tree.xtype == 'Body') ;
397           
398         var page_has_body = (Roo.get(document.body).attr('xtype') == 'Roo.bootstrap.Body');
399           
400         var self_cntr_el = Roo.get(this[cntr](false));
401         
402         // do not try and build conditional elements 
403         if ((has_flexy_each || has_flexy_if || this.can_build_overlaid == false ) && build_from_html) {
404             return false;
405         }
406         
407         if (!has_flexy_each || !build_from_html || is_body || !page_has_body) {
408             if(!has_flexy_if || typeof(tree.name) == 'undefined' || !build_from_html || is_body || !page_has_body){
409                 return this.addxtypeChild(tree,cntr, is_body);
410             }
411             
412             var echild =self_cntr_el ? self_cntr_el.child('>*[name=' + tree.name + ']') : false;
413                 
414             if(echild){
415                 return this.addxtypeChild(Roo.apply({}, tree),cntr);
416             }
417             
418             Roo.log('skipping render');
419             return cn;
420             
421         }
422         
423         var ret = false;
424         if (!build_from_html) {
425             return false;
426         }
427         
428         // this i think handles overlaying multiple children of the same type
429         // with the sam eelement.. - which might be buggy..
430         while (true) {
431             var echild =self_cntr_el ? self_cntr_el.child('>*[xtype]') : false;
432             
433             if (!echild) {
434                 break;
435             }
436             
437             if (echild && echild.attr('xtype').split('.').pop() != cn.xtype) {
438                 break;
439             }
440             
441             ret = this.addxtypeChild(Roo.apply({}, tree),cntr);
442         }
443        
444         return ret;
445     },
446     
447     
448     addxtypeChild : function (tree, cntr, is_body)
449     {
450         Roo.debug && Roo.log('addxtypeChild:' + cntr);
451         var cn = this;
452         cntr = (typeof(cntr) == 'undefined' ) ? 'getChildContainer' : cntr;
453         
454         
455         var has_flexy = (typeof(tree['flexy:if']) != 'undefined') ||
456                     (typeof(tree['flexy:foreach']) != 'undefined');
457           
458     
459         
460         skip_children = false;
461         // render the element if it's not BODY.
462         if (!is_body) {
463             
464             // if parent was disabled, then do not try and create the children..
465             if(!this[cntr](true)){
466                 tree.items = [];
467                 return tree;
468             }
469            
470             cn = Roo.factory(tree);
471            
472             cn.parentType = this.xtype; //??
473             cn.parentId = this.id;
474             
475             var build_from_html =  Roo.XComponent.build_from_html;
476             
477             
478             // does the container contain child eleemnts with 'xtype' attributes.
479             // that match this xtype..
480             // note - when we render we create these as well..
481             // so we should check to see if body has xtype set.
482             if (build_from_html && Roo.get(document.body).attr('xtype') == 'Roo.bootstrap.Body') {
483                
484                 var self_cntr_el = Roo.get(this[cntr](false));
485                 var echild =self_cntr_el ? self_cntr_el.child('>*[xtype]') : false;
486                 if (echild) { 
487                     //Roo.log(Roo.XComponent.build_from_html);
488                     //Roo.log("got echild:");
489                     //Roo.log(echild);
490                 }
491                 // there is a scenario where some of the child elements are flexy:if (and all of the same type)
492                 // and are not displayed -this causes this to use up the wrong element when matching.
493                 // at present the only work around for this is to nest flexy:if elements in another element that is always rendered.
494                 
495                 
496                 if (echild && echild.attr('xtype').split('.').pop() == cn.xtype) {
497                   //  Roo.log("found child for " + this.xtype +": " + echild.attr('xtype') );
498                   
499                   
500                   
501                     cn.el = echild;
502                   //  Roo.log("GOT");
503                     //echild.dom.removeAttribute('xtype');
504                 } else {
505                     Roo.debug && Roo.log("MISSING " + cn.xtype + " on child of " + (this.el ? this.el.attr('xbuilderid') : 'no parent'));
506                     Roo.debug && Roo.log(self_cntr_el);
507                     Roo.debug && Roo.log(echild);
508                     Roo.debug && Roo.log(cn);
509                 }
510             }
511            
512             
513            
514             // if object has flexy:if - then it may or may not be rendered.
515             if (build_from_html && has_flexy && !cn.el &&  cn.can_build_overlaid) {
516                 // skip a flexy if element.
517                 Roo.debug && Roo.log('skipping render');
518                 Roo.debug && Roo.log(tree);
519                 if (!cn.el) {
520                     Roo.debug && Roo.log('skipping all children');
521                     skip_children = true;
522                 }
523                 
524              } else {
525                  
526                 // actually if flexy:foreach is found, we really want to create 
527                 // multiple copies here...
528                 //Roo.log('render');
529                 //Roo.log(this[cntr]());
530                 // some elements do not have render methods.. like the layouts...
531                 /*
532                 if(this[cntr](true) === false){
533                     cn.items = [];
534                     return cn;
535                 }
536                 */
537                 cn.render && cn.render(this[cntr](true));
538                 
539              }
540             // then add the element..
541         }
542          
543         // handle the kids..
544         
545         var nitems = [];
546         /*
547         if (typeof (tree.menu) != 'undefined') {
548             tree.menu.parentType = cn.xtype;
549             tree.menu.triggerEl = cn.el;
550             nitems.push(cn.addxtype(Roo.apply({}, tree.menu)));
551             
552         }
553         */
554         if (!tree.items || !tree.items.length) {
555             cn.items = nitems;
556             //Roo.log(["no children", this]);
557             
558             return cn;
559         }
560          
561         var items = tree.items;
562         delete tree.items;
563         
564         //Roo.log(items.length);
565             // add the items..
566         if (!skip_children) {    
567             for(var i =0;i < items.length;i++) {
568               //  Roo.log(['add child', items[i]]);
569                 nitems.push(cn.addxtype(Roo.apply({}, items[i])));
570             }
571         }
572         
573         cn.items = nitems;
574         
575         //Roo.log("fire childrenrendered");
576         
577         cn.fireEvent('childrenrendered', this);
578         
579         return cn;
580     },
581     
582     /**
583      * Set the element that will be used to show or hide
584      */
585     setVisibilityEl : function(el)
586     {
587         this.visibilityEl = el;
588     },
589     
590      /**
591      * Get the element that will be used to show or hide
592      */
593     getVisibilityEl : function()
594     {
595         if (typeof(this.visibilityEl) == 'object') {
596             return this.visibilityEl;
597         }
598         
599         if (typeof(this.visibilityEl) == 'string') {
600             return this.visibilityEl == 'parent' ? this.parent().getEl() : this.getEl();
601         }
602         
603         return this.getEl();
604     },
605     
606     /**
607      * Show a component - removes 'hidden' class
608      */
609     show : function()
610     {
611         if(!this.getVisibilityEl()){
612             return;
613         }
614          
615         this.getVisibilityEl().removeClass(['hidden','d-none']);
616         
617         this.fireEvent('show', this);
618         
619         
620     },
621     /**
622      * Hide a component - adds 'hidden' class
623      */
624     hide: function()
625     {
626         if(!this.getVisibilityEl()){
627             return;
628         }
629         
630         this.getVisibilityEl().addClass(['hidden','d-none']);
631         
632         this.fireEvent('hide', this);
633         
634     }
635 });
636
637  /*
638  * - LGPL
639  *
640  * element
641  * 
642  */
643
644 /**
645  * @class Roo.bootstrap.Element
646  * @extends Roo.bootstrap.Component
647  * Bootstrap Element class
648  * @cfg {String} html contents of the element
649  * @cfg {String} tag tag of the element
650  * @cfg {String} cls class of the element
651  * @cfg {Boolean} preventDefault (true|false) default false
652  * @cfg {Boolean} clickable (true|false) default false
653  * 
654  * @constructor
655  * Create a new Element
656  * @param {Object} config The config object
657  */
658
659 Roo.bootstrap.Element = function(config){
660     Roo.bootstrap.Element.superclass.constructor.call(this, config);
661     
662     this.addEvents({
663         // raw events
664         /**
665          * @event click
666          * When a element is chick
667          * @param {Roo.bootstrap.Element} this
668          * @param {Roo.EventObject} e
669          */
670         "click" : true
671     });
672 };
673
674 Roo.extend(Roo.bootstrap.Element, Roo.bootstrap.Component,  {
675     
676     tag: 'div',
677     cls: '',
678     html: '',
679     preventDefault: false, 
680     clickable: false,
681     
682     getAutoCreate : function(){
683         
684         var cfg = {
685             tag: this.tag,
686             // cls: this.cls, double assign in parent class Component.js :: onRender
687             html: this.html
688         };
689         
690         return cfg;
691     },
692     
693     initEvents: function() 
694     {
695         Roo.bootstrap.Element.superclass.initEvents.call(this);
696         
697         if(this.clickable){
698             this.el.on('click', this.onClick, this);
699         }
700         
701     },
702     
703     onClick : function(e)
704     {
705         if(this.preventDefault){
706             e.preventDefault();
707         }
708         
709         this.fireEvent('click', this, e);
710     },
711     
712     getValue : function()
713     {
714         return this.el.dom.innerHTML;
715     },
716     
717     setValue : function(value)
718     {
719         this.el.dom.innerHTML = value;
720     }
721    
722 });
723
724  
725
726  /*
727  * - LGPL
728  *
729  * dropable area
730  * 
731  */
732
733 /**
734  * @class Roo.bootstrap.DropTarget
735  * @extends Roo.bootstrap.Element
736  * Bootstrap DropTarget class
737  
738  * @cfg {string} name dropable name
739  * 
740  * @constructor
741  * Create a new Dropable Area
742  * @param {Object} config The config object
743  */
744
745 Roo.bootstrap.DropTarget = function(config){
746     Roo.bootstrap.DropTarget.superclass.constructor.call(this, config);
747     
748     this.addEvents({
749         // raw events
750         /**
751          * @event click
752          * When a element is chick
753          * @param {Roo.bootstrap.Element} this
754          * @param {Roo.EventObject} e
755          */
756         "drop" : true
757     });
758 };
759
760 Roo.extend(Roo.bootstrap.DropTarget, Roo.bootstrap.Element,  {
761     
762     
763     getAutoCreate : function(){
764         
765          
766     },
767     
768     initEvents: function() 
769     {
770         Roo.bootstrap.DropTarget.superclass.initEvents.call(this);
771         this.dropZone = new Roo.dd.DropTarget(this.getEl(), {
772             ddGroup: this.name,
773             listeners : {
774                 drop : this.dragDrop.createDelegate(this),
775                 enter : this.dragEnter.createDelegate(this),
776                 out : this.dragOut.createDelegate(this),
777                 over : this.dragOver.createDelegate(this)
778             }
779             
780         });
781         this.dropZone.DDM.useCache = false // so data gets refreshed when we resize stuff
782     },
783     
784     dragDrop : function(source,e,data)
785     {
786         // user has to decide how to impliment this.
787         Roo.log('drop');
788         Roo.log(this);
789         //this.fireEvent('drop', this, source, e ,data);
790         return false;
791     },
792     
793     dragEnter : function(n, dd, e, data)
794     {
795         // probably want to resize the element to match the dropped element..
796         Roo.log("enter");
797         this.originalSize = this.el.getSize();
798         this.el.setSize( n.el.getSize());
799         this.dropZone.DDM.refreshCache(this.name);
800         Roo.log([n, dd, e, data]);
801     },
802     
803     dragOut : function(value)
804     {
805         // resize back to normal
806         Roo.log("out");
807         this.el.setSize(this.originalSize);
808         this.dropZone.resetConstraints();
809     },
810     
811     dragOver : function()
812     {
813         // ??? do nothing?
814     }
815    
816 });
817
818  
819
820  /*
821  * - LGPL
822  *
823  * Body
824  *
825  */
826
827 /**
828  * @class Roo.bootstrap.Body
829  * @extends Roo.bootstrap.Component
830  * Bootstrap Body class
831  *
832  * @constructor
833  * Create a new body
834  * @param {Object} config The config object
835  */
836
837 Roo.bootstrap.Body = function(config){
838
839     config = config || {};
840
841     Roo.bootstrap.Body.superclass.constructor.call(this, config);
842     this.el = Roo.get(config.el ? config.el : document.body );
843     if (this.cls && this.cls.length) {
844         Roo.get(document.body).addClass(this.cls);
845     }
846 };
847
848 Roo.extend(Roo.bootstrap.Body, Roo.bootstrap.Component,  {
849
850     is_body : true,// just to make sure it's constructed?
851
852         autoCreate : {
853         cls: 'container'
854     },
855     onRender : function(ct, position)
856     {
857        /* Roo.log("Roo.bootstrap.Body - onRender");
858         if (this.cls && this.cls.length) {
859             Roo.get(document.body).addClass(this.cls);
860         }
861         // style??? xttr???
862         */
863     }
864
865
866
867
868 });
869 /*
870  * - LGPL
871  *
872  * button group
873  * 
874  */
875
876
877 /**
878  * @class Roo.bootstrap.ButtonGroup
879  * @extends Roo.bootstrap.Component
880  * Bootstrap ButtonGroup class
881  * @cfg {String} size lg | sm | xs (default empty normal)
882  * @cfg {String} align vertical | justified  (default none)
883  * @cfg {String} direction up | down (default down)
884  * @cfg {Boolean} toolbar false | true
885  * @cfg {Boolean} btn true | false
886  * 
887  * 
888  * @constructor
889  * Create a new Input
890  * @param {Object} config The config object
891  */
892
893 Roo.bootstrap.ButtonGroup = function(config){
894     Roo.bootstrap.ButtonGroup.superclass.constructor.call(this, config);
895 };
896
897 Roo.extend(Roo.bootstrap.ButtonGroup, Roo.bootstrap.Component,  {
898     
899     size: '',
900     align: '',
901     direction: '',
902     toolbar: false,
903     btn: true,
904
905     getAutoCreate : function(){
906         var cfg = {
907             cls: 'btn-group',
908             html : null
909         };
910         
911         cfg.html = this.html || cfg.html;
912         
913         if (this.toolbar) {
914             cfg = {
915                 cls: 'btn-toolbar',
916                 html: null
917             };
918             
919             return cfg;
920         }
921         
922         if (['vertical','justified'].indexOf(this.align)!==-1) {
923             cfg.cls = 'btn-group-' + this.align;
924             
925             if (this.align == 'justified') {
926                 console.log(this.items);
927             }
928         }
929         
930         if (['lg','sm','xs'].indexOf(this.size)!==-1) {
931             cfg.cls += ' btn-group-' + this.size;
932         }
933         
934         if (this.direction == 'up') {
935             cfg.cls += ' dropup' ;
936         }
937         
938         return cfg;
939     },
940     /**
941      * Add a button to the group (similar to NavItem API.)
942      */
943     addItem : function(cfg)
944     {
945         var cn = new Roo.bootstrap.Button(cfg);
946         //this.register(cn);
947         cn.parentId = this.id;
948         cn.onRender(this.el, null);
949         return cn;
950     }
951    
952 });
953
954  /*
955  * - LGPL
956  *
957  * button
958  * 
959  */
960
961 /**
962  * @class Roo.bootstrap.Button
963  * @extends Roo.bootstrap.Component
964  * Bootstrap Button class
965  * @cfg {String} html The button content
966  * @cfg {String} weight (default|primary|secondary|success|info|warning|danger|link|light|dark) default
967  * @cfg {String} badge_weight (default|primary|secondary|success|info|warning|danger|link|light|dark) default (same as button)
968  * @cfg {Boolean} outline default false (except for weight=default which emulates old behaveiour with an outline)
969  * @cfg {String} size (lg|sm|xs)
970  * @cfg {String} tag (a|input|submit)
971  * @cfg {String} href empty or href
972  * @cfg {Boolean} disabled default false;
973  * @cfg {Boolean} isClose default false;
974  * @cfg {String} glyphicon depricated - use fa
975  * @cfg {String} fa fontawesome icon - eg. 'comment' - without the fa/fas etc..
976  * @cfg {String} badge text for badge
977  * @cfg {String} theme (default|glow)  
978  * @cfg {Boolean} inverse dark themed version
979  * @cfg {Boolean} toggle is it a slidy toggle button
980  * @cfg {Boolean} pressed   default null - if the button ahs active state
981  * @cfg {String} ontext text for on slidy toggle state
982  * @cfg {String} offtext text for off slidy toggle state
983  * @cfg {Boolean} preventDefault  default true (stop click event triggering the URL if it's a link.)
984  * @cfg {Boolean} removeClass remove the standard class..
985  * @cfg {String} target (_self|_blank|_parent|_top|other) target for a href. 
986  * @cfg {Boolean} grpup if parent is a btn group - then it turns it into a toogleGroup.
987  * 
988  * @constructor
989  * Create a new button
990  * @param {Object} config The config object
991  */
992
993
994 Roo.bootstrap.Button = function(config){
995     Roo.bootstrap.Button.superclass.constructor.call(this, config);
996     
997     this.addEvents({
998         // raw events
999         /**
1000          * @event click
1001          * When a button is pressed
1002          * @param {Roo.bootstrap.Button} btn
1003          * @param {Roo.EventObject} e
1004          */
1005         "click" : true,
1006         /**
1007          * @event dblclick
1008          * When a button is double clicked
1009          * @param {Roo.bootstrap.Button} btn
1010          * @param {Roo.EventObject} e
1011          */
1012         "dblclick" : true,
1013          /**
1014          * @event toggle
1015          * After the button has been toggles
1016          * @param {Roo.bootstrap.Button} btn
1017          * @param {Roo.EventObject} e
1018          * @param {boolean} pressed (also available as button.pressed)
1019          */
1020         "toggle" : true
1021     });
1022 };
1023
1024 Roo.extend(Roo.bootstrap.Button, Roo.bootstrap.Component,  {
1025     html: false,
1026     active: false,
1027     weight: '',
1028     badge_weight: '',
1029     outline : false,
1030     size: '',
1031     tag: 'button',
1032     href: '',
1033     disabled: false,
1034     isClose: false,
1035     glyphicon: '',
1036     fa: '',
1037     badge: '',
1038     theme: 'default',
1039     inverse: false,
1040     
1041     toggle: false,
1042     ontext: 'ON',
1043     offtext: 'OFF',
1044     defaulton: true,
1045     preventDefault: true,
1046     removeClass: false,
1047     name: false,
1048     target: false,
1049     group : false,
1050      
1051     pressed : null,
1052      
1053     
1054     getAutoCreate : function(){
1055         
1056         var cfg = {
1057             tag : 'button',
1058             cls : 'roo-button',
1059             html: ''
1060         };
1061         
1062         if (['a', 'button', 'input', 'submit'].indexOf(this.tag) < 0) {
1063             throw "Invalid value for tag: " + this.tag + ". must be a, button, input or submit.";
1064             this.tag = 'button';
1065         } else {
1066             cfg.tag = this.tag;
1067         }
1068         cfg.html = '<span class="roo-button-text">' + (this.html || cfg.html) + '</span>';
1069         
1070         if (this.toggle == true) {
1071             cfg={
1072                 tag: 'div',
1073                 cls: 'slider-frame roo-button',
1074                 cn: [
1075                     {
1076                         tag: 'span',
1077                         'data-on-text':'ON',
1078                         'data-off-text':'OFF',
1079                         cls: 'slider-button',
1080                         html: this.offtext
1081                     }
1082                 ]
1083             };
1084             // why are we validating the weights?
1085             if (Roo.bootstrap.Button.weights.indexOf(this.weight) > -1) {
1086                 cfg.cls +=  ' ' + this.weight;
1087             }
1088             
1089             return cfg;
1090         }
1091         
1092         if (this.isClose) {
1093             cfg.cls += ' close';
1094             
1095             cfg["aria-hidden"] = true;
1096             
1097             cfg.html = "&times;";
1098             
1099             return cfg;
1100         }
1101              
1102         
1103         if (this.theme==='default') {
1104             cfg.cls = 'btn roo-button';
1105             
1106             //if (this.parentType != 'Navbar') {
1107             this.weight = this.weight.length ?  this.weight : 'default';
1108             //}
1109             if (Roo.bootstrap.Button.weights.indexOf(this.weight) > -1) {
1110                 
1111                 var outline = this.outline || this.weight == 'default' ? 'outline-' : '';
1112                 var weight = this.weight == 'default' ? 'secondary' : this.weight;
1113                 cfg.cls += ' btn-' + outline + weight;
1114                 if (this.weight == 'default') {
1115                     // BC
1116                     cfg.cls += ' btn-' + this.weight;
1117                 }
1118             }
1119         } else if (this.theme==='glow') {
1120             
1121             cfg.tag = 'a';
1122             cfg.cls = 'btn-glow roo-button';
1123             
1124             if (Roo.bootstrap.Button.weights.indexOf(this.weight) > -1) {
1125                 
1126                 cfg.cls += ' ' + this.weight;
1127             }
1128         }
1129    
1130         
1131         if (this.inverse) {
1132             this.cls += ' inverse';
1133         }
1134         
1135         
1136         if (this.active || this.pressed === true) {
1137             cfg.cls += ' active';
1138         }
1139         
1140         if (this.disabled) {
1141             cfg.disabled = 'disabled';
1142         }
1143         
1144         if (this.items) {
1145             Roo.log('changing to ul' );
1146             cfg.tag = 'ul';
1147             this.glyphicon = 'caret';
1148             if (Roo.bootstrap.version == 4) {
1149                 this.fa = 'caret-down';
1150             }
1151             
1152         }
1153         
1154         cfg.cls += this.size.length ? (' btn-' + this.size) : '';
1155          
1156         //gsRoo.log(this.parentType);
1157         if (this.parentType === 'Navbar' && !this.parent().bar) {
1158             Roo.log('changing to li?');
1159             
1160             cfg.tag = 'li';
1161             
1162             cfg.cls = '';
1163             cfg.cn =  [{
1164                 tag : 'a',
1165                 cls : 'roo-button',
1166                 html : this.html,
1167                 href : this.href || '#'
1168             }];
1169             if (this.menu) {
1170                 cfg.cn[0].html = this.html  + ' <span class="caret"></span>';
1171                 cfg.cls += ' dropdown';
1172             }   
1173             
1174             delete cfg.html;
1175             
1176         }
1177         
1178        cfg.cls += this.parentType === 'Navbar' ?  ' navbar-btn' : '';
1179         
1180         if (this.glyphicon) {
1181             cfg.html = ' ' + cfg.html;
1182             
1183             cfg.cn = [
1184                 {
1185                     tag: 'span',
1186                     cls: 'glyphicon glyphicon-' + this.glyphicon
1187                 }
1188             ];
1189         }
1190         if (this.fa) {
1191             cfg.html = ' ' + cfg.html;
1192             
1193             cfg.cn = [
1194                 {
1195                     tag: 'i',
1196                     cls: 'fa fas fa-' + this.fa
1197                 }
1198             ];
1199         }
1200         
1201         if (this.badge) {
1202             cfg.html += ' ';
1203             
1204             cfg.tag = 'a';
1205             
1206 //            cfg.cls='btn roo-button';
1207             
1208             cfg.href=this.href;
1209             
1210             var value = cfg.html;
1211             
1212             if(this.glyphicon){
1213                 value = {
1214                     tag: 'span',
1215                     cls: 'glyphicon glyphicon-' + this.glyphicon,
1216                     html: this.html
1217                 };
1218             }
1219             if(this.fa){
1220                 value = {
1221                     tag: 'i',
1222                     cls: 'fa fas fa-' + this.fa,
1223                     html: this.html
1224                 };
1225             }
1226             
1227             var bw = this.badge_weight.length ? this.badge_weight :
1228                 (this.weight.length ? this.weight : 'secondary');
1229             bw = bw == 'default' ? 'secondary' : bw;
1230             
1231             cfg.cn = [
1232                 value,
1233                 {
1234                     tag: 'span',
1235                     cls: 'badge badge-' + bw,
1236                     html: this.badge
1237                 }
1238             ];
1239             
1240             cfg.html='';
1241         }
1242         
1243         if (this.menu) {
1244             cfg.cls += ' dropdown';
1245             cfg.html = typeof(cfg.html) != 'undefined' ?
1246                     cfg.html + ' <span class="caret"></span>' : '<span class="caret"></span>';
1247         }
1248         
1249         if (cfg.tag !== 'a' && this.href !== '') {
1250             throw "Tag must be a to set href.";
1251         } else if (this.href.length > 0) {
1252             cfg.href = this.href;
1253         }
1254         
1255         if(this.removeClass){
1256             cfg.cls = '';
1257         }
1258         
1259         if(this.target){
1260             cfg.target = this.target;
1261         }
1262         
1263         return cfg;
1264     },
1265     initEvents: function() {
1266        // Roo.log('init events?');
1267 //        Roo.log(this.el.dom);
1268         // add the menu...
1269         
1270         if (typeof (this.menu) != 'undefined') {
1271             this.menu.parentType = this.xtype;
1272             this.menu.triggerEl = this.el;
1273             this.addxtype(Roo.apply({}, this.menu));
1274         }
1275
1276
1277         if (this.el.hasClass('roo-button')) {
1278              this.el.on('click', this.onClick, this);
1279              this.el.on('dblclick', this.onDblClick, this);
1280         } else {
1281              this.el.select('.roo-button').on('click', this.onClick, this);
1282              this.el.select('.roo-button').on('dblclick', this.onDblClick, this);
1283              
1284         }
1285         // why?
1286         if(this.removeClass){
1287             this.el.on('click', this.onClick, this);
1288         }
1289         
1290         if (this.group === true) {
1291              if (this.pressed === false || this.pressed === true) {
1292                 // nothing
1293             } else {
1294                 this.pressed = false;
1295                 this.setActive(this.pressed);
1296             }
1297             
1298         }
1299         
1300         this.el.enableDisplayMode();
1301         
1302     },
1303     onClick : function(e)
1304     {
1305         if (this.disabled) {
1306             return;
1307         }
1308         
1309         Roo.log('button on click ');
1310         if(this.preventDefault){
1311             e.preventDefault();
1312         }
1313         
1314         if (this.group) {
1315             if (this.pressed) {
1316                 // do nothing -
1317                 return;
1318             }
1319             this.setActive(true);
1320             var pi = this.parent().items;
1321             for (var i = 0;i < pi.length;i++) {
1322                 if (this == pi[i]) {
1323                     continue;
1324                 }
1325                 if (pi[i].el.hasClass('roo-button')) {
1326                     pi[i].setActive(false);
1327                 }
1328             }
1329             this.fireEvent('click', this, e);            
1330             return;
1331         }
1332         
1333         if (this.pressed === true || this.pressed === false) {
1334             this.toggleActive(e);
1335         }
1336         
1337         
1338         this.fireEvent('click', this, e);
1339     },
1340     onDblClick: function(e)
1341     {
1342         if (this.disabled) {
1343             return;
1344         }
1345         if(this.preventDefault){
1346             e.preventDefault();
1347         }
1348         this.fireEvent('dblclick', this, e);
1349     },
1350     /**
1351      * Enables this button
1352      */
1353     enable : function()
1354     {
1355         this.disabled = false;
1356         this.el.removeClass('disabled');
1357     },
1358     
1359     /**
1360      * Disable this button
1361      */
1362     disable : function()
1363     {
1364         this.disabled = true;
1365         this.el.addClass('disabled');
1366     },
1367      /**
1368      * sets the active state on/off, 
1369      * @param {Boolean} state (optional) Force a particular state
1370      */
1371     setActive : function(v) {
1372         
1373         this.el[v ? 'addClass' : 'removeClass']('active');
1374         this.pressed = v;
1375     },
1376      /**
1377      * toggles the current active state 
1378      */
1379     toggleActive : function(e)
1380     {
1381         this.setActive(!this.pressed); // this modifies pressed...
1382         this.fireEvent('toggle', this, e, this.pressed);
1383     },
1384      /**
1385      * get the current active state
1386      * @return {boolean} true if it's active
1387      */
1388     isActive : function()
1389     {
1390         return this.el.hasClass('active');
1391     },
1392     /**
1393      * set the text of the first selected button
1394      */
1395     setText : function(str)
1396     {
1397         this.el.select('.roo-button-text',true).first().dom.innerHTML = str;
1398     },
1399     /**
1400      * get the text of the first selected button
1401      */
1402     getText : function()
1403     {
1404         return this.el.select('.roo-button-text',true).first().dom.innerHTML;
1405     },
1406     
1407     setWeight : function(str)
1408     {
1409         this.el.removeClass(Roo.bootstrap.Button.weights.map(function(w) { return 'btn-' + w; } ) );
1410         this.el.removeClass(Roo.bootstrap.Button.weights.map(function(w) { return 'btn-outline-' + w; } ) );
1411         this.weight = str;
1412         var outline = this.outline ? 'outline-' : '';
1413         if (str == 'default') {
1414             this.el.addClass('btn-default btn-outline-secondary');        
1415             return;
1416         }
1417         this.el.addClass('btn-' + outline + str);        
1418     }
1419     
1420     
1421 });
1422 // fixme - this is probably generic bootstrap - should go in some kind of enum file.. - like sizes.
1423
1424 Roo.bootstrap.Button.weights = [
1425     'default',
1426     'secondary' ,
1427     'primary',
1428     'success',
1429     'info',
1430     'warning',
1431     'danger',
1432     'link',
1433     'light',
1434     'dark'              
1435    
1436 ];/*
1437  * - LGPL
1438  *
1439  * column
1440  * 
1441  */
1442
1443 /**
1444  * @class Roo.bootstrap.Column
1445  * @extends Roo.bootstrap.Component
1446  * Bootstrap Column class
1447  * @cfg {Number} xs colspan out of 12 for mobile-sized screens or 0 for hidden
1448  * @cfg {Number} sm colspan out of 12 for tablet-sized screens or 0 for hidden
1449  * @cfg {Number} md colspan out of 12 for computer-sized screens or 0 for hidden
1450  * @cfg {Number} lg colspan out of 12 for large computer-sized screens or 0 for hidden
1451  * @cfg {Number} xsoff colspan offset out of 12 for mobile-sized screens or 0 for hidden
1452  * @cfg {Number} smoff colspan offset out of 12 for tablet-sized screens or 0 for hidden
1453  * @cfg {Number} mdoff colspan offset out of 12 for computer-sized screens or 0 for hidden
1454  * @cfg {Number} lgoff colspan offset out of 12 for large computer-sized screens or 0 for hidden
1455  *
1456  * 
1457  * @cfg {Boolean} hidden (true|false) hide the element
1458  * @cfg {String} alert (success|info|warning|danger) type alert (changes background / border...)
1459  * @cfg {String} fa (ban|check|...) font awesome icon
1460  * @cfg {Number} fasize (1|2|....) font awsome size
1461
1462  * @cfg {String} icon (info-sign|check|...) glyphicon name
1463
1464  * @cfg {String} html content of column.
1465  * 
1466  * @constructor
1467  * Create a new Column
1468  * @param {Object} config The config object
1469  */
1470
1471 Roo.bootstrap.Column = function(config){
1472     Roo.bootstrap.Column.superclass.constructor.call(this, config);
1473 };
1474
1475 Roo.extend(Roo.bootstrap.Column, Roo.bootstrap.Component,  {
1476     
1477     xs: false,
1478     sm: false,
1479     md: false,
1480     lg: false,
1481     xsoff: false,
1482     smoff: false,
1483     mdoff: false,
1484     lgoff: false,
1485     html: '',
1486     offset: 0,
1487     alert: false,
1488     fa: false,
1489     icon : false,
1490     hidden : false,
1491     fasize : 1,
1492     
1493     getAutoCreate : function(){
1494         var cfg = Roo.apply({}, Roo.bootstrap.Column.superclass.getAutoCreate.call(this));
1495         
1496         cfg = {
1497             tag: 'div',
1498             cls: 'column'
1499         };
1500         
1501         var settings=this;
1502         var sizes =   ['xs','sm','md','lg'];
1503         sizes.map(function(size ,ix){
1504             //Roo.log( size + ':' + settings[size]);
1505             
1506             if (settings[size+'off'] !== false) {
1507                 cfg.cls += ' col-' + size + '-offset-' + settings[size+'off'] ;
1508             }
1509             
1510             if (settings[size] === false) {
1511                 return;
1512             }
1513             
1514             if (!settings[size]) { // 0 = hidden
1515                 cfg.cls += ' hidden-' + size + ' hidden-' + size + '-down';
1516                 // bootsrap4
1517                 for (var i = ix; i > -1; i--) {
1518                     cfg.cls +=  ' d-' + sizes[i] + '-none'; 
1519                 }
1520                 
1521                 
1522                 return;
1523             }
1524             cfg.cls += ' col-' + size + '-' + settings[size] + (
1525                 size == 'xs' ? (' col-' + settings[size] ) : '' // bs4 col-{num} replaces col-xs
1526             );
1527             
1528         });
1529         
1530         if (this.hidden) {
1531             cfg.cls += ' hidden';
1532         }
1533         
1534         if (this.alert && ["success","info","warning", "danger"].indexOf(this.alert) > -1) {
1535             cfg.cls +=' alert alert-' + this.alert;
1536         }
1537         
1538         
1539         if (this.html.length) {
1540             cfg.html = this.html;
1541         }
1542         if (this.fa) {
1543             var fasize = '';
1544             if (this.fasize > 1) {
1545                 fasize = ' fa-' + this.fasize + 'x';
1546             }
1547             cfg.html = '<i class="fa fa-'+this.fa + fasize + '"></i>' + (cfg.html || '');
1548             
1549             
1550         }
1551         if (this.icon) {
1552             cfg.html = '<i class="glyphicon glyphicon-'+this.icon + '"></i>' +  (cfg.html || '');
1553         }
1554         
1555         return cfg;
1556     }
1557    
1558 });
1559
1560  
1561
1562  /*
1563  * - LGPL
1564  *
1565  * page container.
1566  * 
1567  */
1568
1569
1570 /**
1571  * @class Roo.bootstrap.Container
1572  * @extends Roo.bootstrap.Component
1573  * Bootstrap Container class
1574  * @cfg {Boolean} jumbotron is it a jumbotron element
1575  * @cfg {String} html content of element
1576  * @cfg {String} well (lg|sm|md) a well, large, small or medium.
1577  * @cfg {String} panel (default|primary|success|info|warning|danger) render as panel  - type - primary/success.....
1578  * @cfg {String} header content of header (for panel)
1579  * @cfg {String} footer content of footer (for panel)
1580  * @cfg {String} sticky (footer|wrap|push) block to use as footer or body- needs css-bootstrap/sticky-footer.css
1581  * @cfg {String} tag (header|aside|section) type of HTML tag.
1582  * @cfg {String} alert (success|info|warning|danger) type alert (changes background / border...)
1583  * @cfg {String} fa font awesome icon
1584  * @cfg {String} icon (info-sign|check|...) glyphicon name
1585  * @cfg {Boolean} hidden (true|false) hide the element
1586  * @cfg {Boolean} expandable (true|false) default false
1587  * @cfg {Boolean} expanded (true|false) default true
1588  * @cfg {String} rheader contet on the right of header
1589  * @cfg {Boolean} clickable (true|false) default false
1590
1591  *     
1592  * @constructor
1593  * Create a new Container
1594  * @param {Object} config The config object
1595  */
1596
1597 Roo.bootstrap.Container = function(config){
1598     Roo.bootstrap.Container.superclass.constructor.call(this, config);
1599     
1600     this.addEvents({
1601         // raw events
1602          /**
1603          * @event expand
1604          * After the panel has been expand
1605          * 
1606          * @param {Roo.bootstrap.Container} this
1607          */
1608         "expand" : true,
1609         /**
1610          * @event collapse
1611          * After the panel has been collapsed
1612          * 
1613          * @param {Roo.bootstrap.Container} this
1614          */
1615         "collapse" : true,
1616         /**
1617          * @event click
1618          * When a element is chick
1619          * @param {Roo.bootstrap.Container} this
1620          * @param {Roo.EventObject} e
1621          */
1622         "click" : true
1623     });
1624 };
1625
1626 Roo.extend(Roo.bootstrap.Container, Roo.bootstrap.Component,  {
1627     
1628     jumbotron : false,
1629     well: '',
1630     panel : '',
1631     header: '',
1632     footer : '',
1633     sticky: '',
1634     tag : false,
1635     alert : false,
1636     fa: false,
1637     icon : false,
1638     expandable : false,
1639     rheader : '',
1640     expanded : true,
1641     clickable: false,
1642   
1643      
1644     getChildContainer : function() {
1645         
1646         if(!this.el){
1647             return false;
1648         }
1649         
1650         if (this.panel.length) {
1651             return this.el.select('.panel-body',true).first();
1652         }
1653         
1654         return this.el;
1655     },
1656     
1657     
1658     getAutoCreate : function(){
1659         
1660         var cfg = {
1661             tag : this.tag || 'div',
1662             html : '',
1663             cls : ''
1664         };
1665         if (this.jumbotron) {
1666             cfg.cls = 'jumbotron';
1667         }
1668         
1669         
1670         
1671         // - this is applied by the parent..
1672         //if (this.cls) {
1673         //    cfg.cls = this.cls + '';
1674         //}
1675         
1676         if (this.sticky.length) {
1677             
1678             var bd = Roo.get(document.body);
1679             if (!bd.hasClass('bootstrap-sticky')) {
1680                 bd.addClass('bootstrap-sticky');
1681                 Roo.select('html',true).setStyle('height', '100%');
1682             }
1683              
1684             cfg.cls += 'bootstrap-sticky-' + this.sticky;
1685         }
1686         
1687         
1688         if (this.well.length) {
1689             switch (this.well) {
1690                 case 'lg':
1691                 case 'sm':
1692                     cfg.cls +=' well well-' +this.well;
1693                     break;
1694                 default:
1695                     cfg.cls +=' well';
1696                     break;
1697             }
1698         }
1699         
1700         if (this.hidden) {
1701             cfg.cls += ' hidden';
1702         }
1703         
1704         
1705         if (this.alert && ["success","info","warning", "danger"].indexOf(this.alert) > -1) {
1706             cfg.cls +=' alert alert-' + this.alert;
1707         }
1708         
1709         var body = cfg;
1710         
1711         if (this.panel.length) {
1712             cfg.cls += ' panel panel-' + this.panel;
1713             cfg.cn = [];
1714             if (this.header.length) {
1715                 
1716                 var h = [];
1717                 
1718                 if(this.expandable){
1719                     
1720                     cfg.cls = cfg.cls + ' expandable';
1721                     
1722                     h.push({
1723                         tag: 'i',
1724                         cls: (this.expanded ? 'fa fa-minus' : 'fa fa-plus') 
1725                     });
1726                     
1727                 }
1728                 
1729                 h.push(
1730                     {
1731                         tag: 'span',
1732                         cls : 'panel-title',
1733                         html : (this.expandable ? '&nbsp;' : '') + this.header
1734                     },
1735                     {
1736                         tag: 'span',
1737                         cls: 'panel-header-right',
1738                         html: this.rheader
1739                     }
1740                 );
1741                 
1742                 cfg.cn.push({
1743                     cls : 'panel-heading',
1744                     style : this.expandable ? 'cursor: pointer' : '',
1745                     cn : h
1746                 });
1747                 
1748             }
1749             
1750             body = false;
1751             cfg.cn.push({
1752                 cls : 'panel-body' + (this.expanded ? '' : ' hide'),
1753                 html : this.html
1754             });
1755             
1756             
1757             if (this.footer.length) {
1758                 cfg.cn.push({
1759                     cls : 'panel-footer',
1760                     html : this.footer
1761                     
1762                 });
1763             }
1764             
1765         }
1766         
1767         if (body) {
1768             body.html = this.html || cfg.html;
1769             // prefix with the icons..
1770             if (this.fa) {
1771                 body.html = '<i class="fa fa-'+this.fa + '"></i>' + body.html ;
1772             }
1773             if (this.icon) {
1774                 body.html = '<i class="glyphicon glyphicon-'+this.icon + '"></i>' + body.html ;
1775             }
1776             
1777             
1778         }
1779         if ((!this.cls || !this.cls.length) && (!cfg.cls || !cfg.cls.length)) {
1780             cfg.cls =  'container';
1781         }
1782         
1783         return cfg;
1784     },
1785     
1786     initEvents: function() 
1787     {
1788         if(this.expandable){
1789             var headerEl = this.headerEl();
1790         
1791             if(headerEl){
1792                 headerEl.on('click', this.onToggleClick, this);
1793             }
1794         }
1795         
1796         if(this.clickable){
1797             this.el.on('click', this.onClick, this);
1798         }
1799         
1800     },
1801     
1802     onToggleClick : function()
1803     {
1804         var headerEl = this.headerEl();
1805         
1806         if(!headerEl){
1807             return;
1808         }
1809         
1810         if(this.expanded){
1811             this.collapse();
1812             return;
1813         }
1814         
1815         this.expand();
1816     },
1817     
1818     expand : function()
1819     {
1820         if(this.fireEvent('expand', this)) {
1821             
1822             this.expanded = true;
1823             
1824             //this.el.select('.panel-body',true).first().setVisibilityMode(Roo.Element.DISPLAY).show();
1825             
1826             this.el.select('.panel-body',true).first().removeClass('hide');
1827             
1828             var toggleEl = this.toggleEl();
1829
1830             if(!toggleEl){
1831                 return;
1832             }
1833
1834             toggleEl.removeClass(['fa-minus', 'fa-plus']).addClass(['fa-minus']);
1835         }
1836         
1837     },
1838     
1839     collapse : function()
1840     {
1841         if(this.fireEvent('collapse', this)) {
1842             
1843             this.expanded = false;
1844             
1845             //this.el.select('.panel-body',true).first().setVisibilityMode(Roo.Element.DISPLAY).hide();
1846             this.el.select('.panel-body',true).first().addClass('hide');
1847         
1848             var toggleEl = this.toggleEl();
1849
1850             if(!toggleEl){
1851                 return;
1852             }
1853
1854             toggleEl.removeClass(['fa-minus', 'fa-plus']).addClass(['fa-plus']);
1855         }
1856     },
1857     
1858     toggleEl : function()
1859     {
1860         if(!this.el || !this.panel.length || !this.header.length || !this.expandable){
1861             return;
1862         }
1863         
1864         return this.el.select('.panel-heading .fa',true).first();
1865     },
1866     
1867     headerEl : function()
1868     {
1869         if(!this.el || !this.panel.length || !this.header.length){
1870             return;
1871         }
1872         
1873         return this.el.select('.panel-heading',true).first()
1874     },
1875     
1876     bodyEl : function()
1877     {
1878         if(!this.el || !this.panel.length){
1879             return;
1880         }
1881         
1882         return this.el.select('.panel-body',true).first()
1883     },
1884     
1885     titleEl : function()
1886     {
1887         if(!this.el || !this.panel.length || !this.header.length){
1888             return;
1889         }
1890         
1891         return this.el.select('.panel-title',true).first();
1892     },
1893     
1894     setTitle : function(v)
1895     {
1896         var titleEl = this.titleEl();
1897         
1898         if(!titleEl){
1899             return;
1900         }
1901         
1902         titleEl.dom.innerHTML = v;
1903     },
1904     
1905     getTitle : function()
1906     {
1907         
1908         var titleEl = this.titleEl();
1909         
1910         if(!titleEl){
1911             return '';
1912         }
1913         
1914         return titleEl.dom.innerHTML;
1915     },
1916     
1917     setRightTitle : function(v)
1918     {
1919         var t = this.el.select('.panel-header-right',true).first();
1920         
1921         if(!t){
1922             return;
1923         }
1924         
1925         t.dom.innerHTML = v;
1926     },
1927     
1928     onClick : function(e)
1929     {
1930         e.preventDefault();
1931         
1932         this.fireEvent('click', this, e);
1933     }
1934 });
1935
1936  /*
1937  *  - LGPL
1938  *
1939  *  This is BS4's Card element.. - similar to our containers probably..
1940  * 
1941  */
1942 /**
1943  * @class Roo.bootstrap.Card
1944  * @extends Roo.bootstrap.Component
1945  * Bootstrap Card class
1946  *
1947  *
1948  * possible... may not be implemented..
1949  * @cfg {String} header_image  src url of image.
1950  * @cfg {String|Object} header
1951  * @cfg {Number} header_size (0|1|2|3|4|5) H1 or H2 etc.. 0 indicates default
1952  * @cfg {Number} header_weight  (primary|secondary|success|info|warning|danger|light|dark)
1953  * 
1954  * @cfg {String} title
1955  * @cfg {String} subtitle
1956  * @cfg {String|Boolean} html -- html contents - or just use children.. use false to hide it..
1957  * @cfg {String} footer
1958  
1959  * @cfg {String} weight (primary|warning|info|danger|secondary|success|light|dark)
1960  * 
1961  * @cfg {String} margin (0|1|2|3|4|5|auto)
1962  * @cfg {String} margin_top (0|1|2|3|4|5|auto)
1963  * @cfg {String} margin_bottom (0|1|2|3|4|5|auto)
1964  * @cfg {String} margin_left (0|1|2|3|4|5|auto)
1965  * @cfg {String} margin_right (0|1|2|3|4|5|auto)
1966  * @cfg {String} margin_x (0|1|2|3|4|5|auto)
1967  * @cfg {String} margin_y (0|1|2|3|4|5|auto)
1968  *
1969  * @cfg {String} padding (0|1|2|3|4|5)
1970  * @cfg {String} padding_top (0|1|2|3|4|5)next_to_card
1971  * @cfg {String} padding_bottom (0|1|2|3|4|5)
1972  * @cfg {String} padding_left (0|1|2|3|4|5)
1973  * @cfg {String} padding_right (0|1|2|3|4|5)
1974  * @cfg {String} padding_x (0|1|2|3|4|5)
1975  * @cfg {String} padding_y (0|1|2|3|4|5)
1976  *
1977  * @cfg {String} display (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
1978  * @cfg {String} display_xs (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
1979  * @cfg {String} display_sm (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
1980  * @cfg {String} display_lg (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
1981  * @cfg {String} display_xl (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
1982  
1983  * @config {Boolean} dragable  if this card can be dragged.
1984  * @config {String} drag_group  group for drag
1985  * @config {Boolean} dropable  if this card can recieve other cards being dropped onto it..
1986  * @config {String} drop_group  group for drag
1987  * 
1988  * @config {Boolean} collapsable can the body be collapsed.
1989  * @config {Boolean} collapsed is the body collapsed when rendered...
1990  * @config {Boolean} rotateable can the body be rotated by clicking on it..
1991  * @config {Boolean} rotated is the body rotated when rendered...
1992  * 
1993  * @constructor
1994  * Create a new Container
1995  * @param {Object} config The config object
1996  */
1997
1998 Roo.bootstrap.Card = function(config){
1999     Roo.bootstrap.Card.superclass.constructor.call(this, config);
2000     
2001     this.addEvents({
2002          // raw events
2003         /**
2004          * @event drop
2005          * When a element a card is dropped
2006          * @param {Roo.bootstrap.Card} this
2007          *
2008          * 
2009          * @param {Roo.bootstrap.Card} move_card the card being dropped?
2010          * @param {String} position 'above' or 'below'
2011          * @param {Roo.bootstrap.Card} next_to_card What card position is relative to of 'false' for empty list.
2012         
2013          */
2014         'drop' : true,
2015          /**
2016          * @event rotate
2017          * When a element a card is rotate
2018          * @param {Roo.bootstrap.Element} this
2019          * @param {Roo.Element} n the node being dropped?
2020          * @param {Boolean} rotate status
2021          */
2022         'rotate' : true
2023         
2024     });
2025 };
2026
2027
2028 Roo.extend(Roo.bootstrap.Card, Roo.bootstrap.Component,  {
2029     
2030     
2031     weight : '',
2032     
2033     margin: '', /// may be better in component?
2034     margin_top: '', 
2035     margin_bottom: '', 
2036     margin_left: '',
2037     margin_right: '',
2038     margin_x: '',
2039     margin_y: '',
2040     
2041     padding : '',
2042     padding_top: '', 
2043     padding_bottom: '', 
2044     padding_left: '',
2045     padding_right: '',
2046     padding_x: '',
2047     padding_y: '',
2048     
2049     display: '', 
2050     display_xs: '', 
2051     display_sm: '', 
2052     display_lg: '',
2053     display_xl: '',
2054  
2055     header_image  : '',
2056     header : '',
2057     header_size : 0,
2058     title : '',
2059     subtitle : '',
2060     html : '',
2061     footer: '',
2062
2063     collapsable : false,
2064     collapsed : false,
2065     rotateable : false,
2066     rotated : false,
2067     
2068     dragable : false,
2069     drag_group : false,
2070     dropable : false,
2071     drop_group : false,
2072     childContainer : false,
2073     dropEl : false, /// the dom placeholde element that indicates drop location.
2074     containerEl: false, // body container
2075     bodyEl: false, // card-body
2076     headerContainerEl : false, //
2077     headerEl : false,
2078     
2079     layoutCls : function()
2080     {
2081         var cls = '';
2082         var t = this;
2083         Roo.log(this.margin_bottom.length);
2084         ['', 'top', 'bottom', 'left', 'right', 'x', 'y' ].forEach(function(v) {
2085             // in theory these can do margin_top : ml-xs-3 ??? but we don't support that yet
2086             
2087             if (('' + t['margin' + (v.length ? '_' : '') + v]).length) {
2088                 cls += ' m' +  (v.length ? v[0]  : '') + '-' +  t['margin' + (v.length ? '_' : '') + v];
2089             }
2090             if (('' + t['padding' + (v.length ? '_' : '') + v]).length) {
2091                 cls += ' p' +  (v.length ? v[0]  : '') + '-' +  t['padding' + (v.length ? '_' : '') + v];
2092             }
2093         });
2094         
2095         ['', 'xs', 'sm', 'lg', 'xl'].forEach(function(v) {
2096             if (('' + t['display' + (v.length ? '_' : '') + v]).length) {
2097                 cls += ' d' +  (v.length ? '-' : '') + v + '-' + t['display' + (v.length ? '_' : '') + v]
2098             }
2099         });
2100         
2101         // more generic support?
2102         if (this.hidden) {
2103             cls += ' d-none';
2104         }
2105         
2106         return cls;
2107     },
2108  
2109        // Roo.log("Call onRender: " + this.xtype);
2110         /*  We are looking at something like this.
2111 <div class="card">
2112     <img src="..." class="card-img-top" alt="...">
2113     <div class="card-body">
2114         <h5 class="card-title">Card title</h5>
2115          <h6 class="card-subtitle mb-2 text-muted">Card subtitle</h6>
2116
2117         >> this bit is really the body...
2118         <div> << we will ad dthis in hopefully it will not break shit.
2119         
2120         ** card text does not actually have any styling...
2121         
2122             <p class="card-text">This is a wider card with supporting text below as a natural lead-in to additional content. This content is a little bit longer.</p>
2123         
2124         </div> <<
2125           <a href="#" class="card-link">Card link</a>
2126           
2127     </div>
2128     <div class="card-footer">
2129         <small class="text-muted">Last updated 3 mins ago</small>
2130     </div>
2131 </div>
2132          */
2133     getAutoCreate : function(){
2134         
2135         var cfg = {
2136             tag : 'div',
2137             cls : 'card',
2138             cn : [ ]
2139         };
2140         
2141         if (this.weight.length && this.weight != 'light') {
2142             cfg.cls += ' text-white';
2143         } else {
2144             cfg.cls += ' text-dark'; // need as it's nested..
2145         }
2146         if (this.weight.length) {
2147             cfg.cls += ' bg-' + this.weight;
2148         }
2149         
2150         cfg.cls += ' ' + this.layoutCls(); 
2151         
2152         var hdr = false;
2153         var hdr_ctr = false;
2154         if (this.header.length) {
2155             hdr = {
2156                 tag : this.header_size > 0 ? 'h' + this.header_size : 'div',
2157                 cls : 'card-header ' + (this.header_weight ? 'bg-' + this.header_weight : ''),
2158                 cn : []
2159             };
2160             cfg.cn.push(hdr);
2161             hdr_ctr = hdr;
2162         } else {
2163             hdr = {
2164                 tag : 'div',
2165                 cls : 'card-header d-none ' + (this.header_weight ? 'bg-' + this.header_weight : ''),
2166                 cn : []
2167             };
2168             cfg.cn.push(hdr);
2169             hdr_ctr = hdr;
2170         }
2171         if (this.collapsable) {
2172             hdr_ctr = {
2173             tag : 'a',
2174             cls : 'd-block user-select-none',
2175             cn: [
2176                     {
2177                         tag: 'i',
2178                         cls : 'roo-collapse-toggle fa fa-chevron-down float-right ' + (this.collapsed ? 'collapsed' : '')
2179                     }
2180                    
2181                 ]
2182             };
2183             hdr.cn.push(hdr_ctr);
2184         }
2185         
2186         hdr_ctr.cn.push(        {
2187             tag: 'span',
2188             cls: 'roo-card-header-ctr' + ( this.header.length ? '' : ' d-none'),
2189             html : this.header
2190         });
2191         
2192         
2193         if (this.header_image.length) {
2194             cfg.cn.push({
2195                 tag : 'img',
2196                 cls : 'card-img-top',
2197                 src: this.header_image // escape?
2198             });
2199         } else {
2200             cfg.cn.push({
2201                     tag : 'div',
2202                     cls : 'card-img-top d-none' 
2203                 });
2204         }
2205             
2206         var body = {
2207             tag : 'div',
2208             cls : 'card-body' + (this.html === false  ? ' d-none' : ''),
2209             cn : []
2210         };
2211         var obody = body;
2212         if (this.collapsable || this.rotateable) {
2213             obody = {
2214                 tag: 'div',
2215                 cls : 'roo-collapsable collapse ' + (this.collapsed || this.rotated ? '' : 'show'),
2216                 cn : [  body ]
2217             };
2218         }
2219         
2220         cfg.cn.push(obody);
2221         
2222         if (this.title.length) {
2223             body.cn.push({
2224                 tag : 'div',
2225                 cls : 'card-title',
2226                 src: this.title // escape?
2227             });
2228         }  
2229         
2230         if (this.subtitle.length) {
2231             body.cn.push({
2232                 tag : 'div',
2233                 cls : 'card-title',
2234                 src: this.subtitle // escape?
2235             });
2236         }
2237         
2238         body.cn.push({
2239             tag : 'div',
2240             cls : 'roo-card-body-ctr'
2241         });
2242         
2243         if (this.html.length) {
2244             body.cn.push({
2245                 tag: 'div',
2246                 html : this.html
2247             });
2248         }
2249         // fixme ? handle objects?
2250         
2251         if (this.footer.length) {
2252            
2253             cfg.cn.push({
2254                 cls : 'card-footer ' + (this.rotated ? 'd-none' : ''),
2255                 html : this.footer
2256             });
2257             
2258         } else {
2259             cfg.cn.push({cls : 'card-footer d-none'});
2260         }
2261         
2262         // footer...
2263         
2264         return cfg;
2265     },
2266     
2267     
2268     getCardHeader : function()
2269     {
2270         var  ret = this.el.select('.card-header',true).first();
2271         if (ret.hasClass('d-none')) {
2272             ret.removeClass('d-none');
2273         }
2274         
2275         return ret;
2276     },
2277     getCardFooter : function()
2278     {
2279         var  ret = this.el.select('.card-footer',true).first();
2280         if (ret.hasClass('d-none')) {
2281             ret.removeClass('d-none');
2282         }
2283         
2284         return ret;
2285     },
2286     getCardImageTop : function()
2287     {
2288         var  ret = this.el.select('.card-img-top',true).first();
2289         if (ret.hasClass('d-none')) {
2290             ret.removeClass('d-none');
2291         }
2292             
2293         return ret;
2294     },
2295     
2296     getChildContainer : function()
2297     {
2298         
2299         if(!this.el){
2300             return false;
2301         }
2302         return this.el.select('.roo-card-body-ctr',true).first();    
2303     },
2304     
2305     initEvents: function() 
2306     {
2307         this.bodyEl = this.el.select('.card-body',true).first(); 
2308         this.containerEl = this.getChildContainer();
2309         if(this.dragable){
2310             this.dragZone = new Roo.dd.DragZone(this.getEl(), {
2311                     containerScroll: true,
2312                     ddGroup: this.drag_group || 'default_card_drag_group'
2313             });
2314             this.dragZone.getDragData = this.getDragData.createDelegate(this);
2315         }
2316         if (this.dropable) {
2317             this.dropZone = new Roo.dd.DropZone(this.el.select('.card-body',true).first() , {
2318                 containerScroll: true,
2319                 ddGroup: this.drop_group || 'default_card_drag_group'
2320             });
2321             this.dropZone.getTargetFromEvent = this.getTargetFromEvent.createDelegate(this);
2322             this.dropZone.onNodeEnter = this.onNodeEnter.createDelegate(this);
2323             this.dropZone.onNodeOver = this.onNodeOver.createDelegate(this);
2324             this.dropZone.onNodeOut = this.onNodeOut.createDelegate(this);
2325             this.dropZone.onNodeDrop = this.onNodeDrop.createDelegate(this);
2326         }
2327         
2328         if (this.collapsable) {
2329             this.el.select('.card-header',true).on('click', this.onToggleCollapse, this);
2330         }
2331         if (this.rotateable) {
2332             this.el.select('.card-header',true).on('click', this.onToggleRotate, this);
2333         }
2334         this.collapsableEl = this.el.select('.roo-collapsable').first();
2335          
2336         this.footerEl = this.el.select('.card-footer').first();
2337         this.collapsableToggleEl = this.el.select('.roo-collapse-toggle');
2338         this.headerContainerEl = this.el.select('.roo-card-header-ctr').first();
2339         this.headerEl = this.el.select('.card-header',true).first();
2340         
2341         if (this.rotated) {
2342             this.el.addClass('roo-card-rotated');
2343             this.fireEvent('rotate', this, true);
2344         }
2345         
2346     },
2347     getDragData : function(e)
2348     {
2349         var target = this.getEl();
2350         if (target) {
2351             //this.handleSelection(e);
2352             
2353             var dragData = {
2354                 source: this,
2355                 copy: false,
2356                 nodes: this.getEl(),
2357                 records: []
2358             };
2359             
2360             
2361             dragData.ddel = target.dom ;    // the div element
2362             Roo.log(target.getWidth( ));
2363             dragData.ddel.style.width = target.getWidth() + 'px';
2364             
2365             return dragData;
2366         }
2367         return false;
2368     },
2369     /**
2370     *    Part of the Roo.dd.DropZone interface. If no target node is found, the
2371     *    whole Element becomes the target, and this causes the drop gesture to append.
2372     */
2373     getTargetFromEvent : function(e, dragged_card_el)
2374     {
2375         var target = e.getTarget();
2376         while ((target !== null) && (target.parentNode != this.containerEl.dom)) {
2377             target = target.parentNode;
2378         }
2379         
2380         var ret = {
2381             position: '',
2382             cards : [],
2383             card_n : -1,
2384             items_n : -1,
2385             card : false 
2386         };
2387         
2388         //Roo.log([ 'target' , target ? target.id : '--nothing--']);
2389         // see if target is one of the 'cards'...
2390         
2391         
2392         //Roo.log(this.items.length);
2393         var pos = false;
2394         
2395         var last_card_n = 0;
2396         var cards_len  = 0;
2397         for (var i = 0;i< this.items.length;i++) {
2398             
2399             if (!this.items[i].el.hasClass('card')) {
2400                  continue;
2401             }
2402             pos = this.getDropPoint(e, this.items[i].el.dom);
2403             
2404             cards_len = ret.cards.length;
2405             //Roo.log(this.items[i].el.dom.id);
2406             ret.cards.push(this.items[i]);
2407             last_card_n  = i;
2408             if (ret.card_n < 0 && pos == 'above') {
2409                 ret.position = cards_len > 0 ? 'below' : pos;
2410                 ret.items_n = i > 0 ? i - 1 : 0;
2411                 ret.card_n  = cards_len  > 0 ? cards_len - 1 : 0;
2412                 ret.card = ret.cards[ret.card_n];
2413             }
2414         }
2415         if (!ret.cards.length) {
2416             ret.card = true;
2417             ret.position = 'below';
2418             ret.items_n;
2419             return ret;
2420         }
2421         // could not find a card.. stick it at the end..
2422         if (ret.card_n < 0) {
2423             ret.card_n = last_card_n;
2424             ret.card = ret.cards[last_card_n];
2425             ret.items_n = this.items.indexOf(ret.cards[last_card_n]);
2426             ret.position = 'below';
2427         }
2428         
2429         if (this.items[ret.items_n].el == dragged_card_el) {
2430             return false;
2431         }
2432         
2433         if (ret.position == 'below') {
2434             var card_after = ret.card_n+1 == ret.cards.length ? false : ret.cards[ret.card_n+1];
2435             
2436             if (card_after  && card_after.el == dragged_card_el) {
2437                 return false;
2438             }
2439             return ret;
2440         }
2441         
2442         // its's after ..
2443         var card_before = ret.card_n > 0 ? ret.cards[ret.card_n-1] : false;
2444         
2445         if (card_before  && card_before.el == dragged_card_el) {
2446             return false;
2447         }
2448         
2449         return ret;
2450     },
2451     
2452     onNodeEnter : function(n, dd, e, data){
2453         return false;
2454     },
2455     onNodeOver : function(n, dd, e, data)
2456     {
2457        
2458         var target_info = this.getTargetFromEvent(e,data.source.el);
2459         if (target_info === false) {
2460             this.dropPlaceHolder('hide');
2461             return false;
2462         }
2463         Roo.log(['getTargetFromEvent', target_info ]);
2464         
2465          
2466         this.dropPlaceHolder('show', target_info,data);
2467         
2468         return false; 
2469     },
2470     onNodeOut : function(n, dd, e, data){
2471         this.dropPlaceHolder('hide');
2472      
2473     },
2474     onNodeDrop : function(n, dd, e, data)
2475     {
2476         
2477         // call drop - return false if
2478         
2479         // this could actually fail - if the Network drops..
2480         // we will ignore this at present..- client should probably reload
2481         // the whole set of cards if stuff like that fails.
2482         
2483         
2484         var info = this.getTargetFromEvent(e,data.source.el);
2485         if (info === false) {
2486             return false;
2487         }
2488         this.dropPlaceHolder('hide');
2489   
2490          
2491     
2492     
2493     
2494         this.acceptCard(data.source, info.position, info.card, info.items_n);
2495         return true;
2496          
2497     },
2498     firstChildCard : function()
2499     {
2500         for (var i = 0;i< this.items.length;i++) {
2501             
2502             if (!this.items[i].el.hasClass('card')) {
2503                  continue;
2504             }
2505             return this.items[i];
2506         }
2507         return this.items.length ? this.items[this.items.length-1] : false; // don't try and put stuff after the cards...
2508     },
2509     /**
2510      * accept card
2511      *
2512      * -        card.acceptCard(move_card, info.position, info.card, info.items_n);
2513      */
2514     acceptCard : function(move_card,  position, next_to_card )
2515     {
2516         if (this.fireEvent("drop", this, move_card, position, next_to_card) === false) {
2517             return false;
2518         }
2519         
2520         var to_items_n = next_to_card ? this.items.indexOf(next_to_card) : 0;
2521         
2522         
2523         var dom = move_card.el.dom;
2524         dom.parentNode.removeChild(dom);
2525         dom.style.width = ''; // clear with - which is set by drag.
2526         
2527         if (next_to_card !== false && next_to_card !== true && next_to_card.el.dom.parentNode) {
2528             var cardel = next_to_card.el.dom;
2529             
2530             if (position == 'above' ) {
2531                 cardel.parentNode.insertBefore(dom, cardel);
2532             } else if (cardel.nextSibling) {
2533                 cardel.parentNode.insertBefore(dom,cardel.nextSibling);
2534             } else {
2535                 cardel.parentNode.append(dom);
2536             }
2537         } else {
2538             // card container???
2539             this.containerEl.dom.append(dom);
2540         }
2541         
2542         //FIXME HANDLE card = true 
2543         
2544         // add this to the correct place in items.
2545         
2546         
2547         
2548         // remove Card from items.
2549         
2550         var old_parent = move_card.parent();
2551         
2552         old_parent.items = old_parent.items.filter(function(e) { return e != move_card });
2553         
2554         if (this.items.length) {
2555             var nitems = [];
2556             //Roo.log([info.items_n, info.position, this.items.length]);
2557             for (var i =0; i < this.items.length; i++) {
2558                 if (i == to_items_n && position == 'above') {
2559                     nitems.push(move_card);
2560                 }
2561                 nitems.push(this.items[i]);
2562                 if (i == to_items_n && position == 'below') {
2563                     nitems.push(move_card);
2564                 }
2565             }
2566             this.items = nitems;
2567             Roo.log(this.items);
2568         } else {
2569             this.items.push(move_card);
2570         }
2571         
2572         move_card.parentId = this.id;
2573         
2574         return true;
2575         
2576         
2577     },
2578     
2579     
2580     /**    Decide whether to drop above or below a View node. */
2581     getDropPoint : function(e, n, dd)
2582     {
2583         if (dd) {
2584              return false;
2585         }
2586         if (n == this.containerEl.dom) {
2587             return "above";
2588         }
2589         var t = Roo.lib.Dom.getY(n), b = t + n.offsetHeight;
2590         var c = t + (b - t) / 2;
2591         var y = Roo.lib.Event.getPageY(e);
2592         if(y <= c) {
2593             return "above";
2594         }else{
2595             return "below";
2596         }
2597     },
2598     onToggleCollapse : function(e)
2599         {
2600         if (this.collapsed) {
2601             this.el.select('.roo-collapse-toggle').removeClass('collapsed');
2602             this.collapsableEl.addClass('show');
2603             this.collapsed = false;
2604             return;
2605         }
2606         this.el.select('.roo-collapse-toggle').addClass('collapsed');
2607         this.collapsableEl.removeClass('show');
2608         this.collapsed = true;
2609         
2610     
2611     },
2612     
2613     onToggleRotate : function(e)
2614     {
2615         this.collapsableEl.removeClass('show');
2616         this.footerEl.removeClass('d-none');
2617         this.el.removeClass('roo-card-rotated');
2618         this.el.removeClass('d-none');
2619         if (this.rotated) {
2620             
2621             this.collapsableEl.addClass('show');
2622             this.rotated = false;
2623             this.fireEvent('rotate', this, this.rotated);
2624             return;
2625         }
2626         this.el.addClass('roo-card-rotated');
2627         this.footerEl.addClass('d-none');
2628         this.el.select('.roo-collapsable').removeClass('show');
2629         
2630         this.rotated = true;
2631         this.fireEvent('rotate', this, this.rotated);
2632     
2633     },
2634     
2635     dropPlaceHolder: function (action, info, data)
2636     {
2637         if (this.dropEl === false) {
2638             this.dropEl = Roo.DomHelper.append(this.containerEl, {
2639             cls : 'd-none'
2640             },true);
2641         }
2642         this.dropEl.removeClass(['d-none', 'd-block']);        
2643         if (action == 'hide') {
2644             
2645             this.dropEl.addClass('d-none');
2646             return;
2647         }
2648         // FIXME - info.card == true!!!
2649         this.dropEl.dom.parentNode.removeChild(this.dropEl.dom);
2650         
2651         if (info.card !== true) {
2652             var cardel = info.card.el.dom;
2653             
2654             if (info.position == 'above') {
2655                 cardel.parentNode.insertBefore(this.dropEl.dom, cardel);
2656             } else if (cardel.nextSibling) {
2657                 cardel.parentNode.insertBefore(this.dropEl.dom,cardel.nextSibling);
2658             } else {
2659                 cardel.parentNode.append(this.dropEl.dom);
2660             }
2661         } else {
2662             // card container???
2663             this.containerEl.dom.append(this.dropEl.dom);
2664         }
2665         
2666         this.dropEl.addClass('d-block roo-card-dropzone');
2667         
2668         this.dropEl.setHeight( Roo.get(data.ddel).getHeight() );
2669         
2670         
2671     
2672     
2673     
2674     },
2675     setHeaderText: function(html)
2676     {
2677         this.headerContainerEl.dom.innerHTML = html;
2678     }
2679
2680     
2681 });
2682
2683 /*
2684  * - LGPL
2685  *
2686  * Card header - holder for the card header elements.
2687  * 
2688  */
2689
2690 /**
2691  * @class Roo.bootstrap.CardHeader
2692  * @extends Roo.bootstrap.Element
2693  * Bootstrap CardHeader class
2694  * @constructor
2695  * Create a new Card Header - that you can embed children into
2696  * @param {Object} config The config object
2697  */
2698
2699 Roo.bootstrap.CardHeader = function(config){
2700     Roo.bootstrap.CardHeader.superclass.constructor.call(this, config);
2701 };
2702
2703 Roo.extend(Roo.bootstrap.CardHeader, Roo.bootstrap.Element,  {
2704     
2705     
2706     container_method : 'getCardHeader' 
2707     
2708      
2709     
2710     
2711    
2712 });
2713
2714  
2715
2716  /*
2717  * - LGPL
2718  *
2719  * Card footer - holder for the card footer elements.
2720  * 
2721  */
2722
2723 /**
2724  * @class Roo.bootstrap.CardFooter
2725  * @extends Roo.bootstrap.Element
2726  * Bootstrap CardFooter class
2727  * @constructor
2728  * Create a new Card Footer - that you can embed children into
2729  * @param {Object} config The config object
2730  */
2731
2732 Roo.bootstrap.CardFooter = function(config){
2733     Roo.bootstrap.CardFooter.superclass.constructor.call(this, config);
2734 };
2735
2736 Roo.extend(Roo.bootstrap.CardFooter, Roo.bootstrap.Element,  {
2737     
2738     
2739     container_method : 'getCardFooter' 
2740     
2741      
2742     
2743     
2744    
2745 });
2746
2747  
2748
2749  /*
2750  * - LGPL
2751  *
2752  * Card header - holder for the card header elements.
2753  * 
2754  */
2755
2756 /**
2757  * @class Roo.bootstrap.CardImageTop
2758  * @extends Roo.bootstrap.Element
2759  * Bootstrap CardImageTop class
2760  * @constructor
2761  * Create a new Card Image Top container
2762  * @param {Object} config The config object
2763  */
2764
2765 Roo.bootstrap.CardImageTop = function(config){
2766     Roo.bootstrap.CardImageTop.superclass.constructor.call(this, config);
2767 };
2768
2769 Roo.extend(Roo.bootstrap.CardImageTop, Roo.bootstrap.Element,  {
2770     
2771    
2772     container_method : 'getCardImageTop' 
2773     
2774      
2775     
2776    
2777 });
2778
2779  
2780
2781  /*
2782  * - LGPL
2783  *
2784  * image
2785  * 
2786  */
2787
2788
2789 /**
2790  * @class Roo.bootstrap.Img
2791  * @extends Roo.bootstrap.Component
2792  * Bootstrap Img class
2793  * @cfg {Boolean} imgResponsive false | true
2794  * @cfg {String} border rounded | circle | thumbnail
2795  * @cfg {String} src image source
2796  * @cfg {String} alt image alternative text
2797  * @cfg {String} href a tag href
2798  * @cfg {String} target (_self|_blank|_parent|_top)target for a href.
2799  * @cfg {String} xsUrl xs image source
2800  * @cfg {String} smUrl sm image source
2801  * @cfg {String} mdUrl md image source
2802  * @cfg {String} lgUrl lg image source
2803  * 
2804  * @constructor
2805  * Create a new Input
2806  * @param {Object} config The config object
2807  */
2808
2809 Roo.bootstrap.Img = function(config){
2810     Roo.bootstrap.Img.superclass.constructor.call(this, config);
2811     
2812     this.addEvents({
2813         // img events
2814         /**
2815          * @event click
2816          * The img click event for the img.
2817          * @param {Roo.EventObject} e
2818          */
2819         "click" : true
2820     });
2821 };
2822
2823 Roo.extend(Roo.bootstrap.Img, Roo.bootstrap.Component,  {
2824     
2825     imgResponsive: true,
2826     border: '',
2827     src: 'about:blank',
2828     href: false,
2829     target: false,
2830     xsUrl: '',
2831     smUrl: '',
2832     mdUrl: '',
2833     lgUrl: '',
2834
2835     getAutoCreate : function()
2836     {   
2837         if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
2838             return this.createSingleImg();
2839         }
2840         
2841         var cfg = {
2842             tag: 'div',
2843             cls: 'roo-image-responsive-group',
2844             cn: []
2845         };
2846         var _this = this;
2847         
2848         Roo.each(['xs', 'sm', 'md', 'lg'], function(size){
2849             
2850             if(!_this[size + 'Url']){
2851                 return;
2852             }
2853             
2854             var img = {
2855                 tag: 'img',
2856                 cls: (_this.imgResponsive) ? 'img-responsive' : '',
2857                 html: _this.html || cfg.html,
2858                 src: _this[size + 'Url']
2859             };
2860             
2861             img.cls += ' roo-image-responsive-' + size;
2862             
2863             var s = ['xs', 'sm', 'md', 'lg'];
2864             
2865             s.splice(s.indexOf(size), 1);
2866             
2867             Roo.each(s, function(ss){
2868                 img.cls += ' hidden-' + ss;
2869             });
2870             
2871             if (['rounded','circle','thumbnail'].indexOf(_this.border)>-1) {
2872                 cfg.cls += ' img-' + _this.border;
2873             }
2874             
2875             if(_this.alt){
2876                 cfg.alt = _this.alt;
2877             }
2878             
2879             if(_this.href){
2880                 var a = {
2881                     tag: 'a',
2882                     href: _this.href,
2883                     cn: [
2884                         img
2885                     ]
2886                 };
2887
2888                 if(this.target){
2889                     a.target = _this.target;
2890                 }
2891             }
2892             
2893             cfg.cn.push((_this.href) ? a : img);
2894             
2895         });
2896         
2897         return cfg;
2898     },
2899     
2900     createSingleImg : function()
2901     {
2902         var cfg = {
2903             tag: 'img',
2904             cls: (this.imgResponsive) ? 'img-responsive' : '',
2905             html : null,
2906             src : 'about:blank'  // just incase src get's set to undefined?!?
2907         };
2908         
2909         cfg.html = this.html || cfg.html;
2910         
2911         cfg.src = this.src || cfg.src;
2912         
2913         if (['rounded','circle','thumbnail'].indexOf(this.border)>-1) {
2914             cfg.cls += ' img-' + this.border;
2915         }
2916         
2917         if(this.alt){
2918             cfg.alt = this.alt;
2919         }
2920         
2921         if(this.href){
2922             var a = {
2923                 tag: 'a',
2924                 href: this.href,
2925                 cn: [
2926                     cfg
2927                 ]
2928             };
2929             
2930             if(this.target){
2931                 a.target = this.target;
2932             }
2933             
2934         }
2935         
2936         return (this.href) ? a : cfg;
2937     },
2938     
2939     initEvents: function() 
2940     {
2941         if(!this.href){
2942             this.el.on('click', this.onClick, this);
2943         }
2944         
2945     },
2946     
2947     onClick : function(e)
2948     {
2949         Roo.log('img onclick');
2950         this.fireEvent('click', this, e);
2951     },
2952     /**
2953      * Sets the url of the image - used to update it
2954      * @param {String} url the url of the image
2955      */
2956     
2957     setSrc : function(url)
2958     {
2959         this.src =  url;
2960         
2961         if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
2962             this.el.dom.src =  url;
2963             return;
2964         }
2965         
2966         this.el.select('img', true).first().dom.src =  url;
2967     }
2968     
2969     
2970    
2971 });
2972
2973  /*
2974  * - LGPL
2975  *
2976  * image
2977  * 
2978  */
2979
2980
2981 /**
2982  * @class Roo.bootstrap.Link
2983  * @extends Roo.bootstrap.Component
2984  * Bootstrap Link Class
2985  * @cfg {String} alt image alternative text
2986  * @cfg {String} href a tag href
2987  * @cfg {String} target (_self|_blank|_parent|_top) target for a href.
2988  * @cfg {String} html the content of the link.
2989  * @cfg {String} anchor name for the anchor link
2990  * @cfg {String} fa - favicon
2991
2992  * @cfg {Boolean} preventDefault (true | false) default false
2993
2994  * 
2995  * @constructor
2996  * Create a new Input
2997  * @param {Object} config The config object
2998  */
2999
3000 Roo.bootstrap.Link = function(config){
3001     Roo.bootstrap.Link.superclass.constructor.call(this, config);
3002     
3003     this.addEvents({
3004         // img events
3005         /**
3006          * @event click
3007          * The img click event for the img.
3008          * @param {Roo.EventObject} e
3009          */
3010         "click" : true
3011     });
3012 };
3013
3014 Roo.extend(Roo.bootstrap.Link, Roo.bootstrap.Component,  {
3015     
3016     href: false,
3017     target: false,
3018     preventDefault: false,
3019     anchor : false,
3020     alt : false,
3021     fa: false,
3022
3023
3024     getAutoCreate : function()
3025     {
3026         var html = this.html || '';
3027         
3028         if (this.fa !== false) {
3029             html = '<i class="fa fa-' + this.fa + '"></i>';
3030         }
3031         var cfg = {
3032             tag: 'a'
3033         };
3034         // anchor's do not require html/href...
3035         if (this.anchor === false) {
3036             cfg.html = html;
3037             cfg.href = this.href || '#';
3038         } else {
3039             cfg.name = this.anchor;
3040             if (this.html !== false || this.fa !== false) {
3041                 cfg.html = html;
3042             }
3043             if (this.href !== false) {
3044                 cfg.href = this.href;
3045             }
3046         }
3047         
3048         if(this.alt !== false){
3049             cfg.alt = this.alt;
3050         }
3051         
3052         
3053         if(this.target !== false) {
3054             cfg.target = this.target;
3055         }
3056         
3057         return cfg;
3058     },
3059     
3060     initEvents: function() {
3061         
3062         if(!this.href || this.preventDefault){
3063             this.el.on('click', this.onClick, this);
3064         }
3065     },
3066     
3067     onClick : function(e)
3068     {
3069         if(this.preventDefault){
3070             e.preventDefault();
3071         }
3072         //Roo.log('img onclick');
3073         this.fireEvent('click', this, e);
3074     }
3075    
3076 });
3077
3078  /*
3079  * - LGPL
3080  *
3081  * header
3082  * 
3083  */
3084
3085 /**
3086  * @class Roo.bootstrap.Header
3087  * @extends Roo.bootstrap.Component
3088  * Bootstrap Header class
3089  * @cfg {String} html content of header
3090  * @cfg {Number} level (1|2|3|4|5|6) default 1
3091  * 
3092  * @constructor
3093  * Create a new Header
3094  * @param {Object} config The config object
3095  */
3096
3097
3098 Roo.bootstrap.Header  = function(config){
3099     Roo.bootstrap.Header.superclass.constructor.call(this, config);
3100 };
3101
3102 Roo.extend(Roo.bootstrap.Header, Roo.bootstrap.Component,  {
3103     
3104     //href : false,
3105     html : false,
3106     level : 1,
3107     
3108     
3109     
3110     getAutoCreate : function(){
3111         
3112         
3113         
3114         var cfg = {
3115             tag: 'h' + (1 *this.level),
3116             html: this.html || ''
3117         } ;
3118         
3119         return cfg;
3120     }
3121    
3122 });
3123
3124  
3125
3126  /*
3127  * Based on:
3128  * Ext JS Library 1.1.1
3129  * Copyright(c) 2006-2007, Ext JS, LLC.
3130  *
3131  * Originally Released Under LGPL - original licence link has changed is not relivant.
3132  *
3133  * Fork - LGPL
3134  * <script type="text/javascript">
3135  */
3136  
3137 /**
3138  * @class Roo.bootstrap.MenuMgr
3139  * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
3140  * @singleton
3141  */
3142 Roo.bootstrap.MenuMgr = function(){
3143    var menus, active, groups = {}, attached = false, lastShow = new Date();
3144
3145    // private - called when first menu is created
3146    function init(){
3147        menus = {};
3148        active = new Roo.util.MixedCollection();
3149        Roo.get(document).addKeyListener(27, function(){
3150            if(active.length > 0){
3151                hideAll();
3152            }
3153        });
3154    }
3155
3156    // private
3157    function hideAll(){
3158        if(active && active.length > 0){
3159            var c = active.clone();
3160            c.each(function(m){
3161                m.hide();
3162            });
3163        }
3164    }
3165
3166    // private
3167    function onHide(m){
3168        active.remove(m);
3169        if(active.length < 1){
3170            Roo.get(document).un("mouseup", onMouseDown);
3171             
3172            attached = false;
3173        }
3174    }
3175
3176    // private
3177    function onShow(m){
3178        var last = active.last();
3179        lastShow = new Date();
3180        active.add(m);
3181        if(!attached){
3182           Roo.get(document).on("mouseup", onMouseDown);
3183            
3184            attached = true;
3185        }
3186        if(m.parentMenu){
3187           //m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
3188           m.parentMenu.activeChild = m;
3189        }else if(last && last.isVisible()){
3190           //m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
3191        }
3192    }
3193
3194    // private
3195    function onBeforeHide(m){
3196        if(m.activeChild){
3197            m.activeChild.hide();
3198        }
3199        if(m.autoHideTimer){
3200            clearTimeout(m.autoHideTimer);
3201            delete m.autoHideTimer;
3202        }
3203    }
3204
3205    // private
3206    function onBeforeShow(m){
3207        var pm = m.parentMenu;
3208        if(!pm && !m.allowOtherMenus){
3209            hideAll();
3210        }else if(pm && pm.activeChild && active != m){
3211            pm.activeChild.hide();
3212        }
3213    }
3214
3215    // private this should really trigger on mouseup..
3216    function onMouseDown(e){
3217         Roo.log("on Mouse Up");
3218         
3219         if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".dropdown-menu") && !e.getTarget('.user-menu')){
3220             Roo.log("MenuManager hideAll");
3221             hideAll();
3222             e.stopEvent();
3223         }
3224         
3225         
3226    }
3227
3228    // private
3229    function onBeforeCheck(mi, state){
3230        if(state){
3231            var g = groups[mi.group];
3232            for(var i = 0, l = g.length; i < l; i++){
3233                if(g[i] != mi){
3234                    g[i].setChecked(false);
3235                }
3236            }
3237        }
3238    }
3239
3240    return {
3241
3242        /**
3243         * Hides all menus that are currently visible
3244         */
3245        hideAll : function(){
3246             hideAll();  
3247        },
3248
3249        // private
3250        register : function(menu){
3251            if(!menus){
3252                init();
3253            }
3254            menus[menu.id] = menu;
3255            menu.on("beforehide", onBeforeHide);
3256            menu.on("hide", onHide);
3257            menu.on("beforeshow", onBeforeShow);
3258            menu.on("show", onShow);
3259            var g = menu.group;
3260            if(g && menu.events["checkchange"]){
3261                if(!groups[g]){
3262                    groups[g] = [];
3263                }
3264                groups[g].push(menu);
3265                menu.on("checkchange", onCheck);
3266            }
3267        },
3268
3269         /**
3270          * Returns a {@link Roo.menu.Menu} object
3271          * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
3272          * be used to generate and return a new Menu instance.
3273          */
3274        get : function(menu){
3275            if(typeof menu == "string"){ // menu id
3276                return menus[menu];
3277            }else if(menu.events){  // menu instance
3278                return menu;
3279            }
3280            /*else if(typeof menu.length == 'number'){ // array of menu items?
3281                return new Roo.bootstrap.Menu({items:menu});
3282            }else{ // otherwise, must be a config
3283                return new Roo.bootstrap.Menu(menu);
3284            }
3285            */
3286            return false;
3287        },
3288
3289        // private
3290        unregister : function(menu){
3291            delete menus[menu.id];
3292            menu.un("beforehide", onBeforeHide);
3293            menu.un("hide", onHide);
3294            menu.un("beforeshow", onBeforeShow);
3295            menu.un("show", onShow);
3296            var g = menu.group;
3297            if(g && menu.events["checkchange"]){
3298                groups[g].remove(menu);
3299                menu.un("checkchange", onCheck);
3300            }
3301        },
3302
3303        // private
3304        registerCheckable : function(menuItem){
3305            var g = menuItem.group;
3306            if(g){
3307                if(!groups[g]){
3308                    groups[g] = [];
3309                }
3310                groups[g].push(menuItem);
3311                menuItem.on("beforecheckchange", onBeforeCheck);
3312            }
3313        },
3314
3315        // private
3316        unregisterCheckable : function(menuItem){
3317            var g = menuItem.group;
3318            if(g){
3319                groups[g].remove(menuItem);
3320                menuItem.un("beforecheckchange", onBeforeCheck);
3321            }
3322        }
3323    };
3324 }();/*
3325  * - LGPL
3326  *
3327  * menu
3328  * 
3329  */
3330
3331 /**
3332  * @class Roo.bootstrap.Menu
3333  * @extends Roo.bootstrap.Component
3334  * Bootstrap Menu class - container for MenuItems
3335  * @cfg {String} type (dropdown|treeview|submenu) type of menu
3336  * @cfg {bool} hidden  if the menu should be hidden when rendered.
3337  * @cfg {bool} stopEvent (true|false)  Stop event after trigger press (default true)
3338  * @cfg {bool} isLink (true|false)  the menu has link disable auto expand and collaspe (default false)
3339  * 
3340  * @constructor
3341  * Create a new Menu
3342  * @param {Object} config The config object
3343  */
3344
3345
3346 Roo.bootstrap.Menu = function(config){
3347     Roo.bootstrap.Menu.superclass.constructor.call(this, config);
3348     if (this.registerMenu && this.type != 'treeview')  {
3349         Roo.bootstrap.MenuMgr.register(this);
3350     }
3351     
3352     
3353     this.addEvents({
3354         /**
3355          * @event beforeshow
3356          * Fires before this menu is displayed (return false to block)
3357          * @param {Roo.menu.Menu} this
3358          */
3359         beforeshow : true,
3360         /**
3361          * @event beforehide
3362          * Fires before this menu is hidden (return false to block)
3363          * @param {Roo.menu.Menu} this
3364          */
3365         beforehide : true,
3366         /**
3367          * @event show
3368          * Fires after this menu is displayed
3369          * @param {Roo.menu.Menu} this
3370          */
3371         show : true,
3372         /**
3373          * @event hide
3374          * Fires after this menu is hidden
3375          * @param {Roo.menu.Menu} this
3376          */
3377         hide : true,
3378         /**
3379          * @event click
3380          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
3381          * @param {Roo.menu.Menu} this
3382          * @param {Roo.menu.Item} menuItem The menu item that was clicked
3383          * @param {Roo.EventObject} e
3384          */
3385         click : true,
3386         /**
3387          * @event mouseover
3388          * Fires when the mouse is hovering over this menu
3389          * @param {Roo.menu.Menu} this
3390          * @param {Roo.EventObject} e
3391          * @param {Roo.menu.Item} menuItem The menu item that was clicked
3392          */
3393         mouseover : true,
3394         /**
3395          * @event mouseout
3396          * Fires when the mouse exits this menu
3397          * @param {Roo.menu.Menu} this
3398          * @param {Roo.EventObject} e
3399          * @param {Roo.menu.Item} menuItem The menu item that was clicked
3400          */
3401         mouseout : true,
3402         /**
3403          * @event itemclick
3404          * Fires when a menu item contained in this menu is clicked
3405          * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
3406          * @param {Roo.EventObject} e
3407          */
3408         itemclick: true
3409     });
3410     this.menuitems = new Roo.util.MixedCollection(false, function(o) { return o.el.id; });
3411 };
3412
3413 Roo.extend(Roo.bootstrap.Menu, Roo.bootstrap.Component,  {
3414     
3415    /// html : false,
3416     //align : '',
3417     triggerEl : false,  // is this set by component builder? -- it should really be fetched from parent()???
3418     type: false,
3419     /**
3420      * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
3421      */
3422     registerMenu : true,
3423     
3424     menuItems :false, // stores the menu items..
3425     
3426     hidden:true,
3427         
3428     parentMenu : false,
3429     
3430     stopEvent : true,
3431     
3432     isLink : false,
3433     
3434     getChildContainer : function() {
3435         return this.el;  
3436     },
3437     
3438     getAutoCreate : function(){
3439          
3440         //if (['right'].indexOf(this.align)!==-1) {
3441         //    cfg.cn[1].cls += ' pull-right'
3442         //}
3443         
3444         
3445         var cfg = {
3446             tag : 'ul',
3447             cls : 'dropdown-menu' ,
3448             style : 'z-index:1000'
3449             
3450         };
3451         
3452         if (this.type === 'submenu') {
3453             cfg.cls = 'submenu active';
3454         }
3455         if (this.type === 'treeview') {
3456             cfg.cls = 'treeview-menu';
3457         }
3458         
3459         return cfg;
3460     },
3461     initEvents : function() {
3462         
3463        // Roo.log("ADD event");
3464        // Roo.log(this.triggerEl.dom);
3465         
3466         this.triggerEl.on('click', this.onTriggerClick, this);
3467         
3468         this.triggerEl.on(Roo.isTouch ? 'touchstart' : 'mouseup', this.onTriggerPress, this);
3469         
3470         
3471         if (this.triggerEl.hasClass('nav-item')) {
3472             // dropdown toggle on the 'a' in BS4?
3473             this.triggerEl.select('.nav-link',true).first().addClass('dropdown-toggle');
3474         } else {
3475             this.triggerEl.addClass('dropdown-toggle');
3476         }
3477         if (Roo.isTouch) {
3478             this.el.on('touchstart'  , this.onTouch, this);
3479         }
3480         this.el.on('click' , this.onClick, this);
3481
3482         this.el.on("mouseover", this.onMouseOver, this);
3483         this.el.on("mouseout", this.onMouseOut, this);
3484         
3485     },
3486     
3487     findTargetItem : function(e)
3488     {
3489         var t = e.getTarget(".dropdown-menu-item", this.el,  true);
3490         if(!t){
3491             return false;
3492         }
3493         //Roo.log(t);         Roo.log(t.id);
3494         if(t && t.id){
3495             //Roo.log(this.menuitems);
3496             return this.menuitems.get(t.id);
3497             
3498             //return this.items.get(t.menuItemId);
3499         }
3500         
3501         return false;
3502     },
3503     
3504     onTouch : function(e) 
3505     {
3506         Roo.log("menu.onTouch");
3507         //e.stopEvent(); this make the user popdown broken
3508         this.onClick(e);
3509     },
3510     
3511     onClick : function(e)
3512     {
3513         Roo.log("menu.onClick");
3514         
3515         var t = this.findTargetItem(e);
3516         if(!t || t.isContainer){
3517             return;
3518         }
3519         Roo.log(e);
3520         /*
3521         if (Roo.isTouch && e.type == 'touchstart' && t.menu  && !t.disabled) {
3522             if(t == this.activeItem && t.shouldDeactivate(e)){
3523                 this.activeItem.deactivate();
3524                 delete this.activeItem;
3525                 return;
3526             }
3527             if(t.canActivate){
3528                 this.setActiveItem(t, true);
3529             }
3530             return;
3531             
3532             
3533         }
3534         */
3535        
3536         Roo.log('pass click event');
3537         
3538         t.onClick(e);
3539         
3540         this.fireEvent("click", this, t, e);
3541         
3542         var _this = this;
3543         
3544         if(!t.href.length || t.href == '#'){
3545             (function() { _this.hide(); }).defer(100);
3546         }
3547         
3548     },
3549     
3550     onMouseOver : function(e){
3551         var t  = this.findTargetItem(e);
3552         //Roo.log(t);
3553         //if(t){
3554         //    if(t.canActivate && !t.disabled){
3555         //        this.setActiveItem(t, true);
3556         //    }
3557         //}
3558         
3559         this.fireEvent("mouseover", this, e, t);
3560     },
3561     isVisible : function(){
3562         return !this.hidden;
3563     },
3564     onMouseOut : function(e){
3565         var t  = this.findTargetItem(e);
3566         
3567         //if(t ){
3568         //    if(t == this.activeItem && t.shouldDeactivate(e)){
3569         //        this.activeItem.deactivate();
3570         //        delete this.activeItem;
3571         //    }
3572         //}
3573         this.fireEvent("mouseout", this, e, t);
3574     },
3575     
3576     
3577     /**
3578      * Displays this menu relative to another element
3579      * @param {String/HTMLElement/Roo.Element} element The element to align to
3580      * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
3581      * the element (defaults to this.defaultAlign)
3582      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
3583      */
3584     show : function(el, pos, parentMenu)
3585     {
3586         if (false === this.fireEvent("beforeshow", this)) {
3587             Roo.log("show canceled");
3588             return;
3589         }
3590         this.parentMenu = parentMenu;
3591         if(!this.el){
3592             this.render();
3593         }
3594         
3595         this.showAt(this.el.getAlignToXY(el, pos || this.defaultAlign), parentMenu, false);
3596     },
3597      /**
3598      * Displays this menu at a specific xy position
3599      * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
3600      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
3601      */
3602     showAt : function(xy, parentMenu, /* private: */_e){
3603         this.parentMenu = parentMenu;
3604         if(!this.el){
3605             this.render();
3606         }
3607         if(_e !== false){
3608             this.fireEvent("beforeshow", this);
3609             //xy = this.el.adjustForConstraints(xy);
3610         }
3611         
3612         //this.el.show();
3613         this.hideMenuItems();
3614         this.hidden = false;
3615         this.triggerEl.addClass('open');
3616         this.el.addClass('show');
3617         
3618         // reassign x when hitting right
3619         if(this.el.getWidth() + xy[0] >= Roo.lib.Dom.getViewWidth()){
3620             xy[0] = xy[0] - this.el.getWidth() + this.triggerEl.getWidth();
3621         }
3622         
3623         // reassign y when hitting bottom
3624         if(this.el.getHeight() + xy[1] >= Roo.lib.Dom.getViewHeight()){
3625             xy[1] = xy[1] - this.el.getHeight() - this.triggerEl.getHeight();
3626         }
3627         
3628         // but the list may align on trigger left or trigger top... should it be a properity?
3629         
3630         if(this.el.getStyle('top') != 'auto' && this.el.getStyle('top').slice(-1) != "%"){
3631             this.el.setXY(xy);
3632         }
3633         
3634         this.focus();
3635         this.fireEvent("show", this);
3636     },
3637     
3638     focus : function(){
3639         return;
3640         if(!this.hidden){
3641             this.doFocus.defer(50, this);
3642         }
3643     },
3644
3645     doFocus : function(){
3646         if(!this.hidden){
3647             this.focusEl.focus();
3648         }
3649     },
3650
3651     /**
3652      * Hides this menu and optionally all parent menus
3653      * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
3654      */
3655     hide : function(deep)
3656     {
3657         if (false === this.fireEvent("beforehide", this)) {
3658             Roo.log("hide canceled");
3659             return;
3660         }
3661         this.hideMenuItems();
3662         if(this.el && this.isVisible()){
3663            
3664             if(this.activeItem){
3665                 this.activeItem.deactivate();
3666                 this.activeItem = null;
3667             }
3668             this.triggerEl.removeClass('open');;
3669             this.el.removeClass('show');
3670             this.hidden = true;
3671             this.fireEvent("hide", this);
3672         }
3673         if(deep === true && this.parentMenu){
3674             this.parentMenu.hide(true);
3675         }
3676     },
3677     
3678     onTriggerClick : function(e)
3679     {
3680         Roo.log('trigger click');
3681         
3682         var target = e.getTarget();
3683         
3684         Roo.log(target.nodeName.toLowerCase());
3685         
3686         if(target.nodeName.toLowerCase() === 'i'){
3687             e.preventDefault();
3688         }
3689         
3690     },
3691     
3692     onTriggerPress  : function(e)
3693     {
3694         Roo.log('trigger press');
3695         //Roo.log(e.getTarget());
3696        // Roo.log(this.triggerEl.dom);
3697        
3698         // trigger only occurs on normal menu's -- if it's a treeview or dropdown... do not hide/show..
3699         var pel = Roo.get(e.getTarget());
3700         if (pel.findParent('.dropdown-menu') || pel.findParent('.treeview-menu') ) {
3701             Roo.log('is treeview or dropdown?');
3702             return;
3703         }
3704         
3705         if(e.getTarget().nodeName.toLowerCase() !== 'i' && this.isLink){
3706             return;
3707         }
3708         
3709         if (this.isVisible()) {
3710             Roo.log('hide');
3711             this.hide();
3712         } else {
3713             Roo.log('show');
3714             this.show(this.triggerEl, '?', false);
3715         }
3716         
3717         if(this.stopEvent || e.getTarget().nodeName.toLowerCase() === 'i'){
3718             e.stopEvent();
3719         }
3720         
3721     },
3722        
3723     
3724     hideMenuItems : function()
3725     {
3726         Roo.log("hide Menu Items");
3727         if (!this.el) { 
3728             return;
3729         }
3730         
3731         this.el.select('.open',true).each(function(aa) {
3732             
3733             aa.removeClass('open');
3734          
3735         });
3736     },
3737     addxtypeChild : function (tree, cntr) {
3738         var comp= Roo.bootstrap.Menu.superclass.addxtypeChild.call(this, tree, cntr);
3739           
3740         this.menuitems.add(comp);
3741         return comp;
3742
3743     },
3744     getEl : function()
3745     {
3746         Roo.log(this.el);
3747         return this.el;
3748     },
3749     
3750     clear : function()
3751     {
3752         this.getEl().dom.innerHTML = '';
3753         this.menuitems.clear();
3754     }
3755 });
3756
3757  
3758  /*
3759  * - LGPL
3760  *
3761  * menu item
3762  * 
3763  */
3764
3765
3766 /**
3767  * @class Roo.bootstrap.MenuItem
3768  * @extends Roo.bootstrap.Component
3769  * Bootstrap MenuItem class
3770  * @cfg {String} html the menu label
3771  * @cfg {String} href the link
3772  * @cfg {Boolean} preventDefault do not trigger A href on clicks (default false).
3773  * @cfg {Boolean} isContainer is it a container - just returns a drop down item..
3774  * @cfg {Boolean} active  used on sidebars to highlight active itesm
3775  * @cfg {String} fa favicon to show on left of menu item.
3776  * @cfg {Roo.bootsrap.Menu} menu the child menu.
3777  * 
3778  * 
3779  * @constructor
3780  * Create a new MenuItem
3781  * @param {Object} config The config object
3782  */
3783
3784
3785 Roo.bootstrap.MenuItem = function(config){
3786     Roo.bootstrap.MenuItem.superclass.constructor.call(this, config);
3787     this.addEvents({
3788         // raw events
3789         /**
3790          * @event click
3791          * The raw click event for the entire grid.
3792          * @param {Roo.bootstrap.MenuItem} this
3793          * @param {Roo.EventObject} e
3794          */
3795         "click" : true
3796     });
3797 };
3798
3799 Roo.extend(Roo.bootstrap.MenuItem, Roo.bootstrap.Component,  {
3800     
3801     href : false,
3802     html : false,
3803     preventDefault: false,
3804     isContainer : false,
3805     active : false,
3806     fa: false,
3807     
3808     getAutoCreate : function(){
3809         
3810         if(this.isContainer){
3811             return {
3812                 tag: 'li',
3813                 cls: 'dropdown-menu-item '
3814             };
3815         }
3816         var ctag = {
3817             tag: 'span',
3818             html: 'Link'
3819         };
3820         
3821         var anc = {
3822             tag : 'a',
3823             cls : 'dropdown-item',
3824             href : '#',
3825             cn : [  ]
3826         };
3827         
3828         if (this.fa !== false) {
3829             anc.cn.push({
3830                 tag : 'i',
3831                 cls : 'fa fa-' + this.fa
3832             });
3833         }
3834         
3835         anc.cn.push(ctag);
3836         
3837         
3838         var cfg= {
3839             tag: 'li',
3840             cls: 'dropdown-menu-item',
3841             cn: [ anc ]
3842         };
3843         if (this.parent().type == 'treeview') {
3844             cfg.cls = 'treeview-menu';
3845         }
3846         if (this.active) {
3847             cfg.cls += ' active';
3848         }
3849         
3850         
3851         
3852         anc.href = this.href || cfg.cn[0].href ;
3853         ctag.html = this.html || cfg.cn[0].html ;
3854         return cfg;
3855     },
3856     
3857     initEvents: function()
3858     {
3859         if (this.parent().type == 'treeview') {
3860             this.el.select('a').on('click', this.onClick, this);
3861         }
3862         
3863         if (this.menu) {
3864             this.menu.parentType = this.xtype;
3865             this.menu.triggerEl = this.el;
3866             this.menu = this.addxtype(Roo.apply({}, this.menu));
3867         }
3868         
3869     },
3870     onClick : function(e)
3871     {
3872         Roo.log('item on click ');
3873         
3874         if(this.preventDefault){
3875             e.preventDefault();
3876         }
3877         //this.parent().hideMenuItems();
3878         
3879         this.fireEvent('click', this, e);
3880     },
3881     getEl : function()
3882     {
3883         return this.el;
3884     } 
3885 });
3886
3887  
3888
3889  /*
3890  * - LGPL
3891  *
3892  * menu separator
3893  * 
3894  */
3895
3896
3897 /**
3898  * @class Roo.bootstrap.MenuSeparator
3899  * @extends Roo.bootstrap.Component
3900  * Bootstrap MenuSeparator class
3901  * 
3902  * @constructor
3903  * Create a new MenuItem
3904  * @param {Object} config The config object
3905  */
3906
3907
3908 Roo.bootstrap.MenuSeparator = function(config){
3909     Roo.bootstrap.MenuSeparator.superclass.constructor.call(this, config);
3910 };
3911
3912 Roo.extend(Roo.bootstrap.MenuSeparator, Roo.bootstrap.Component,  {
3913     
3914     getAutoCreate : function(){
3915         var cfg = {
3916             cls: 'divider',
3917             tag : 'li'
3918         };
3919         
3920         return cfg;
3921     }
3922    
3923 });
3924
3925  
3926
3927  
3928 /*
3929 * Licence: LGPL
3930 */
3931
3932 /**
3933  * @class Roo.bootstrap.Modal
3934  * @extends Roo.bootstrap.Component
3935  * Bootstrap Modal class
3936  * @cfg {String} title Title of dialog
3937  * @cfg {String} html - the body of the dialog (for simple ones) - you can also use template..
3938  * @cfg {Roo.Template} tmpl - a template with variables. to use it, add a handler in show:method  adn
3939  * @cfg {Boolean} specificTitle default false
3940  * @cfg {Array} buttons Array of buttons or standard button set..
3941  * @cfg {String} buttonPosition (left|right|center) default right (DEPRICATED) - use mr-auto on buttons to put them on the left
3942  * @cfg {Boolean} animate default true
3943  * @cfg {Boolean} allow_close default true
3944  * @cfg {Boolean} fitwindow default false
3945  * @cfg {Number} width fixed width - usefull for chrome extension only really.
3946  * @cfg {Number} height fixed height - usefull for chrome extension only really.
3947  * @cfg {String} size (sm|lg|xl) default empty
3948  * @cfg {Number} max_width set the max width of modal
3949  * @cfg {Boolean} editableTitle can the title be edited
3950
3951  *
3952  *
3953  * @constructor
3954  * Create a new Modal Dialog
3955  * @param {Object} config The config object
3956  */
3957
3958 Roo.bootstrap.Modal = function(config){
3959     Roo.bootstrap.Modal.superclass.constructor.call(this, config);
3960     this.addEvents({
3961         // raw events
3962         /**
3963          * @event btnclick
3964          * The raw btnclick event for the button
3965          * @param {Roo.EventObject} e
3966          */
3967         "btnclick" : true,
3968         /**
3969          * @event resize
3970          * Fire when dialog resize
3971          * @param {Roo.bootstrap.Modal} this
3972          * @param {Roo.EventObject} e
3973          */
3974         "resize" : true,
3975         /**
3976          * @event titlechanged
3977          * Fire when the editable title has been changed
3978          * @param {Roo.bootstrap.Modal} this
3979          * @param {Roo.EventObject} value
3980          */
3981         "titlechanged" : true 
3982         
3983     });
3984     this.buttons = this.buttons || [];
3985
3986     if (this.tmpl) {
3987         this.tmpl = Roo.factory(this.tmpl);
3988     }
3989
3990 };
3991
3992 Roo.extend(Roo.bootstrap.Modal, Roo.bootstrap.Component,  {
3993
3994     title : 'test dialog',
3995
3996     buttons : false,
3997
3998     // set on load...
3999
4000     html: false,
4001
4002     tmp: false,
4003
4004     specificTitle: false,
4005
4006     buttonPosition: 'right',
4007
4008     allow_close : true,
4009
4010     animate : true,
4011
4012     fitwindow: false,
4013     
4014      // private
4015     dialogEl: false,
4016     bodyEl:  false,
4017     footerEl:  false,
4018     titleEl:  false,
4019     closeEl:  false,
4020
4021     size: '',
4022     
4023     max_width: 0,
4024     
4025     max_height: 0,
4026     
4027     fit_content: false,
4028     editableTitle  : false,
4029
4030     onRender : function(ct, position)
4031     {
4032         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
4033
4034         if(!this.el){
4035             var cfg = Roo.apply({},  this.getAutoCreate());
4036             cfg.id = Roo.id();
4037             //if(!cfg.name){
4038             //    cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
4039             //}
4040             //if (!cfg.name.length) {
4041             //    delete cfg.name;
4042            // }
4043             if (this.cls) {
4044                 cfg.cls += ' ' + this.cls;
4045             }
4046             if (this.style) {
4047                 cfg.style = this.style;
4048             }
4049             this.el = Roo.get(document.body).createChild(cfg, position);
4050         }
4051         //var type = this.el.dom.type;
4052
4053
4054         if(this.tabIndex !== undefined){
4055             this.el.dom.setAttribute('tabIndex', this.tabIndex);
4056         }
4057
4058         this.dialogEl = this.el.select('.modal-dialog',true).first();
4059         this.bodyEl = this.el.select('.modal-body',true).first();
4060         this.closeEl = this.el.select('.modal-header .close', true).first();
4061         this.headerEl = this.el.select('.modal-header',true).first();
4062         this.titleEl = this.el.select('.modal-title',true).first();
4063         this.footerEl = this.el.select('.modal-footer',true).first();
4064
4065         this.maskEl = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
4066         
4067         //this.el.addClass("x-dlg-modal");
4068
4069         if (this.buttons.length) {
4070             Roo.each(this.buttons, function(bb) {
4071                 var b = Roo.apply({}, bb);
4072                 b.xns = b.xns || Roo.bootstrap;
4073                 b.xtype = b.xtype || 'Button';
4074                 if (typeof(b.listeners) == 'undefined') {
4075                     b.listeners = { click : this.onButtonClick.createDelegate(this)  };
4076                 }
4077
4078                 var btn = Roo.factory(b);
4079
4080                 btn.render(this.getButtonContainer());
4081
4082             },this);
4083         }
4084         // render the children.
4085         var nitems = [];
4086
4087         if(typeof(this.items) != 'undefined'){
4088             var items = this.items;
4089             delete this.items;
4090
4091             for(var i =0;i < items.length;i++) {
4092                 nitems.push(this.addxtype(Roo.apply({}, items[i])));
4093             }
4094         }
4095
4096         this.items = nitems;
4097
4098         // where are these used - they used to be body/close/footer
4099
4100
4101         this.initEvents();
4102         //this.el.addClass([this.fieldClass, this.cls]);
4103
4104     },
4105
4106     getAutoCreate : function()
4107     {
4108         // we will default to modal-body-overflow - might need to remove or make optional later.
4109         var bdy = {
4110                 cls : 'modal-body enable-modal-body-overflow ', 
4111                 html : this.html || ''
4112         };
4113
4114         var title = {
4115             tag: 'h4',
4116             cls : 'modal-title',
4117             html : this.title
4118         };
4119
4120         if(this.specificTitle){ // WTF is this?
4121             title = this.title;
4122         }
4123
4124         var header = [];
4125         if (this.allow_close && Roo.bootstrap.version == 3) {
4126             header.push({
4127                 tag: 'button',
4128                 cls : 'close',
4129                 html : '&times'
4130             });
4131         }
4132
4133         header.push(title);
4134
4135         if (this.editableTitle) {
4136             header.push({
4137                 cls: 'form-control roo-editable-title d-none',
4138                 tag: 'input',
4139                 type: 'text'
4140             });
4141         }
4142         
4143         if (this.allow_close && Roo.bootstrap.version == 4) {
4144             header.push({
4145                 tag: 'button',
4146                 cls : 'close',
4147                 html : '&times'
4148             });
4149         }
4150         
4151         var size = '';
4152
4153         if(this.size.length){
4154             size = 'modal-' + this.size;
4155         }
4156         
4157         var footer = Roo.bootstrap.version == 3 ?
4158             {
4159                 cls : 'modal-footer',
4160                 cn : [
4161                     {
4162                         tag: 'div',
4163                         cls: 'btn-' + this.buttonPosition
4164                     }
4165                 ]
4166
4167             } :
4168             {  // BS4 uses mr-auto on left buttons....
4169                 cls : 'modal-footer'
4170             };
4171
4172             
4173
4174         
4175         
4176         var modal = {
4177             cls: "modal",
4178              cn : [
4179                 {
4180                     cls: "modal-dialog " + size,
4181                     cn : [
4182                         {
4183                             cls : "modal-content",
4184                             cn : [
4185                                 {
4186                                     cls : 'modal-header',
4187                                     cn : header
4188                                 },
4189                                 bdy,
4190                                 footer
4191                             ]
4192
4193                         }
4194                     ]
4195
4196                 }
4197             ]
4198         };
4199
4200         if(this.animate){
4201             modal.cls += ' fade';
4202         }
4203
4204         return modal;
4205
4206     },
4207     getChildContainer : function() {
4208
4209          return this.bodyEl;
4210
4211     },
4212     getButtonContainer : function() {
4213         
4214          return Roo.bootstrap.version == 4 ?
4215             this.el.select('.modal-footer',true).first()
4216             : this.el.select('.modal-footer div',true).first();
4217
4218     },
4219     initEvents : function()
4220     {
4221         if (this.allow_close) {
4222             this.closeEl.on('click', this.hide, this);
4223         }
4224         Roo.EventManager.onWindowResize(this.resize, this, true);
4225         if (this.editableTitle) {
4226             this.headerEditEl =  this.headerEl.select('.form-control',true).first();
4227             this.headerEl.on('click', function() { this.toggleHeaderInput(true) } , this);
4228             this.headerEditEl.on('keyup', function(e) {
4229                     if([  e.RETURN , e.TAB , e.ESC ].indexOf(e.keyCode) > -1) {
4230                         this.toggleHeaderInput(false)
4231                     }
4232                 }, this);
4233             this.headerEditEl.on('blur', function(e) {
4234                 this.toggleHeaderInput(false)
4235             },this);
4236         }
4237
4238     },
4239   
4240
4241     resize : function()
4242     {
4243         this.maskEl.setSize(
4244             Roo.lib.Dom.getViewWidth(true),
4245             Roo.lib.Dom.getViewHeight(true)
4246         );
4247         
4248         if (this.fitwindow) {
4249             
4250            
4251             this.setSize(
4252                 this.width || Roo.lib.Dom.getViewportWidth(true) - 30,
4253                 this.height || Roo.lib.Dom.getViewportHeight(true) // catering margin-top 30 margin-bottom 30
4254             );
4255             return;
4256         }
4257         
4258         if(this.max_width !== 0) {
4259             
4260             var w = Math.min(this.max_width, Roo.lib.Dom.getViewportWidth(true) - 30);
4261             
4262             if(this.height) {
4263                 this.setSize(w, this.height);
4264                 return;
4265             }
4266             
4267             if(this.max_height) {
4268                 this.setSize(w,Math.min(
4269                     this.max_height,
4270                     Roo.lib.Dom.getViewportHeight(true) - 60
4271                 ));
4272                 
4273                 return;
4274             }
4275             
4276             if(!this.fit_content) {
4277                 this.setSize(w, Roo.lib.Dom.getViewportHeight(true) - 60);
4278                 return;
4279             }
4280             
4281             this.setSize(w, Math.min(
4282                 60 +
4283                 this.headerEl.getHeight() + 
4284                 this.footerEl.getHeight() + 
4285                 this.getChildHeight(this.bodyEl.dom.childNodes),
4286                 Roo.lib.Dom.getViewportHeight(true) - 60)
4287             );
4288         }
4289         
4290     },
4291
4292     setSize : function(w,h)
4293     {
4294         if (!w && !h) {
4295             return;
4296         }
4297         
4298         this.resizeTo(w,h);
4299     },
4300
4301     show : function() {
4302
4303         if (!this.rendered) {
4304             this.render();
4305         }
4306         this.toggleHeaderInput(false);
4307         //this.el.setStyle('display', 'block');
4308         this.el.removeClass('hideing');
4309         this.el.dom.style.display='block';
4310         
4311         Roo.get(document.body).addClass('modal-open');
4312  
4313         if(this.animate){  // element has 'fade'  - so stuff happens after .3s ?- not sure why the delay?
4314             
4315             (function(){
4316                 this.el.addClass('show');
4317                 this.el.addClass('in');
4318             }).defer(50, this);
4319         }else{
4320             this.el.addClass('show');
4321             this.el.addClass('in');
4322         }
4323
4324         // not sure how we can show data in here..
4325         //if (this.tmpl) {
4326         //    this.getChildContainer().dom.innerHTML = this.tmpl.applyTemplate(this);
4327         //}
4328
4329         Roo.get(document.body).addClass("x-body-masked");
4330         
4331         this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true),   Roo.lib.Dom.getViewHeight(true));
4332         this.maskEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
4333         this.maskEl.dom.style.display = 'block';
4334         this.maskEl.addClass('show');
4335         
4336         
4337         this.resize();
4338         
4339         this.fireEvent('show', this);
4340
4341         // set zindex here - otherwise it appears to be ignored...
4342         this.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
4343
4344         (function () {
4345             this.items.forEach( function(e) {
4346                 e.layout ? e.layout() : false;
4347
4348             });
4349         }).defer(100,this);
4350
4351     },
4352     hide : function()
4353     {
4354         if(this.fireEvent("beforehide", this) !== false){
4355             
4356             this.maskEl.removeClass('show');
4357             
4358             this.maskEl.dom.style.display = '';
4359             Roo.get(document.body).removeClass("x-body-masked");
4360             this.el.removeClass('in');
4361             this.el.select('.modal-dialog', true).first().setStyle('transform','');
4362
4363             if(this.animate){ // why
4364                 this.el.addClass('hideing');
4365                 this.el.removeClass('show');
4366                 (function(){
4367                     if (!this.el.hasClass('hideing')) {
4368                         return; // it's been shown again...
4369                     }
4370                     
4371                     this.el.dom.style.display='';
4372
4373                     Roo.get(document.body).removeClass('modal-open');
4374                     this.el.removeClass('hideing');
4375                 }).defer(150,this);
4376                 
4377             }else{
4378                 this.el.removeClass('show');
4379                 this.el.dom.style.display='';
4380                 Roo.get(document.body).removeClass('modal-open');
4381
4382             }
4383             this.fireEvent('hide', this);
4384         }
4385     },
4386     isVisible : function()
4387     {
4388         
4389         return this.el.hasClass('show') && !this.el.hasClass('hideing');
4390         
4391     },
4392
4393     addButton : function(str, cb)
4394     {
4395
4396
4397         var b = Roo.apply({}, { html : str } );
4398         b.xns = b.xns || Roo.bootstrap;
4399         b.xtype = b.xtype || 'Button';
4400         if (typeof(b.listeners) == 'undefined') {
4401             b.listeners = { click : cb.createDelegate(this)  };
4402         }
4403
4404         var btn = Roo.factory(b);
4405
4406         btn.render(this.getButtonContainer());
4407
4408         return btn;
4409
4410     },
4411
4412     setDefaultButton : function(btn)
4413     {
4414         //this.el.select('.modal-footer').()
4415     },
4416
4417     resizeTo: function(w,h)
4418     {
4419         this.dialogEl.setWidth(w);
4420         
4421         var diff = this.headerEl.getHeight() + this.footerEl.getHeight() + 60; // dialog margin-bottom: 30  
4422
4423         this.bodyEl.setHeight(h - diff);
4424         
4425         this.fireEvent('resize', this);
4426     },
4427     
4428     setContentSize  : function(w, h)
4429     {
4430
4431     },
4432     onButtonClick: function(btn,e)
4433     {
4434         //Roo.log([a,b,c]);
4435         this.fireEvent('btnclick', btn.name, e);
4436     },
4437      /**
4438      * Set the title of the Dialog
4439      * @param {String} str new Title
4440      */
4441     setTitle: function(str) {
4442         this.titleEl.dom.innerHTML = str;
4443         this.title = str;
4444     },
4445     /**
4446      * Set the body of the Dialog
4447      * @param {String} str new Title
4448      */
4449     setBody: function(str) {
4450         this.bodyEl.dom.innerHTML = str;
4451     },
4452     /**
4453      * Set the body of the Dialog using the template
4454      * @param {Obj} data - apply this data to the template and replace the body contents.
4455      */
4456     applyBody: function(obj)
4457     {
4458         if (!this.tmpl) {
4459             Roo.log("Error - using apply Body without a template");
4460             //code
4461         }
4462         this.tmpl.overwrite(this.bodyEl, obj);
4463     },
4464     
4465     getChildHeight : function(child_nodes)
4466     {
4467         if(
4468             !child_nodes ||
4469             child_nodes.length == 0
4470         ) {
4471             return 0;
4472         }
4473         
4474         var child_height = 0;
4475         
4476         for(var i = 0; i < child_nodes.length; i++) {
4477             
4478             /*
4479             * for modal with tabs...
4480             if(child_nodes[i].classList.contains('roo-layout-panel')) {
4481                 
4482                 var layout_childs = child_nodes[i].childNodes;
4483                 
4484                 for(var j = 0; j < layout_childs.length; j++) {
4485                     
4486                     if(layout_childs[j].classList.contains('roo-layout-panel-body')) {
4487                         
4488                         var layout_body_childs = layout_childs[j].childNodes;
4489                         
4490                         for(var k = 0; k < layout_body_childs.length; k++) {
4491                             
4492                             if(layout_body_childs[k].classList.contains('navbar')) {
4493                                 child_height += layout_body_childs[k].offsetHeight;
4494                                 continue;
4495                             }
4496                             
4497                             if(layout_body_childs[k].classList.contains('roo-layout-tabs-body')) {
4498                                 
4499                                 var layout_body_tab_childs = layout_body_childs[k].childNodes;
4500                                 
4501                                 for(var m = 0; m < layout_body_tab_childs.length; m++) {
4502                                     
4503                                     if(layout_body_tab_childs[m].classList.contains('roo-layout-active-content')) {
4504                                         child_height += this.getChildHeight(layout_body_tab_childs[m].childNodes);
4505                                         continue;
4506                                     }
4507                                     
4508                                 }
4509                                 
4510                             }
4511                             
4512                         }
4513                     }
4514                 }
4515                 continue;
4516             }
4517             */
4518             
4519             child_height += child_nodes[i].offsetHeight;
4520             // Roo.log(child_nodes[i].offsetHeight);
4521         }
4522         
4523         return child_height;
4524     },
4525     toggleHeaderInput : function(is_edit)
4526     {
4527         if (!this.editableTitle) {
4528             return; // not editable.
4529         }
4530         if (is_edit && this.is_header_editing) {
4531             return; // already editing..
4532         }
4533         if (is_edit) {
4534     
4535             this.headerEditEl.dom.value = this.title;
4536             this.headerEditEl.removeClass('d-none');
4537             this.headerEditEl.dom.focus();
4538             this.titleEl.addClass('d-none');
4539             
4540             this.is_header_editing = true;
4541             return
4542         }
4543         // flip back to not editing.
4544         this.title = this.headerEditEl.dom.value;
4545         this.headerEditEl.addClass('d-none');
4546         this.titleEl.removeClass('d-none');
4547         this.titleEl.dom.innerHTML = String.format('{0}', this.title);
4548         this.is_header_editing = false;
4549         this.fireEvent('titlechanged', this, this.title);
4550     
4551             
4552         
4553     }
4554
4555 });
4556
4557
4558 Roo.apply(Roo.bootstrap.Modal,  {
4559     /**
4560          * Button config that displays a single OK button
4561          * @type Object
4562          */
4563         OK :  [{
4564             name : 'ok',
4565             weight : 'primary',
4566             html : 'OK'
4567         }],
4568         /**
4569          * Button config that displays Yes and No buttons
4570          * @type Object
4571          */
4572         YESNO : [
4573             {
4574                 name  : 'no',
4575                 html : 'No'
4576             },
4577             {
4578                 name  :'yes',
4579                 weight : 'primary',
4580                 html : 'Yes'
4581             }
4582         ],
4583
4584         /**
4585          * Button config that displays OK and Cancel buttons
4586          * @type Object
4587          */
4588         OKCANCEL : [
4589             {
4590                name : 'cancel',
4591                 html : 'Cancel'
4592             },
4593             {
4594                 name : 'ok',
4595                 weight : 'primary',
4596                 html : 'OK'
4597             }
4598         ],
4599         /**
4600          * Button config that displays Yes, No and Cancel buttons
4601          * @type Object
4602          */
4603         YESNOCANCEL : [
4604             {
4605                 name : 'yes',
4606                 weight : 'primary',
4607                 html : 'Yes'
4608             },
4609             {
4610                 name : 'no',
4611                 html : 'No'
4612             },
4613             {
4614                 name : 'cancel',
4615                 html : 'Cancel'
4616             }
4617         ],
4618         
4619         zIndex : 10001
4620 });
4621
4622 /*
4623  * - LGPL
4624  *
4625  * messagebox - can be used as a replace
4626  * 
4627  */
4628 /**
4629  * @class Roo.MessageBox
4630  * Utility class for generating different styles of message boxes.  The alias Roo.Msg can also be used.
4631  * Example usage:
4632  *<pre><code>
4633 // Basic alert:
4634 Roo.Msg.alert('Status', 'Changes saved successfully.');
4635
4636 // Prompt for user data:
4637 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
4638     if (btn == 'ok'){
4639         // process text value...
4640     }
4641 });
4642
4643 // Show a dialog using config options:
4644 Roo.Msg.show({
4645    title:'Save Changes?',
4646    msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
4647    buttons: Roo.Msg.YESNOCANCEL,
4648    fn: processResult,
4649    animEl: 'elId'
4650 });
4651 </code></pre>
4652  * @singleton
4653  */
4654 Roo.bootstrap.MessageBox = function(){
4655     var dlg, opt, mask, waitTimer;
4656     var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
4657     var buttons, activeTextEl, bwidth;
4658
4659     
4660     // private
4661     var handleButton = function(button){
4662         dlg.hide();
4663         Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
4664     };
4665
4666     // private
4667     var handleHide = function(){
4668         if(opt && opt.cls){
4669             dlg.el.removeClass(opt.cls);
4670         }
4671         //if(waitTimer){
4672         //    Roo.TaskMgr.stop(waitTimer);
4673         //    waitTimer = null;
4674         //}
4675     };
4676
4677     // private
4678     var updateButtons = function(b){
4679         var width = 0;
4680         if(!b){
4681             buttons["ok"].hide();
4682             buttons["cancel"].hide();
4683             buttons["yes"].hide();
4684             buttons["no"].hide();
4685             dlg.footerEl.hide();
4686             
4687             return width;
4688         }
4689         dlg.footerEl.show();
4690         for(var k in buttons){
4691             if(typeof buttons[k] != "function"){
4692                 if(b[k]){
4693                     buttons[k].show();
4694                     buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.bootstrap.MessageBox.buttonText[k]);
4695                     width += buttons[k].el.getWidth()+15;
4696                 }else{
4697                     buttons[k].hide();
4698                 }
4699             }
4700         }
4701         return width;
4702     };
4703
4704     // private
4705     var handleEsc = function(d, k, e){
4706         if(opt && opt.closable !== false){
4707             dlg.hide();
4708         }
4709         if(e){
4710             e.stopEvent();
4711         }
4712     };
4713
4714     return {
4715         /**
4716          * Returns a reference to the underlying {@link Roo.BasicDialog} element
4717          * @return {Roo.BasicDialog} The BasicDialog element
4718          */
4719         getDialog : function(){
4720            if(!dlg){
4721                 dlg = new Roo.bootstrap.Modal( {
4722                     //draggable: true,
4723                     //resizable:false,
4724                     //constraintoviewport:false,
4725                     //fixedcenter:true,
4726                     //collapsible : false,
4727                     //shim:true,
4728                     //modal: true,
4729                 //    width: 'auto',
4730                   //  height:100,
4731                     //buttonAlign:"center",
4732                     closeClick : function(){
4733                         if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
4734                             handleButton("no");
4735                         }else{
4736                             handleButton("cancel");
4737                         }
4738                     }
4739                 });
4740                 dlg.render();
4741                 dlg.on("hide", handleHide);
4742                 mask = dlg.mask;
4743                 //dlg.addKeyListener(27, handleEsc);
4744                 buttons = {};
4745                 this.buttons = buttons;
4746                 var bt = this.buttonText;
4747                 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
4748                 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
4749                 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
4750                 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
4751                 //Roo.log(buttons);
4752                 bodyEl = dlg.bodyEl.createChild({
4753
4754                     html:'<span class="roo-mb-text"></span><br /><input type="text" class="roo-mb-input" />' +
4755                         '<textarea class="roo-mb-textarea"></textarea>' +
4756                         '<div class="roo-mb-progress-wrap"><div class="roo-mb-progress"><div class="roo-mb-progress-bar">&#160;</div></div></div>'
4757                 });
4758                 msgEl = bodyEl.dom.firstChild;
4759                 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
4760                 textboxEl.enableDisplayMode();
4761                 textboxEl.addKeyListener([10,13], function(){
4762                     if(dlg.isVisible() && opt && opt.buttons){
4763                         if(opt.buttons.ok){
4764                             handleButton("ok");
4765                         }else if(opt.buttons.yes){
4766                             handleButton("yes");
4767                         }
4768                     }
4769                 });
4770                 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
4771                 textareaEl.enableDisplayMode();
4772                 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
4773                 progressEl.enableDisplayMode();
4774                 
4775                 // This is supposed to be the progessElement.. but I think it's controlling the height of everything..
4776                 var pf = progressEl.dom.firstChild;
4777                 if (pf) {
4778                     pp = Roo.get(pf.firstChild);
4779                     pp.setHeight(pf.offsetHeight);
4780                 }
4781                 
4782             }
4783             return dlg;
4784         },
4785
4786         /**
4787          * Updates the message box body text
4788          * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
4789          * the XHTML-compliant non-breaking space character '&amp;#160;')
4790          * @return {Roo.MessageBox} This message box
4791          */
4792         updateText : function(text)
4793         {
4794             if(!dlg.isVisible() && !opt.width){
4795                 dlg.dialogEl.setStyle({ 'max-width' : this.maxWidth});
4796                 // dlg.resizeTo(this.maxWidth, 100); // forcing the height breaks long alerts()
4797             }
4798             msgEl.innerHTML = text || '&#160;';
4799       
4800             var cw =  Math.max(msgEl.offsetWidth, msgEl.parentNode.scrollWidth);
4801             //Roo.log("guesed size: " + JSON.stringify([cw,msgEl.offsetWidth, msgEl.parentNode.scrollWidth]));
4802             var w = Math.max(
4803                     Math.min(opt.width || cw , this.maxWidth), 
4804                     Math.max(opt.minWidth || this.minWidth, bwidth)
4805             );
4806             if(opt.prompt){
4807                 activeTextEl.setWidth(w);
4808             }
4809             if(dlg.isVisible()){
4810                 dlg.fixedcenter = false;
4811             }
4812             // to big, make it scroll. = But as usual stupid IE does not support
4813             // !important..
4814             
4815             if ( bodyEl.getHeight() > (Roo.lib.Dom.getViewHeight() - 100)) {
4816                 bodyEl.setHeight ( Roo.lib.Dom.getViewHeight() - 100 );
4817                 bodyEl.dom.style.overflowY = 'auto' + ( Roo.isIE ? '' : ' !important');
4818             } else {
4819                 bodyEl.dom.style.height = '';
4820                 bodyEl.dom.style.overflowY = '';
4821             }
4822             if (cw > w) {
4823                 bodyEl.dom.style.get = 'auto' + ( Roo.isIE ? '' : ' !important');
4824             } else {
4825                 bodyEl.dom.style.overflowX = '';
4826             }
4827             
4828             dlg.setContentSize(w, bodyEl.getHeight());
4829             if(dlg.isVisible()){
4830                 dlg.fixedcenter = true;
4831             }
4832             return this;
4833         },
4834
4835         /**
4836          * Updates a progress-style message box's text and progress bar.  Only relevant on message boxes
4837          * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
4838          * @param {Number} value Any number between 0 and 1 (e.g., .5)
4839          * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
4840          * @return {Roo.MessageBox} This message box
4841          */
4842         updateProgress : function(value, text){
4843             if(text){
4844                 this.updateText(text);
4845             }
4846             
4847             if (pp) { // weird bug on my firefox - for some reason this is not defined
4848                 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
4849                 pp.setHeight(Math.floor(progressEl.dom.firstChild.offsetHeight));
4850             }
4851             return this;
4852         },        
4853
4854         /**
4855          * Returns true if the message box is currently displayed
4856          * @return {Boolean} True if the message box is visible, else false
4857          */
4858         isVisible : function(){
4859             return dlg && dlg.isVisible();  
4860         },
4861
4862         /**
4863          * Hides the message box if it is displayed
4864          */
4865         hide : function(){
4866             if(this.isVisible()){
4867                 dlg.hide();
4868             }  
4869         },
4870
4871         /**
4872          * Displays a new message box, or reinitializes an existing message box, based on the config options
4873          * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
4874          * The following config object properties are supported:
4875          * <pre>
4876 Property    Type             Description
4877 ----------  ---------------  ------------------------------------------------------------------------------------
4878 animEl            String/Element   An id or Element from which the message box should animate as it opens and
4879                                    closes (defaults to undefined)
4880 buttons           Object/Boolean   A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
4881                                    cancel:'Bar'}), or false to not show any buttons (defaults to false)
4882 closable          Boolean          False to hide the top-right close button (defaults to true).  Note that
4883                                    progress and wait dialogs will ignore this property and always hide the
4884                                    close button as they can only be closed programmatically.
4885 cls               String           A custom CSS class to apply to the message box element
4886 defaultTextHeight Number           The default height in pixels of the message box's multiline textarea if
4887                                    displayed (defaults to 75)
4888 fn                Function         A callback function to execute after closing the dialog.  The arguments to the
4889                                    function will be btn (the name of the button that was clicked, if applicable,
4890                                    e.g. "ok"), and text (the value of the active text field, if applicable).
4891                                    Progress and wait dialogs will ignore this option since they do not respond to
4892                                    user actions and can only be closed programmatically, so any required function
4893                                    should be called by the same code after it closes the dialog.
4894 icon              String           A CSS class that provides a background image to be used as an icon for
4895                                    the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
4896 maxWidth          Number           The maximum width in pixels of the message box (defaults to 600)
4897 minWidth          Number           The minimum width in pixels of the message box (defaults to 100)
4898 modal             Boolean          False to allow user interaction with the page while the message box is
4899                                    displayed (defaults to true)
4900 msg               String           A string that will replace the existing message box body text (defaults
4901                                    to the XHTML-compliant non-breaking space character '&#160;')
4902 multiline         Boolean          True to prompt the user to enter multi-line text (defaults to false)
4903 progress          Boolean          True to display a progress bar (defaults to false)
4904 progressText      String           The text to display inside the progress bar if progress = true (defaults to '')
4905 prompt            Boolean          True to prompt the user to enter single-line text (defaults to false)
4906 proxyDrag         Boolean          True to display a lightweight proxy while dragging (defaults to false)
4907 title             String           The title text
4908 value             String           The string value to set into the active textbox element if displayed
4909 wait              Boolean          True to display a progress bar (defaults to false)
4910 width             Number           The width of the dialog in pixels
4911 </pre>
4912          *
4913          * Example usage:
4914          * <pre><code>
4915 Roo.Msg.show({
4916    title: 'Address',
4917    msg: 'Please enter your address:',
4918    width: 300,
4919    buttons: Roo.MessageBox.OKCANCEL,
4920    multiline: true,
4921    fn: saveAddress,
4922    animEl: 'addAddressBtn'
4923 });
4924 </code></pre>
4925          * @param {Object} config Configuration options
4926          * @return {Roo.MessageBox} This message box
4927          */
4928         show : function(options)
4929         {
4930             
4931             // this causes nightmares if you show one dialog after another
4932             // especially on callbacks..
4933              
4934             if(this.isVisible()){
4935                 
4936                 this.hide();
4937                 Roo.log("[Roo.Messagebox] Show called while message displayed:" );
4938                 Roo.log("Old Dialog Message:" +  msgEl.innerHTML );
4939                 Roo.log("New Dialog Message:" +  options.msg )
4940                 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
4941                 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
4942                 
4943             }
4944             var d = this.getDialog();
4945             opt = options;
4946             d.setTitle(opt.title || "&#160;");
4947             d.closeEl.setDisplayed(opt.closable !== false);
4948             activeTextEl = textboxEl;
4949             opt.prompt = opt.prompt || (opt.multiline ? true : false);
4950             if(opt.prompt){
4951                 if(opt.multiline){
4952                     textboxEl.hide();
4953                     textareaEl.show();
4954                     textareaEl.setHeight(typeof opt.multiline == "number" ?
4955                         opt.multiline : this.defaultTextHeight);
4956                     activeTextEl = textareaEl;
4957                 }else{
4958                     textboxEl.show();
4959                     textareaEl.hide();
4960                 }
4961             }else{
4962                 textboxEl.hide();
4963                 textareaEl.hide();
4964             }
4965             progressEl.setDisplayed(opt.progress === true);
4966             if (opt.progress) {
4967                 d.animate = false; // do not animate progress, as it may not have finished animating before we close it..
4968             }
4969             this.updateProgress(0);
4970             activeTextEl.dom.value = opt.value || "";
4971             if(opt.prompt){
4972                 dlg.setDefaultButton(activeTextEl);
4973             }else{
4974                 var bs = opt.buttons;
4975                 var db = null;
4976                 if(bs && bs.ok){
4977                     db = buttons["ok"];
4978                 }else if(bs && bs.yes){
4979                     db = buttons["yes"];
4980                 }
4981                 dlg.setDefaultButton(db);
4982             }
4983             bwidth = updateButtons(opt.buttons);
4984             this.updateText(opt.msg);
4985             if(opt.cls){
4986                 d.el.addClass(opt.cls);
4987             }
4988             d.proxyDrag = opt.proxyDrag === true;
4989             d.modal = opt.modal !== false;
4990             d.mask = opt.modal !== false ? mask : false;
4991             if(!d.isVisible()){
4992                 // force it to the end of the z-index stack so it gets a cursor in FF
4993                 document.body.appendChild(dlg.el.dom);
4994                 d.animateTarget = null;
4995                 d.show(options.animEl);
4996             }
4997             return this;
4998         },
4999
5000         /**
5001          * Displays a message box with a progress bar.  This message box has no buttons and is not closeable by
5002          * the user.  You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
5003          * and closing the message box when the process is complete.
5004          * @param {String} title The title bar text
5005          * @param {String} msg The message box body text
5006          * @return {Roo.MessageBox} This message box
5007          */
5008         progress : function(title, msg){
5009             this.show({
5010                 title : title,
5011                 msg : msg,
5012                 buttons: false,
5013                 progress:true,
5014                 closable:false,
5015                 minWidth: this.minProgressWidth,
5016                 modal : true
5017             });
5018             return this;
5019         },
5020
5021         /**
5022          * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
5023          * If a callback function is passed it will be called after the user clicks the button, and the
5024          * id of the button that was clicked will be passed as the only parameter to the callback
5025          * (could also be the top-right close button).
5026          * @param {String} title The title bar text
5027          * @param {String} msg The message box body text
5028          * @param {Function} fn (optional) The callback function invoked after the message box is closed
5029          * @param {Object} scope (optional) The scope of the callback function
5030          * @return {Roo.MessageBox} This message box
5031          */
5032         alert : function(title, msg, fn, scope)
5033         {
5034             this.show({
5035                 title : title,
5036                 msg : msg,
5037                 buttons: this.OK,
5038                 fn: fn,
5039                 closable : false,
5040                 scope : scope,
5041                 modal : true
5042             });
5043             return this;
5044         },
5045
5046         /**
5047          * Displays a message box with an infinitely auto-updating progress bar.  This can be used to block user
5048          * interaction while waiting for a long-running process to complete that does not have defined intervals.
5049          * You are responsible for closing the message box when the process is complete.
5050          * @param {String} msg The message box body text
5051          * @param {String} title (optional) The title bar text
5052          * @return {Roo.MessageBox} This message box
5053          */
5054         wait : function(msg, title){
5055             this.show({
5056                 title : title,
5057                 msg : msg,
5058                 buttons: false,
5059                 closable:false,
5060                 progress:true,
5061                 modal:true,
5062                 width:300,
5063                 wait:true
5064             });
5065             waitTimer = Roo.TaskMgr.start({
5066                 run: function(i){
5067                     Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
5068                 },
5069                 interval: 1000
5070             });
5071             return this;
5072         },
5073
5074         /**
5075          * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
5076          * If a callback function is passed it will be called after the user clicks either button, and the id of the
5077          * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
5078          * @param {String} title The title bar text
5079          * @param {String} msg The message box body text
5080          * @param {Function} fn (optional) The callback function invoked after the message box is closed
5081          * @param {Object} scope (optional) The scope of the callback function
5082          * @return {Roo.MessageBox} This message box
5083          */
5084         confirm : function(title, msg, fn, scope){
5085             this.show({
5086                 title : title,
5087                 msg : msg,
5088                 buttons: this.YESNO,
5089                 fn: fn,
5090                 scope : scope,
5091                 modal : true
5092             });
5093             return this;
5094         },
5095
5096         /**
5097          * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
5098          * JavaScript's Window.prompt).  The prompt can be a single-line or multi-line textbox.  If a callback function
5099          * is passed it will be called after the user clicks either button, and the id of the button that was clicked
5100          * (could also be the top-right close button) and the text that was entered will be passed as the two
5101          * parameters to the callback.
5102          * @param {String} title The title bar text
5103          * @param {String} msg The message box body text
5104          * @param {Function} fn (optional) The callback function invoked after the message box is closed
5105          * @param {Object} scope (optional) The scope of the callback function
5106          * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
5107          * property, or the height in pixels to create the textbox (defaults to false / single-line)
5108          * @return {Roo.MessageBox} This message box
5109          */
5110         prompt : function(title, msg, fn, scope, multiline){
5111             this.show({
5112                 title : title,
5113                 msg : msg,
5114                 buttons: this.OKCANCEL,
5115                 fn: fn,
5116                 minWidth:250,
5117                 scope : scope,
5118                 prompt:true,
5119                 multiline: multiline,
5120                 modal : true
5121             });
5122             return this;
5123         },
5124
5125         /**
5126          * Button config that displays a single OK button
5127          * @type Object
5128          */
5129         OK : {ok:true},
5130         /**
5131          * Button config that displays Yes and No buttons
5132          * @type Object
5133          */
5134         YESNO : {yes:true, no:true},
5135         /**
5136          * Button config that displays OK and Cancel buttons
5137          * @type Object
5138          */
5139         OKCANCEL : {ok:true, cancel:true},
5140         /**
5141          * Button config that displays Yes, No and Cancel buttons
5142          * @type Object
5143          */
5144         YESNOCANCEL : {yes:true, no:true, cancel:true},
5145
5146         /**
5147          * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
5148          * @type Number
5149          */
5150         defaultTextHeight : 75,
5151         /**
5152          * The maximum width in pixels of the message box (defaults to 600)
5153          * @type Number
5154          */
5155         maxWidth : 600,
5156         /**
5157          * The minimum width in pixels of the message box (defaults to 100)
5158          * @type Number
5159          */
5160         minWidth : 100,
5161         /**
5162          * The minimum width in pixels of the message box if it is a progress-style dialog.  This is useful
5163          * for setting a different minimum width than text-only dialogs may need (defaults to 250)
5164          * @type Number
5165          */
5166         minProgressWidth : 250,
5167         /**
5168          * An object containing the default button text strings that can be overriden for localized language support.
5169          * Supported properties are: ok, cancel, yes and no.
5170          * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
5171          * @type Object
5172          */
5173         buttonText : {
5174             ok : "OK",
5175             cancel : "Cancel",
5176             yes : "Yes",
5177             no : "No"
5178         }
5179     };
5180 }();
5181
5182 /**
5183  * Shorthand for {@link Roo.MessageBox}
5184  */
5185 Roo.MessageBox = Roo.MessageBox || Roo.bootstrap.MessageBox;
5186 Roo.Msg = Roo.Msg || Roo.MessageBox;
5187 /*
5188  * - LGPL
5189  *
5190  * navbar
5191  * 
5192  */
5193
5194 /**
5195  * @class Roo.bootstrap.Navbar
5196  * @extends Roo.bootstrap.Component
5197  * Bootstrap Navbar class
5198
5199  * @constructor
5200  * Create a new Navbar
5201  * @param {Object} config The config object
5202  */
5203
5204
5205 Roo.bootstrap.Navbar = function(config){
5206     Roo.bootstrap.Navbar.superclass.constructor.call(this, config);
5207     this.addEvents({
5208         // raw events
5209         /**
5210          * @event beforetoggle
5211          * Fire before toggle the menu
5212          * @param {Roo.EventObject} e
5213          */
5214         "beforetoggle" : true
5215     });
5216 };
5217
5218 Roo.extend(Roo.bootstrap.Navbar, Roo.bootstrap.Component,  {
5219     
5220     
5221    
5222     // private
5223     navItems : false,
5224     loadMask : false,
5225     
5226     
5227     getAutoCreate : function(){
5228         
5229         
5230         throw { message : "nav bar is now a abstract base class - use NavSimplebar / NavHeaderbar / NavSidebar etc..."};
5231         
5232     },
5233     
5234     initEvents :function ()
5235     {
5236         //Roo.log(this.el.select('.navbar-toggle',true));
5237         this.el.select('.navbar-toggle',true).on('click', this.onToggle , this);
5238         
5239         var mark = {
5240             tag: "div",
5241             cls:"x-dlg-mask"
5242         };
5243         
5244         this.maskEl = Roo.DomHelper.append(this.el, mark, true);
5245         
5246         var size = this.el.getSize();
5247         this.maskEl.setSize(size.width, size.height);
5248         this.maskEl.enableDisplayMode("block");
5249         this.maskEl.hide();
5250         
5251         if(this.loadMask){
5252             this.maskEl.show();
5253         }
5254     },
5255     
5256     
5257     getChildContainer : function()
5258     {
5259         if (this.el && this.el.select('.collapse').getCount()) {
5260             return this.el.select('.collapse',true).first();
5261         }
5262         
5263         return this.el;
5264     },
5265     
5266     mask : function()
5267     {
5268         this.maskEl.show();
5269     },
5270     
5271     unmask : function()
5272     {
5273         this.maskEl.hide();
5274     },
5275     onToggle : function()
5276     {
5277         
5278         if(this.fireEvent('beforetoggle', this) === false){
5279             return;
5280         }
5281         var ce = this.el.select('.navbar-collapse',true).first();
5282       
5283         if (!ce.hasClass('show')) {
5284            this.expand();
5285         } else {
5286             this.collapse();
5287         }
5288         
5289         
5290     
5291     },
5292     /**
5293      * Expand the navbar pulldown 
5294      */
5295     expand : function ()
5296     {
5297        
5298         var ce = this.el.select('.navbar-collapse',true).first();
5299         if (ce.hasClass('collapsing')) {
5300             return;
5301         }
5302         ce.dom.style.height = '';
5303                // show it...
5304         ce.addClass('in'); // old...
5305         ce.removeClass('collapse');
5306         ce.addClass('show');
5307         var h = ce.getHeight();
5308         Roo.log(h);
5309         ce.removeClass('show');
5310         // at this point we should be able to see it..
5311         ce.addClass('collapsing');
5312         
5313         ce.setHeight(0); // resize it ...
5314         ce.on('transitionend', function() {
5315             //Roo.log('done transition');
5316             ce.removeClass('collapsing');
5317             ce.addClass('show');
5318             ce.removeClass('collapse');
5319
5320             ce.dom.style.height = '';
5321         }, this, { single: true} );
5322         ce.setHeight(h);
5323         ce.dom.scrollTop = 0;
5324     },
5325     /**
5326      * Collapse the navbar pulldown 
5327      */
5328     collapse : function()
5329     {
5330          var ce = this.el.select('.navbar-collapse',true).first();
5331        
5332         if (ce.hasClass('collapsing') || ce.hasClass('collapse') ) {
5333             // it's collapsed or collapsing..
5334             return;
5335         }
5336         ce.removeClass('in'); // old...
5337         ce.setHeight(ce.getHeight());
5338         ce.removeClass('show');
5339         ce.addClass('collapsing');
5340         
5341         ce.on('transitionend', function() {
5342             ce.dom.style.height = '';
5343             ce.removeClass('collapsing');
5344             ce.addClass('collapse');
5345         }, this, { single: true} );
5346         ce.setHeight(0);
5347     }
5348     
5349     
5350     
5351 });
5352
5353
5354
5355  
5356
5357  /*
5358  * - LGPL
5359  *
5360  * navbar
5361  * 
5362  */
5363
5364 /**
5365  * @class Roo.bootstrap.NavSimplebar
5366  * @extends Roo.bootstrap.Navbar
5367  * Bootstrap Sidebar class
5368  *
5369  * @cfg {Boolean} inverse is inverted color
5370  * 
5371  * @cfg {String} type (nav | pills | tabs)
5372  * @cfg {Boolean} arrangement stacked | justified
5373  * @cfg {String} align (left | right) alignment
5374  * 
5375  * @cfg {Boolean} main (true|false) main nav bar? default false
5376  * @cfg {Boolean} loadMask (true|false) loadMask on the bar
5377  * 
5378  * @cfg {String} tag (header|footer|nav|div) default is nav 
5379
5380  * @cfg {String} weight (light|primary|secondary|success|danger|warning|info|dark|white) default is light.
5381  * 
5382  * 
5383  * @constructor
5384  * Create a new Sidebar
5385  * @param {Object} config The config object
5386  */
5387
5388
5389 Roo.bootstrap.NavSimplebar = function(config){
5390     Roo.bootstrap.NavSimplebar.superclass.constructor.call(this, config);
5391 };
5392
5393 Roo.extend(Roo.bootstrap.NavSimplebar, Roo.bootstrap.Navbar,  {
5394     
5395     inverse: false,
5396     
5397     type: false,
5398     arrangement: '',
5399     align : false,
5400     
5401     weight : 'light',
5402     
5403     main : false,
5404     
5405     
5406     tag : false,
5407     
5408     
5409     getAutoCreate : function(){
5410         
5411         
5412         var cfg = {
5413             tag : this.tag || 'div',
5414             cls : 'navbar roo-navbar-simple' //navbar-expand-lg ??
5415         };
5416         if (['light','white'].indexOf(this.weight) > -1) {
5417             cfg.cls += ['light','white'].indexOf(this.weight) > -1 ? ' navbar-light' : ' navbar-dark';
5418         }
5419         cfg.cls += ' bg-' + this.weight;
5420         
5421         if (this.inverse) {
5422             cfg.cls += ' navbar-inverse';
5423             
5424         }
5425         
5426         // i'm not actually sure these are really used - normally we add a navGroup to a navbar
5427         
5428         if (Roo.bootstrap.version == 4 && this.xtype == 'NavSimplebar') {
5429             return cfg;
5430         }
5431         
5432         
5433     
5434         
5435         cfg.cn = [
5436             {
5437                 cls: 'nav nav-' + this.xtype,
5438                 tag : 'ul'
5439             }
5440         ];
5441         
5442          
5443         this.type = this.type || 'nav';
5444         if (['tabs','pills'].indexOf(this.type) != -1) {
5445             cfg.cn[0].cls += ' nav-' + this.type
5446         
5447         
5448         } else {
5449             if (this.type!=='nav') {
5450                 Roo.log('nav type must be nav/tabs/pills')
5451             }
5452             cfg.cn[0].cls += ' navbar-nav'
5453         }
5454         
5455         
5456         
5457         
5458         if (['stacked','justified'].indexOf(this.arrangement) != -1) {
5459             cfg.cn[0].cls += ' nav-' + this.arrangement;
5460         }
5461         
5462         
5463         if (this.align === 'right') {
5464             cfg.cn[0].cls += ' navbar-right';
5465         }
5466         
5467         
5468         
5469         
5470         return cfg;
5471     
5472         
5473     }
5474     
5475     
5476     
5477 });
5478
5479
5480
5481  
5482
5483  
5484        /*
5485  * - LGPL
5486  *
5487  * navbar
5488  * navbar-fixed-top
5489  * navbar-expand-md  fixed-top 
5490  */
5491
5492 /**
5493  * @class Roo.bootstrap.NavHeaderbar
5494  * @extends Roo.bootstrap.NavSimplebar
5495  * Bootstrap Sidebar class
5496  *
5497  * @cfg {String} brand what is brand
5498  * @cfg {String} position (fixed-top|fixed-bottom|static-top) position
5499  * @cfg {String} brand_href href of the brand
5500  * @cfg {Boolean} srButton generate the (screen reader / mobile) sr-only button   default true
5501  * @cfg {Boolean} autohide a top nav bar header that hides on scroll.
5502  * @cfg {Boolean} desktopCenter should the header be centered on desktop using a container class
5503  * @cfg {Roo.bootstrap.Row} mobilerow - a row to display on mobile only..
5504  * 
5505  * @constructor
5506  * Create a new Sidebar
5507  * @param {Object} config The config object
5508  */
5509
5510
5511 Roo.bootstrap.NavHeaderbar = function(config){
5512     Roo.bootstrap.NavHeaderbar.superclass.constructor.call(this, config);
5513       
5514 };
5515
5516 Roo.extend(Roo.bootstrap.NavHeaderbar, Roo.bootstrap.NavSimplebar,  {
5517     
5518     position: '',
5519     brand: '',
5520     brand_href: false,
5521     srButton : true,
5522     autohide : false,
5523     desktopCenter : false,
5524    
5525     
5526     getAutoCreate : function(){
5527         
5528         var   cfg = {
5529             tag: this.nav || 'nav',
5530             cls: 'navbar navbar-expand-md',
5531             role: 'navigation',
5532             cn: []
5533         };
5534         
5535         var cn = cfg.cn;
5536         if (this.desktopCenter) {
5537             cn.push({cls : 'container', cn : []});
5538             cn = cn[0].cn;
5539         }
5540         
5541         if(this.srButton){
5542             var btn = {
5543                 tag: 'button',
5544                 type: 'button',
5545                 cls: 'navbar-toggle navbar-toggler',
5546                 'data-toggle': 'collapse',
5547                 cn: [
5548                     {
5549                         tag: 'span',
5550                         cls: 'sr-only',
5551                         html: 'Toggle navigation'
5552                     },
5553                     {
5554                         tag: 'span',
5555                         cls: 'icon-bar navbar-toggler-icon'
5556                     },
5557                     {
5558                         tag: 'span',
5559                         cls: 'icon-bar'
5560                     },
5561                     {
5562                         tag: 'span',
5563                         cls: 'icon-bar'
5564                     }
5565                 ]
5566             };
5567             
5568             cn.push( Roo.bootstrap.version == 4 ? btn : {
5569                 tag: 'div',
5570                 cls: 'navbar-header',
5571                 cn: [
5572                     btn
5573                 ]
5574             });
5575         }
5576         
5577         cn.push({
5578             tag: 'div',
5579             cls: Roo.bootstrap.version == 4  ? 'nav flex-row roo-navbar-collapse collapse navbar-collapse' : 'collapse navbar-collapse roo-navbar-collapse',
5580             cn : []
5581         });
5582         
5583         cfg.cls += this.inverse ? ' navbar-inverse navbar-dark bg-dark' : ' navbar-default';
5584         
5585         if (['light','white'].indexOf(this.weight) > -1) {
5586             cfg.cls += ['light','white'].indexOf(this.weight) > -1 ? ' navbar-light' : ' navbar-dark';
5587         }
5588         cfg.cls += ' bg-' + this.weight;
5589         
5590         
5591         if (['fixed-top','fixed-bottom','static-top'].indexOf(this.position)>-1) {
5592             cfg.cls += ' navbar-' + this.position + ' ' + this.position ;
5593             
5594             // tag can override this..
5595             
5596             cfg.tag = this.tag || (this.position  == 'fixed-bottom' ? 'footer' : 'header');
5597         }
5598         
5599         if (this.brand !== '') {
5600             var cp =  Roo.bootstrap.version == 4 ? cn : cn[0].cn;
5601             cp.unshift({ // changed from push ?? BS4 needs it at the start? - does this break or exsiting?
5602                 tag: 'a',
5603                 href: this.brand_href ? this.brand_href : '#',
5604                 cls: 'navbar-brand',
5605                 cn: [
5606                 this.brand
5607                 ]
5608             });
5609         }
5610         
5611         if(this.main){
5612             cfg.cls += ' main-nav';
5613         }
5614         
5615         
5616         return cfg;
5617
5618         
5619     },
5620     getHeaderChildContainer : function()
5621     {
5622         if (this.srButton && this.el.select('.navbar-header').getCount()) {
5623             return this.el.select('.navbar-header',true).first();
5624         }
5625         
5626         return this.getChildContainer();
5627     },
5628     
5629     getChildContainer : function()
5630     {
5631          
5632         return this.el.select('.roo-navbar-collapse',true).first();
5633          
5634         
5635     },
5636     
5637     initEvents : function()
5638     {
5639         Roo.bootstrap.NavHeaderbar.superclass.initEvents.call(this);
5640         
5641         if (this.autohide) {
5642             
5643             var prevScroll = 0;
5644             var ft = this.el;
5645             
5646             Roo.get(document).on('scroll',function(e) {
5647                 var ns = Roo.get(document).getScroll().top;
5648                 var os = prevScroll;
5649                 prevScroll = ns;
5650                 
5651                 if(ns > os){
5652                     ft.removeClass('slideDown');
5653                     ft.addClass('slideUp');
5654                     return;
5655                 }
5656                 ft.removeClass('slideUp');
5657                 ft.addClass('slideDown');
5658                  
5659               
5660           },this);
5661         }
5662     }    
5663     
5664 });
5665
5666
5667
5668  
5669
5670  /*
5671  * - LGPL
5672  *
5673  * navbar
5674  * 
5675  */
5676
5677 /**
5678  * @class Roo.bootstrap.NavSidebar
5679  * @extends Roo.bootstrap.Navbar
5680  * Bootstrap Sidebar class
5681  * 
5682  * @constructor
5683  * Create a new Sidebar
5684  * @param {Object} config The config object
5685  */
5686
5687
5688 Roo.bootstrap.NavSidebar = function(config){
5689     Roo.bootstrap.NavSidebar.superclass.constructor.call(this, config);
5690 };
5691
5692 Roo.extend(Roo.bootstrap.NavSidebar, Roo.bootstrap.Navbar,  {
5693     
5694     sidebar : true, // used by Navbar Item and NavbarGroup at present...
5695     
5696     getAutoCreate : function(){
5697         
5698         
5699         return  {
5700             tag: 'div',
5701             cls: 'sidebar sidebar-nav'
5702         };
5703     
5704         
5705     }
5706     
5707     
5708     
5709 });
5710
5711
5712
5713  
5714
5715  /*
5716  * - LGPL
5717  *
5718  * nav group
5719  * 
5720  */
5721
5722 /**
5723  * @class Roo.bootstrap.NavGroup
5724  * @extends Roo.bootstrap.Component
5725  * Bootstrap NavGroup class
5726  * @cfg {String} align (left|right)
5727  * @cfg {Boolean} inverse
5728  * @cfg {String} type (nav|pills|tab) default nav
5729  * @cfg {String} navId - reference Id for navbar.
5730
5731  * 
5732  * @constructor
5733  * Create a new nav group
5734  * @param {Object} config The config object
5735  */
5736
5737 Roo.bootstrap.NavGroup = function(config){
5738     Roo.bootstrap.NavGroup.superclass.constructor.call(this, config);
5739     this.navItems = [];
5740    
5741     Roo.bootstrap.NavGroup.register(this);
5742      this.addEvents({
5743         /**
5744              * @event changed
5745              * Fires when the active item changes
5746              * @param {Roo.bootstrap.NavGroup} this
5747              * @param {Roo.bootstrap.Navbar.Item} selected The item selected
5748              * @param {Roo.bootstrap.Navbar.Item} prev The previously selected item 
5749          */
5750         'changed': true
5751      });
5752     
5753 };
5754
5755 Roo.extend(Roo.bootstrap.NavGroup, Roo.bootstrap.Component,  {
5756     
5757     align: '',
5758     inverse: false,
5759     form: false,
5760     type: 'nav',
5761     navId : '',
5762     // private
5763     
5764     navItems : false, 
5765     
5766     getAutoCreate : function()
5767     {
5768         var cfg = Roo.apply({}, Roo.bootstrap.NavGroup.superclass.getAutoCreate.call(this));
5769         
5770         cfg = {
5771             tag : 'ul',
5772             cls: 'nav' 
5773         };
5774         if (Roo.bootstrap.version == 4) {
5775             if (['tabs','pills'].indexOf(this.type) != -1) {
5776                 cfg.cls += ' nav-' + this.type; 
5777             } else {
5778                 // trying to remove so header bar can right align top?
5779                 if (this.parent() && this.parent().xtype != 'NavHeaderbar') {
5780                     // do not use on header bar... 
5781                     cfg.cls += ' navbar-nav';
5782                 }
5783             }
5784             
5785         } else {
5786             if (['tabs','pills'].indexOf(this.type) != -1) {
5787                 cfg.cls += ' nav-' + this.type
5788             } else {
5789                 if (this.type !== 'nav') {
5790                     Roo.log('nav type must be nav/tabs/pills')
5791                 }
5792                 cfg.cls += ' navbar-nav'
5793             }
5794         }
5795         
5796         if (this.parent() && this.parent().sidebar) {
5797             cfg = {
5798                 tag: 'ul',
5799                 cls: 'dashboard-menu sidebar-menu'
5800             };
5801             
5802             return cfg;
5803         }
5804         
5805         if (this.form === true) {
5806             cfg = {
5807                 tag: 'form',
5808                 cls: 'navbar-form form-inline'
5809             };
5810             //nav navbar-right ml-md-auto
5811             if (this.align === 'right') {
5812                 cfg.cls += ' navbar-right ml-md-auto';
5813             } else {
5814                 cfg.cls += ' navbar-left';
5815             }
5816         }
5817         
5818         if (this.align === 'right') {
5819             cfg.cls += ' navbar-right ml-md-auto';
5820         } else {
5821             cfg.cls += ' mr-auto';
5822         }
5823         
5824         if (this.inverse) {
5825             cfg.cls += ' navbar-inverse';
5826             
5827         }
5828         
5829         
5830         return cfg;
5831     },
5832     /**
5833     * sets the active Navigation item
5834     * @param {Roo.bootstrap.NavItem} the new current navitem
5835     */
5836     setActiveItem : function(item)
5837     {
5838         var prev = false;
5839         Roo.each(this.navItems, function(v){
5840             if (v == item) {
5841                 return ;
5842             }
5843             if (v.isActive()) {
5844                 v.setActive(false, true);
5845                 prev = v;
5846                 
5847             }
5848             
5849         });
5850
5851         item.setActive(true, true);
5852         this.fireEvent('changed', this, item, prev);
5853         
5854         
5855     },
5856     /**
5857     * gets the active Navigation item
5858     * @return {Roo.bootstrap.NavItem} the current navitem
5859     */
5860     getActive : function()
5861     {
5862         
5863         var prev = false;
5864         Roo.each(this.navItems, function(v){
5865             
5866             if (v.isActive()) {
5867                 prev = v;
5868                 
5869             }
5870             
5871         });
5872         return prev;
5873     },
5874     
5875     indexOfNav : function()
5876     {
5877         
5878         var prev = false;
5879         Roo.each(this.navItems, function(v,i){
5880             
5881             if (v.isActive()) {
5882                 prev = i;
5883                 
5884             }
5885             
5886         });
5887         return prev;
5888     },
5889     /**
5890     * adds a Navigation item
5891     * @param {Roo.bootstrap.NavItem} the navitem to add
5892     */
5893     addItem : function(cfg)
5894     {
5895         if (this.form && Roo.bootstrap.version == 4) {
5896             cfg.tag = 'div';
5897         }
5898         var cn = new Roo.bootstrap.NavItem(cfg);
5899         this.register(cn);
5900         cn.parentId = this.id;
5901         cn.onRender(this.el, null);
5902         return cn;
5903     },
5904     /**
5905     * register a Navigation item
5906     * @param {Roo.bootstrap.NavItem} the navitem to add
5907     */
5908     register : function(item)
5909     {
5910         this.navItems.push( item);
5911         item.navId = this.navId;
5912     
5913     },
5914     
5915     /**
5916     * clear all the Navigation item
5917     */
5918    
5919     clearAll : function()
5920     {
5921         this.navItems = [];
5922         this.el.dom.innerHTML = '';
5923     },
5924     
5925     getNavItem: function(tabId)
5926     {
5927         var ret = false;
5928         Roo.each(this.navItems, function(e) {
5929             if (e.tabId == tabId) {
5930                ret =  e;
5931                return false;
5932             }
5933             return true;
5934             
5935         });
5936         return ret;
5937     },
5938     
5939     setActiveNext : function()
5940     {
5941         var i = this.indexOfNav(this.getActive());
5942         if (i > this.navItems.length) {
5943             return;
5944         }
5945         this.setActiveItem(this.navItems[i+1]);
5946     },
5947     setActivePrev : function()
5948     {
5949         var i = this.indexOfNav(this.getActive());
5950         if (i  < 1) {
5951             return;
5952         }
5953         this.setActiveItem(this.navItems[i-1]);
5954     },
5955     clearWasActive : function(except) {
5956         Roo.each(this.navItems, function(e) {
5957             if (e.tabId != except.tabId && e.was_active) {
5958                e.was_active = false;
5959                return false;
5960             }
5961             return true;
5962             
5963         });
5964     },
5965     getWasActive : function ()
5966     {
5967         var r = false;
5968         Roo.each(this.navItems, function(e) {
5969             if (e.was_active) {
5970                r = e;
5971                return false;
5972             }
5973             return true;
5974             
5975         });
5976         return r;
5977     }
5978     
5979     
5980 });
5981
5982  
5983 Roo.apply(Roo.bootstrap.NavGroup, {
5984     
5985     groups: {},
5986      /**
5987     * register a Navigation Group
5988     * @param {Roo.bootstrap.NavGroup} the navgroup to add
5989     */
5990     register : function(navgrp)
5991     {
5992         this.groups[navgrp.navId] = navgrp;
5993         
5994     },
5995     /**
5996     * fetch a Navigation Group based on the navigation ID
5997     * @param {string} the navgroup to add
5998     * @returns {Roo.bootstrap.NavGroup} the navgroup 
5999     */
6000     get: function(navId) {
6001         if (typeof(this.groups[navId]) == 'undefined') {
6002             return false;
6003             //this.register(new Roo.bootstrap.NavGroup({ navId : navId }));
6004         }
6005         return this.groups[navId] ;
6006     }
6007     
6008     
6009     
6010 });
6011
6012  /*
6013  * - LGPL
6014  *
6015  * row
6016  * 
6017  */
6018
6019 /**
6020  * @class Roo.bootstrap.NavItem
6021  * @extends Roo.bootstrap.Component
6022  * Bootstrap Navbar.NavItem class
6023  * @cfg {String} href  link to
6024  * @cfg {String} button_weight (default | primary | secondary | success | info | warning | danger | link ) default none
6025
6026  * @cfg {String} html content of button
6027  * @cfg {String} badge text inside badge
6028  * @cfg {String} badgecls (bg-green|bg-red|bg-yellow)the extra classes for the badge
6029  * @cfg {String} glyphicon DEPRICATED - use fa
6030  * @cfg {String} icon DEPRICATED - use fa
6031  * @cfg {String} fa - Fontawsome icon name (can add stuff to it like fa-2x)
6032  * @cfg {Boolean} active Is item active
6033  * @cfg {Boolean} disabled Is item disabled
6034  
6035  * @cfg {Boolean} preventDefault (true | false) default false
6036  * @cfg {String} tabId the tab that this item activates.
6037  * @cfg {String} tagtype (a|span) render as a href or span?
6038  * @cfg {Boolean} animateRef (true|false) link to element default false  
6039   
6040  * @constructor
6041  * Create a new Navbar Item
6042  * @param {Object} config The config object
6043  */
6044 Roo.bootstrap.NavItem = function(config){
6045     Roo.bootstrap.NavItem.superclass.constructor.call(this, config);
6046     this.addEvents({
6047         // raw events
6048         /**
6049          * @event click
6050          * The raw click event for the entire grid.
6051          * @param {Roo.EventObject} e
6052          */
6053         "click" : true,
6054          /**
6055             * @event changed
6056             * Fires when the active item active state changes
6057             * @param {Roo.bootstrap.NavItem} this
6058             * @param {boolean} state the new state
6059              
6060          */
6061         'changed': true,
6062         /**
6063             * @event scrollto
6064             * Fires when scroll to element
6065             * @param {Roo.bootstrap.NavItem} this
6066             * @param {Object} options
6067             * @param {Roo.EventObject} e
6068              
6069          */
6070         'scrollto': true
6071     });
6072    
6073 };
6074
6075 Roo.extend(Roo.bootstrap.NavItem, Roo.bootstrap.Component,  {
6076     
6077     href: false,
6078     html: '',
6079     badge: '',
6080     icon: false,
6081     fa : false,
6082     glyphicon: false,
6083     active: false,
6084     preventDefault : false,
6085     tabId : false,
6086     tagtype : 'a',
6087     tag: 'li',
6088     disabled : false,
6089     animateRef : false,
6090     was_active : false,
6091     button_weight : '',
6092     button_outline : false,
6093     
6094     navLink: false,
6095     
6096     getAutoCreate : function(){
6097          
6098         var cfg = {
6099             tag: this.tag,
6100             cls: 'nav-item'
6101         };
6102         
6103         if (this.active) {
6104             cfg.cls = typeof(cfg.cls) == 'undefined' ? 'active' : cfg.cls + ' active';
6105         }
6106         if (this.disabled) {
6107             cfg.cls += ' disabled';
6108         }
6109         
6110         // BS4 only?
6111         if (this.button_weight.length) {
6112             cfg.tag = this.href ? 'a' : 'button';
6113             cfg.html = this.html || '';
6114             cfg.cls += ' btn btn' + (this.button_outline ? '-outline' : '') + '-' + this.button_weight;
6115             if (this.href) {
6116                 cfg.href = this.href;
6117             }
6118             if (this.fa) {
6119                 cfg.html = '<i class="fa fas fa-'+this.fa+'"></i> <span>' + this.html + '</span>';
6120             }
6121             
6122             // menu .. should add dropdown-menu class - so no need for carat..
6123             
6124             if (this.badge !== '') {
6125                  
6126                 cfg.html += ' <span class="badge badge-secondary">' + this.badge + '</span>';
6127             }
6128             return cfg;
6129         }
6130         
6131         if (this.href || this.html || this.glyphicon || this.icon || this.fa) {
6132             cfg.cn = [
6133                 {
6134                     tag: this.tagtype,
6135                     href : this.href || "#",
6136                     html: this.html || ''
6137                 }
6138             ];
6139             if (this.tagtype == 'a') {
6140                 cfg.cn[0].cls = 'nav-link';
6141             }
6142             if (this.icon) {
6143                 cfg.cn[0].html = '<i class="'+this.icon+'"></i> <span>' + cfg.cn[0].html + '</span>';
6144             }
6145             if (this.fa) {
6146                 cfg.cn[0].html = '<i class="fa fas fa-'+this.fa+'"></i> <span>' + cfg.cn[0].html + '</span>';
6147             }
6148             if(this.glyphicon) {
6149                 cfg.cn[0].html = '<span class="glyphicon glyphicon-' + this.glyphicon + '"></span> '  + cfg.cn[0].html;
6150             }
6151             
6152             if (this.menu) {
6153                 
6154                 cfg.cn[0].html += " <span class='caret'></span>";
6155              
6156             }
6157             
6158             if (this.badge !== '') {
6159                  
6160                 cfg.cn[0].html += ' <span class="badge badge-secondary">' + this.badge + '</span>';
6161             }
6162         }
6163         
6164         
6165         
6166         return cfg;
6167     },
6168     onRender : function(ct, position)
6169     {
6170        // Roo.log("Call onRender: " + this.xtype);
6171         if (Roo.bootstrap.version == 4 && ct.dom.type != 'ul') {
6172             this.tag = 'div';
6173         }
6174         
6175         var ret = Roo.bootstrap.NavItem.superclass.onRender.call(this, ct, position);
6176         this.navLink = this.el.select('.nav-link',true).first();
6177         return ret;
6178     },
6179       
6180     
6181     initEvents: function() 
6182     {
6183         if (typeof (this.menu) != 'undefined') {
6184             this.menu.parentType = this.xtype;
6185             this.menu.triggerEl = this.el;
6186             this.menu = this.addxtype(Roo.apply({}, this.menu));
6187         }
6188         
6189         this.el.select('a',true).on('click', this.onClick, this);
6190         
6191         if(this.tagtype == 'span'){
6192             this.el.select('span',true).on('click', this.onClick, this);
6193         }
6194        
6195         // at this point parent should be available..
6196         this.parent().register(this);
6197     },
6198     
6199     onClick : function(e)
6200     {
6201         if (e.getTarget('.dropdown-menu-item')) {
6202             // did you click on a menu itemm.... - then don't trigger onclick..
6203             return;
6204         }
6205         
6206         if(
6207                 this.preventDefault || 
6208                 this.href == '#' 
6209         ){
6210             Roo.log("NavItem - prevent Default?");
6211             e.preventDefault();
6212         }
6213         
6214         if (this.disabled) {
6215             return;
6216         }
6217         
6218         var tg = Roo.bootstrap.TabGroup.get(this.navId);
6219         if (tg && tg.transition) {
6220             Roo.log("waiting for the transitionend");
6221             return;
6222         }
6223         
6224         
6225         
6226         //Roo.log("fire event clicked");
6227         if(this.fireEvent('click', this, e) === false){
6228             return;
6229         };
6230         
6231         if(this.tagtype == 'span'){
6232             return;
6233         }
6234         
6235         //Roo.log(this.href);
6236         var ael = this.el.select('a',true).first();
6237         //Roo.log(ael);
6238         
6239         if(ael && this.animateRef && this.href.indexOf('#') > -1){
6240             //Roo.log(["test:",ael.dom.href.split("#")[0], document.location.toString().split("#")[0]]);
6241             if (ael.dom.href.split("#")[0] != document.location.toString().split("#")[0]) {
6242                 return; // ignore... - it's a 'hash' to another page.
6243             }
6244             Roo.log("NavItem - prevent Default?");
6245             e.preventDefault();
6246             this.scrollToElement(e);
6247         }
6248         
6249         
6250         var p =  this.parent();
6251    
6252         if (['tabs','pills'].indexOf(p.type)!==-1) {
6253             if (typeof(p.setActiveItem) !== 'undefined') {
6254                 p.setActiveItem(this);
6255             }
6256         }
6257         
6258         // if parent is a navbarheader....- and link is probably a '#' page ref.. then remove the expanded menu.
6259         if (p.parentType == 'NavHeaderbar' && !this.menu) {
6260             // remove the collapsed menu expand...
6261             p.parent().el.select('.roo-navbar-collapse',true).removeClass('in');  
6262         }
6263     },
6264     
6265     isActive: function () {
6266         return this.active
6267     },
6268     setActive : function(state, fire, is_was_active)
6269     {
6270         if (this.active && !state && this.navId) {
6271             this.was_active = true;
6272             var nv = Roo.bootstrap.NavGroup.get(this.navId);
6273             if (nv) {
6274                 nv.clearWasActive(this);
6275             }
6276             
6277         }
6278         this.active = state;
6279         
6280         if (!state ) {
6281             this.el.removeClass('active');
6282             this.navLink ? this.navLink.removeClass('active') : false;
6283         } else if (!this.el.hasClass('active')) {
6284             
6285             this.el.addClass('active');
6286             if (Roo.bootstrap.version == 4 && this.navLink ) {
6287                 this.navLink.addClass('active');
6288             }
6289             
6290         }
6291         if (fire) {
6292             this.fireEvent('changed', this, state);
6293         }
6294         
6295         // show a panel if it's registered and related..
6296         
6297         if (!this.navId || !this.tabId || !state || is_was_active) {
6298             return;
6299         }
6300         
6301         var tg = Roo.bootstrap.TabGroup.get(this.navId);
6302         if (!tg) {
6303             return;
6304         }
6305         var pan = tg.getPanelByName(this.tabId);
6306         if (!pan) {
6307             return;
6308         }
6309         // if we can not flip to new panel - go back to old nav highlight..
6310         if (false == tg.showPanel(pan)) {
6311             var nv = Roo.bootstrap.NavGroup.get(this.navId);
6312             if (nv) {
6313                 var onav = nv.getWasActive();
6314                 if (onav) {
6315                     onav.setActive(true, false, true);
6316                 }
6317             }
6318             
6319         }
6320         
6321         
6322         
6323     },
6324      // this should not be here...
6325     setDisabled : function(state)
6326     {
6327         this.disabled = state;
6328         if (!state ) {
6329             this.el.removeClass('disabled');
6330         } else if (!this.el.hasClass('disabled')) {
6331             this.el.addClass('disabled');
6332         }
6333         
6334     },
6335     
6336     /**
6337      * Fetch the element to display the tooltip on.
6338      * @return {Roo.Element} defaults to this.el
6339      */
6340     tooltipEl : function()
6341     {
6342         return this.el.select('' + this.tagtype + '', true).first();
6343     },
6344     
6345     scrollToElement : function(e)
6346     {
6347         var c = document.body;
6348         
6349         /*
6350          * Firefox / IE places the overflow at the html level, unless specifically styled to behave differently.
6351          */
6352         if(Roo.isFirefox || Roo.isIE || Roo.isIE11){
6353             c = document.documentElement;
6354         }
6355         
6356         var target = Roo.get(c).select('a[name=' + this.href.split('#')[1] +']', true).first();
6357         
6358         if(!target){
6359             return;
6360         }
6361
6362         var o = target.calcOffsetsTo(c);
6363         
6364         var options = {
6365             target : target,
6366             value : o[1]
6367         };
6368         
6369         this.fireEvent('scrollto', this, options, e);
6370         
6371         Roo.get(c).scrollTo('top', options.value, true);
6372         
6373         return;
6374     }
6375 });
6376  
6377
6378  /*
6379  * - LGPL
6380  *
6381  * sidebar item
6382  *
6383  *  li
6384  *    <span> icon </span>
6385  *    <span> text </span>
6386  *    <span>badge </span>
6387  */
6388
6389 /**
6390  * @class Roo.bootstrap.NavSidebarItem
6391  * @extends Roo.bootstrap.NavItem
6392  * Bootstrap Navbar.NavSidebarItem class
6393  * {String} badgeWeight (default|primary|success|info|warning|danger)the extra classes for the badge
6394  * {Boolean} open is the menu open
6395  * {Boolean} buttonView use button as the tigger el rather that a (default false)
6396  * {String} buttonWeight (default|primary|success|info|warning|danger)the extra classes for the button
6397  * {String} buttonSize (sm|md|lg)the extra classes for the button
6398  * {Boolean} showArrow show arrow next to the text (default true)
6399  * @constructor
6400  * Create a new Navbar Button
6401  * @param {Object} config The config object
6402  */
6403 Roo.bootstrap.NavSidebarItem = function(config){
6404     Roo.bootstrap.NavSidebarItem.superclass.constructor.call(this, config);
6405     this.addEvents({
6406         // raw events
6407         /**
6408          * @event click
6409          * The raw click event for the entire grid.
6410          * @param {Roo.EventObject} e
6411          */
6412         "click" : true,
6413          /**
6414             * @event changed
6415             * Fires when the active item active state changes
6416             * @param {Roo.bootstrap.NavSidebarItem} this
6417             * @param {boolean} state the new state
6418              
6419          */
6420         'changed': true
6421     });
6422    
6423 };
6424
6425 Roo.extend(Roo.bootstrap.NavSidebarItem, Roo.bootstrap.NavItem,  {
6426     
6427     badgeWeight : 'default',
6428     
6429     open: false,
6430     
6431     buttonView : false,
6432     
6433     buttonWeight : 'default',
6434     
6435     buttonSize : 'md',
6436     
6437     showArrow : true,
6438     
6439     getAutoCreate : function(){
6440         
6441         
6442         var a = {
6443                 tag: 'a',
6444                 href : this.href || '#',
6445                 cls: '',
6446                 html : '',
6447                 cn : []
6448         };
6449         
6450         if(this.buttonView){
6451             a = {
6452                 tag: 'button',
6453                 href : this.href || '#',
6454                 cls: 'btn btn-' + this.buttonWeight + ' btn-' + this.buttonSize + 'roo-button-dropdown-toggle',
6455                 html : this.html,
6456                 cn : []
6457             };
6458         }
6459         
6460         var cfg = {
6461             tag: 'li',
6462             cls: '',
6463             cn: [ a ]
6464         };
6465         
6466         if (this.active) {
6467             cfg.cls += ' active';
6468         }
6469         
6470         if (this.disabled) {
6471             cfg.cls += ' disabled';
6472         }
6473         if (this.open) {
6474             cfg.cls += ' open x-open';
6475         }
6476         // left icon..
6477         if (this.glyphicon || this.icon) {
6478             var c = this.glyphicon  ? ('glyphicon glyphicon-'+this.glyphicon)  : this.icon;
6479             a.cn.push({ tag : 'i', cls : c }) ;
6480         }
6481         
6482         if(!this.buttonView){
6483             var span = {
6484                 tag: 'span',
6485                 html : this.html || ''
6486             };
6487
6488             a.cn.push(span);
6489             
6490         }
6491         
6492         if (this.badge !== '') {
6493             a.cn.push({ tag: 'span',  cls : 'badge pull-right badge-' + this.badgeWeight, html: this.badge }); 
6494         }
6495         
6496         if (this.menu) {
6497             
6498             if(this.showArrow){
6499                 a.cn.push({ tag : 'i', cls : 'glyphicon glyphicon-chevron-down pull-right'});
6500             }
6501             
6502             a.cls += ' dropdown-toggle treeview' ;
6503         }
6504         
6505         return cfg;
6506     },
6507     
6508     initEvents : function()
6509     { 
6510         if (typeof (this.menu) != 'undefined') {
6511             this.menu.parentType = this.xtype;
6512             this.menu.triggerEl = this.el;
6513             this.menu = this.addxtype(Roo.apply({}, this.menu));
6514         }
6515         
6516         this.el.on('click', this.onClick, this);
6517         
6518         if(this.badge !== ''){
6519             this.badgeEl = this.el.select('.badge', true).first().setVisibilityMode(Roo.Element.DISPLAY);
6520         }
6521         
6522     },
6523     
6524     onClick : function(e)
6525     {
6526         if(this.disabled){
6527             e.preventDefault();
6528             return;
6529         }
6530         
6531         if(this.preventDefault){
6532             e.preventDefault();
6533         }
6534         
6535         this.fireEvent('click', this, e);
6536     },
6537     
6538     disable : function()
6539     {
6540         this.setDisabled(true);
6541     },
6542     
6543     enable : function()
6544     {
6545         this.setDisabled(false);
6546     },
6547     
6548     setDisabled : function(state)
6549     {
6550         if(this.disabled == state){
6551             return;
6552         }
6553         
6554         this.disabled = state;
6555         
6556         if (state) {
6557             this.el.addClass('disabled');
6558             return;
6559         }
6560         
6561         this.el.removeClass('disabled');
6562         
6563         return;
6564     },
6565     
6566     setActive : function(state)
6567     {
6568         if(this.active == state){
6569             return;
6570         }
6571         
6572         this.active = state;
6573         
6574         if (state) {
6575             this.el.addClass('active');
6576             return;
6577         }
6578         
6579         this.el.removeClass('active');
6580         
6581         return;
6582     },
6583     
6584     isActive: function () 
6585     {
6586         return this.active;
6587     },
6588     
6589     setBadge : function(str)
6590     {
6591         if(!this.badgeEl){
6592             return;
6593         }
6594         
6595         this.badgeEl.dom.innerHTML = str;
6596     }
6597     
6598    
6599      
6600  
6601 });
6602  
6603
6604  /*
6605  * - LGPL
6606  *
6607  * row
6608  * 
6609  */
6610
6611 /**
6612  * @class Roo.bootstrap.Row
6613  * @extends Roo.bootstrap.Component
6614  * Bootstrap Row class (contains columns...)
6615  * 
6616  * @constructor
6617  * Create a new Row
6618  * @param {Object} config The config object
6619  */
6620
6621 Roo.bootstrap.Row = function(config){
6622     Roo.bootstrap.Row.superclass.constructor.call(this, config);
6623 };
6624
6625 Roo.extend(Roo.bootstrap.Row, Roo.bootstrap.Component,  {
6626     
6627     getAutoCreate : function(){
6628        return {
6629             cls: 'row clearfix'
6630        };
6631     }
6632     
6633     
6634 });
6635
6636  
6637
6638  /*
6639  * - LGPL
6640  *
6641  * pagination
6642  * 
6643  */
6644
6645 /**
6646  * @class Roo.bootstrap.Pagination
6647  * @extends Roo.bootstrap.Component
6648  * Bootstrap Pagination class
6649  * @cfg {String} size xs | sm | md | lg
6650  * @cfg {Boolean} inverse false | true
6651  * 
6652  * @constructor
6653  * Create a new Pagination
6654  * @param {Object} config The config object
6655  */
6656
6657 Roo.bootstrap.Pagination = function(config){
6658     Roo.bootstrap.Pagination.superclass.constructor.call(this, config);
6659 };
6660
6661 Roo.extend(Roo.bootstrap.Pagination, Roo.bootstrap.Component,  {
6662     
6663     cls: false,
6664     size: false,
6665     inverse: false,
6666     
6667     getAutoCreate : function(){
6668         var cfg = {
6669             tag: 'ul',
6670                 cls: 'pagination'
6671         };
6672         if (this.inverse) {
6673             cfg.cls += ' inverse';
6674         }
6675         if (this.html) {
6676             cfg.html=this.html;
6677         }
6678         if (this.cls) {
6679             cfg.cls += " " + this.cls;
6680         }
6681         return cfg;
6682     }
6683    
6684 });
6685
6686  
6687
6688  /*
6689  * - LGPL
6690  *
6691  * Pagination item
6692  * 
6693  */
6694
6695
6696 /**
6697  * @class Roo.bootstrap.PaginationItem
6698  * @extends Roo.bootstrap.Component
6699  * Bootstrap PaginationItem class
6700  * @cfg {String} html text
6701  * @cfg {String} href the link
6702  * @cfg {Boolean} preventDefault (true | false) default true
6703  * @cfg {Boolean} active (true | false) default false
6704  * @cfg {Boolean} disabled default false
6705  * 
6706  * 
6707  * @constructor
6708  * Create a new PaginationItem
6709  * @param {Object} config The config object
6710  */
6711
6712
6713 Roo.bootstrap.PaginationItem = function(config){
6714     Roo.bootstrap.PaginationItem.superclass.constructor.call(this, config);
6715     this.addEvents({
6716         // raw events
6717         /**
6718          * @event click
6719          * The raw click event for the entire grid.
6720          * @param {Roo.EventObject} e
6721          */
6722         "click" : true
6723     });
6724 };
6725
6726 Roo.extend(Roo.bootstrap.PaginationItem, Roo.bootstrap.Component,  {
6727     
6728     href : false,
6729     html : false,
6730     preventDefault: true,
6731     active : false,
6732     cls : false,
6733     disabled: false,
6734     
6735     getAutoCreate : function(){
6736         var cfg= {
6737             tag: 'li',
6738             cn: [
6739                 {
6740                     tag : 'a',
6741                     href : this.href ? this.href : '#',
6742                     html : this.html ? this.html : ''
6743                 }
6744             ]
6745         };
6746         
6747         if(this.cls){
6748             cfg.cls = this.cls;
6749         }
6750         
6751         if(this.disabled){
6752             cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' disabled' : 'disabled';
6753         }
6754         
6755         if(this.active){
6756             cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' active' : 'active';
6757         }
6758         
6759         return cfg;
6760     },
6761     
6762     initEvents: function() {
6763         
6764         this.el.on('click', this.onClick, this);
6765         
6766     },
6767     onClick : function(e)
6768     {
6769         Roo.log('PaginationItem on click ');
6770         if(this.preventDefault){
6771             e.preventDefault();
6772         }
6773         
6774         if(this.disabled){
6775             return;
6776         }
6777         
6778         this.fireEvent('click', this, e);
6779     }
6780    
6781 });
6782
6783  
6784
6785  /*
6786  * - LGPL
6787  *
6788  * slider
6789  * 
6790  */
6791
6792
6793 /**
6794  * @class Roo.bootstrap.Slider
6795  * @extends Roo.bootstrap.Component
6796  * Bootstrap Slider class
6797  *    
6798  * @constructor
6799  * Create a new Slider
6800  * @param {Object} config The config object
6801  */
6802
6803 Roo.bootstrap.Slider = function(config){
6804     Roo.bootstrap.Slider.superclass.constructor.call(this, config);
6805 };
6806
6807 Roo.extend(Roo.bootstrap.Slider, Roo.bootstrap.Component,  {
6808     
6809     getAutoCreate : function(){
6810         
6811         var cfg = {
6812             tag: 'div',
6813             cls: 'slider slider-sample1 vertical-handler ui-slider ui-slider-horizontal ui-widget ui-widget-content ui-corner-all',
6814             cn: [
6815                 {
6816                     tag: 'a',
6817                     cls: 'ui-slider-handle ui-state-default ui-corner-all'
6818                 }
6819             ]
6820         };
6821         
6822         return cfg;
6823     }
6824    
6825 });
6826
6827  /*
6828  * Based on:
6829  * Ext JS Library 1.1.1
6830  * Copyright(c) 2006-2007, Ext JS, LLC.
6831  *
6832  * Originally Released Under LGPL - original licence link has changed is not relivant.
6833  *
6834  * Fork - LGPL
6835  * <script type="text/javascript">
6836  */
6837  
6838
6839 /**
6840  * @class Roo.grid.ColumnModel
6841  * @extends Roo.util.Observable
6842  * This is the default implementation of a ColumnModel used by the Grid. It defines
6843  * the columns in the grid.
6844  * <br>Usage:<br>
6845  <pre><code>
6846  var colModel = new Roo.grid.ColumnModel([
6847         {header: "Ticker", width: 60, sortable: true, locked: true},
6848         {header: "Company Name", width: 150, sortable: true},
6849         {header: "Market Cap.", width: 100, sortable: true},
6850         {header: "$ Sales", width: 100, sortable: true, renderer: money},
6851         {header: "Employees", width: 100, sortable: true, resizable: false}
6852  ]);
6853  </code></pre>
6854  * <p>
6855  
6856  * The config options listed for this class are options which may appear in each
6857  * individual column definition.
6858  * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
6859  * @constructor
6860  * @param {Object} config An Array of column config objects. See this class's
6861  * config objects for details.
6862 */
6863 Roo.grid.ColumnModel = function(config){
6864         /**
6865      * The config passed into the constructor
6866      */
6867     this.config = config;
6868     this.lookup = {};
6869
6870     // if no id, create one
6871     // if the column does not have a dataIndex mapping,
6872     // map it to the order it is in the config
6873     for(var i = 0, len = config.length; i < len; i++){
6874         var c = config[i];
6875         if(typeof c.dataIndex == "undefined"){
6876             c.dataIndex = i;
6877         }
6878         if(typeof c.renderer == "string"){
6879             c.renderer = Roo.util.Format[c.renderer];
6880         }
6881         if(typeof c.id == "undefined"){
6882             c.id = Roo.id();
6883         }
6884         if(c.editor && c.editor.xtype){
6885             c.editor  = Roo.factory(c.editor, Roo.grid);
6886         }
6887         if(c.editor && c.editor.isFormField){
6888             c.editor = new Roo.grid.GridEditor(c.editor);
6889         }
6890         this.lookup[c.id] = c;
6891     }
6892
6893     /**
6894      * The width of columns which have no width specified (defaults to 100)
6895      * @type Number
6896      */
6897     this.defaultWidth = 100;
6898
6899     /**
6900      * Default sortable of columns which have no sortable specified (defaults to false)
6901      * @type Boolean
6902      */
6903     this.defaultSortable = false;
6904
6905     this.addEvents({
6906         /**
6907              * @event widthchange
6908              * Fires when the width of a column changes.
6909              * @param {ColumnModel} this
6910              * @param {Number} columnIndex The column index
6911              * @param {Number} newWidth The new width
6912              */
6913             "widthchange": true,
6914         /**
6915              * @event headerchange
6916              * Fires when the text of a header changes.
6917              * @param {ColumnModel} this
6918              * @param {Number} columnIndex The column index
6919              * @param {Number} newText The new header text
6920              */
6921             "headerchange": true,
6922         /**
6923              * @event hiddenchange
6924              * Fires when a column is hidden or "unhidden".
6925              * @param {ColumnModel} this
6926              * @param {Number} columnIndex The column index
6927              * @param {Boolean} hidden true if hidden, false otherwise
6928              */
6929             "hiddenchange": true,
6930             /**
6931          * @event columnmoved
6932          * Fires when a column is moved.
6933          * @param {ColumnModel} this
6934          * @param {Number} oldIndex
6935          * @param {Number} newIndex
6936          */
6937         "columnmoved" : true,
6938         /**
6939          * @event columlockchange
6940          * Fires when a column's locked state is changed
6941          * @param {ColumnModel} this
6942          * @param {Number} colIndex
6943          * @param {Boolean} locked true if locked
6944          */
6945         "columnlockchange" : true
6946     });
6947     Roo.grid.ColumnModel.superclass.constructor.call(this);
6948 };
6949 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
6950     /**
6951      * @cfg {String} header The header text to display in the Grid view.
6952      */
6953     /**
6954      * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
6955      * {@link Roo.data.Record} definition from which to draw the column's value. If not
6956      * specified, the column's index is used as an index into the Record's data Array.
6957      */
6958     /**
6959      * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
6960      * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
6961      */
6962     /**
6963      * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
6964      * Defaults to the value of the {@link #defaultSortable} property.
6965      * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
6966      */
6967     /**
6968      * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid.  Defaults to false.
6969      */
6970     /**
6971      * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed.  Defaults to false.
6972      */
6973     /**
6974      * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
6975      */
6976     /**
6977      * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
6978      */
6979     /**
6980      * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
6981      * given the cell's data value. See {@link #setRenderer}. If not specified, the
6982      * default renderer returns the escaped data value. If an object is returned (bootstrap only)
6983      * then it is treated as a Roo Component object instance, and it is rendered after the initial row is rendered
6984      */
6985        /**
6986      * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor 
6987      */
6988     /**
6989      * @cfg {String} align (Optional) Set the CSS text-align property of the column.  Defaults to undefined.
6990      */
6991     /**
6992      * @cfg {String} valign (Optional) Set the CSS vertical-align property of the column (eg. middle, top, bottom etc).  Defaults to undefined.
6993      */
6994     /**
6995      * @cfg {String} cursor (Optional)
6996      */
6997     /**
6998      * @cfg {String} tooltip (Optional)
6999      */
7000     /**
7001      * @cfg {Number} xs (Optional)
7002      */
7003     /**
7004      * @cfg {Number} sm (Optional)
7005      */
7006     /**
7007      * @cfg {Number} md (Optional)
7008      */
7009     /**
7010      * @cfg {Number} lg (Optional)
7011      */
7012     /**
7013      * Returns the id of the column at the specified index.
7014      * @param {Number} index The column index
7015      * @return {String} the id
7016      */
7017     getColumnId : function(index){
7018         return this.config[index].id;
7019     },
7020
7021     /**
7022      * Returns the column for a specified id.
7023      * @param {String} id The column id
7024      * @return {Object} the column
7025      */
7026     getColumnById : function(id){
7027         return this.lookup[id];
7028     },
7029
7030     
7031     /**
7032      * Returns the column for a specified dataIndex.
7033      * @param {String} dataIndex The column dataIndex
7034      * @return {Object|Boolean} the column or false if not found
7035      */
7036     getColumnByDataIndex: function(dataIndex){
7037         var index = this.findColumnIndex(dataIndex);
7038         return index > -1 ? this.config[index] : false;
7039     },
7040     
7041     /**
7042      * Returns the index for a specified column id.
7043      * @param {String} id The column id
7044      * @return {Number} the index, or -1 if not found
7045      */
7046     getIndexById : function(id){
7047         for(var i = 0, len = this.config.length; i < len; i++){
7048             if(this.config[i].id == id){
7049                 return i;
7050             }
7051         }
7052         return -1;
7053     },
7054     
7055     /**
7056      * Returns the index for a specified column dataIndex.
7057      * @param {String} dataIndex The column dataIndex
7058      * @return {Number} the index, or -1 if not found
7059      */
7060     
7061     findColumnIndex : function(dataIndex){
7062         for(var i = 0, len = this.config.length; i < len; i++){
7063             if(this.config[i].dataIndex == dataIndex){
7064                 return i;
7065             }
7066         }
7067         return -1;
7068     },
7069     
7070     
7071     moveColumn : function(oldIndex, newIndex){
7072         var c = this.config[oldIndex];
7073         this.config.splice(oldIndex, 1);
7074         this.config.splice(newIndex, 0, c);
7075         this.dataMap = null;
7076         this.fireEvent("columnmoved", this, oldIndex, newIndex);
7077     },
7078
7079     isLocked : function(colIndex){
7080         return this.config[colIndex].locked === true;
7081     },
7082
7083     setLocked : function(colIndex, value, suppressEvent){
7084         if(this.isLocked(colIndex) == value){
7085             return;
7086         }
7087         this.config[colIndex].locked = value;
7088         if(!suppressEvent){
7089             this.fireEvent("columnlockchange", this, colIndex, value);
7090         }
7091     },
7092
7093     getTotalLockedWidth : function(){
7094         var totalWidth = 0;
7095         for(var i = 0; i < this.config.length; i++){
7096             if(this.isLocked(i) && !this.isHidden(i)){
7097                 this.totalWidth += this.getColumnWidth(i);
7098             }
7099         }
7100         return totalWidth;
7101     },
7102
7103     getLockedCount : function(){
7104         for(var i = 0, len = this.config.length; i < len; i++){
7105             if(!this.isLocked(i)){
7106                 return i;
7107             }
7108         }
7109         
7110         return this.config.length;
7111     },
7112
7113     /**
7114      * Returns the number of columns.
7115      * @return {Number}
7116      */
7117     getColumnCount : function(visibleOnly){
7118         if(visibleOnly === true){
7119             var c = 0;
7120             for(var i = 0, len = this.config.length; i < len; i++){
7121                 if(!this.isHidden(i)){
7122                     c++;
7123                 }
7124             }
7125             return c;
7126         }
7127         return this.config.length;
7128     },
7129
7130     /**
7131      * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
7132      * @param {Function} fn
7133      * @param {Object} scope (optional)
7134      * @return {Array} result
7135      */
7136     getColumnsBy : function(fn, scope){
7137         var r = [];
7138         for(var i = 0, len = this.config.length; i < len; i++){
7139             var c = this.config[i];
7140             if(fn.call(scope||this, c, i) === true){
7141                 r[r.length] = c;
7142             }
7143         }
7144         return r;
7145     },
7146
7147     /**
7148      * Returns true if the specified column is sortable.
7149      * @param {Number} col The column index
7150      * @return {Boolean}
7151      */
7152     isSortable : function(col){
7153         if(typeof this.config[col].sortable == "undefined"){
7154             return this.defaultSortable;
7155         }
7156         return this.config[col].sortable;
7157     },
7158
7159     /**
7160      * Returns the rendering (formatting) function defined for the column.
7161      * @param {Number} col The column index.
7162      * @return {Function} The function used to render the cell. See {@link #setRenderer}.
7163      */
7164     getRenderer : function(col){
7165         if(!this.config[col].renderer){
7166             return Roo.grid.ColumnModel.defaultRenderer;
7167         }
7168         return this.config[col].renderer;
7169     },
7170
7171     /**
7172      * Sets the rendering (formatting) function for a column.
7173      * @param {Number} col The column index
7174      * @param {Function} fn The function to use to process the cell's raw data
7175      * to return HTML markup for the grid view. The render function is called with
7176      * the following parameters:<ul>
7177      * <li>Data value.</li>
7178      * <li>Cell metadata. An object in which you may set the following attributes:<ul>
7179      * <li>css A CSS style string to apply to the table cell.</li>
7180      * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
7181      * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
7182      * <li>Row index</li>
7183      * <li>Column index</li>
7184      * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
7185      */
7186     setRenderer : function(col, fn){
7187         this.config[col].renderer = fn;
7188     },
7189
7190     /**
7191      * Returns the width for the specified column.
7192      * @param {Number} col The column index
7193      * @return {Number}
7194      */
7195     getColumnWidth : function(col){
7196         return this.config[col].width * 1 || this.defaultWidth;
7197     },
7198
7199     /**
7200      * Sets the width for a column.
7201      * @param {Number} col The column index
7202      * @param {Number} width The new width
7203      */
7204     setColumnWidth : function(col, width, suppressEvent){
7205         this.config[col].width = width;
7206         this.totalWidth = null;
7207         if(!suppressEvent){
7208              this.fireEvent("widthchange", this, col, width);
7209         }
7210     },
7211
7212     /**
7213      * Returns the total width of all columns.
7214      * @param {Boolean} includeHidden True to include hidden column widths
7215      * @return {Number}
7216      */
7217     getTotalWidth : function(includeHidden){
7218         if(!this.totalWidth){
7219             this.totalWidth = 0;
7220             for(var i = 0, len = this.config.length; i < len; i++){
7221                 if(includeHidden || !this.isHidden(i)){
7222                     this.totalWidth += this.getColumnWidth(i);
7223                 }
7224             }
7225         }
7226         return this.totalWidth;
7227     },
7228
7229     /**
7230      * Returns the header for the specified column.
7231      * @param {Number} col The column index
7232      * @return {String}
7233      */
7234     getColumnHeader : function(col){
7235         return this.config[col].header;
7236     },
7237
7238     /**
7239      * Sets the header for a column.
7240      * @param {Number} col The column index
7241      * @param {String} header The new header
7242      */
7243     setColumnHeader : function(col, header){
7244         this.config[col].header = header;
7245         this.fireEvent("headerchange", this, col, header);
7246     },
7247
7248     /**
7249      * Returns the tooltip for the specified column.
7250      * @param {Number} col The column index
7251      * @return {String}
7252      */
7253     getColumnTooltip : function(col){
7254             return this.config[col].tooltip;
7255     },
7256     /**
7257      * Sets the tooltip for a column.
7258      * @param {Number} col The column index
7259      * @param {String} tooltip The new tooltip
7260      */
7261     setColumnTooltip : function(col, tooltip){
7262             this.config[col].tooltip = tooltip;
7263     },
7264
7265     /**
7266      * Returns the dataIndex for the specified column.
7267      * @param {Number} col The column index
7268      * @return {Number}
7269      */
7270     getDataIndex : function(col){
7271         return this.config[col].dataIndex;
7272     },
7273
7274     /**
7275      * Sets the dataIndex for a column.
7276      * @param {Number} col The column index
7277      * @param {Number} dataIndex The new dataIndex
7278      */
7279     setDataIndex : function(col, dataIndex){
7280         this.config[col].dataIndex = dataIndex;
7281     },
7282
7283     
7284     
7285     /**
7286      * Returns true if the cell is editable.
7287      * @param {Number} colIndex The column index
7288      * @param {Number} rowIndex The row index - this is nto actually used..?
7289      * @return {Boolean}
7290      */
7291     isCellEditable : function(colIndex, rowIndex){
7292         return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
7293     },
7294
7295     /**
7296      * Returns the editor defined for the cell/column.
7297      * return false or null to disable editing.
7298      * @param {Number} colIndex The column index
7299      * @param {Number} rowIndex The row index
7300      * @return {Object}
7301      */
7302     getCellEditor : function(colIndex, rowIndex){
7303         return this.config[colIndex].editor;
7304     },
7305
7306     /**
7307      * Sets if a column is editable.
7308      * @param {Number} col The column index
7309      * @param {Boolean} editable True if the column is editable
7310      */
7311     setEditable : function(col, editable){
7312         this.config[col].editable = editable;
7313     },
7314
7315
7316     /**
7317      * Returns true if the column is hidden.
7318      * @param {Number} colIndex The column index
7319      * @return {Boolean}
7320      */
7321     isHidden : function(colIndex){
7322         return this.config[colIndex].hidden;
7323     },
7324
7325
7326     /**
7327      * Returns true if the column width cannot be changed
7328      */
7329     isFixed : function(colIndex){
7330         return this.config[colIndex].fixed;
7331     },
7332
7333     /**
7334      * Returns true if the column can be resized
7335      * @return {Boolean}
7336      */
7337     isResizable : function(colIndex){
7338         return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
7339     },
7340     /**
7341      * Sets if a column is hidden.
7342      * @param {Number} colIndex The column index
7343      * @param {Boolean} hidden True if the column is hidden
7344      */
7345     setHidden : function(colIndex, hidden){
7346         this.config[colIndex].hidden = hidden;
7347         this.totalWidth = null;
7348         this.fireEvent("hiddenchange", this, colIndex, hidden);
7349     },
7350
7351     /**
7352      * Sets the editor for a column.
7353      * @param {Number} col The column index
7354      * @param {Object} editor The editor object
7355      */
7356     setEditor : function(col, editor){
7357         this.config[col].editor = editor;
7358     }
7359 });
7360
7361 Roo.grid.ColumnModel.defaultRenderer = function(value)
7362 {
7363     if(typeof value == "object") {
7364         return value;
7365     }
7366         if(typeof value == "string" && value.length < 1){
7367             return "&#160;";
7368         }
7369     
7370         return String.format("{0}", value);
7371 };
7372
7373 // Alias for backwards compatibility
7374 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
7375 /*
7376  * Based on:
7377  * Ext JS Library 1.1.1
7378  * Copyright(c) 2006-2007, Ext JS, LLC.
7379  *
7380  * Originally Released Under LGPL - original licence link has changed is not relivant.
7381  *
7382  * Fork - LGPL
7383  * <script type="text/javascript">
7384  */
7385  
7386 /**
7387  * @class Roo.LoadMask
7388  * A simple utility class for generically masking elements while loading data.  If the element being masked has
7389  * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
7390  * process and the mask element will be cached for reuse.  For all other elements, this mask will replace the
7391  * element's UpdateManager load indicator and will be destroyed after the initial load.
7392  * @constructor
7393  * Create a new LoadMask
7394  * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
7395  * @param {Object} config The config object
7396  */
7397 Roo.LoadMask = function(el, config){
7398     this.el = Roo.get(el);
7399     Roo.apply(this, config);
7400     if(this.store){
7401         this.store.on('beforeload', this.onBeforeLoad, this);
7402         this.store.on('load', this.onLoad, this);
7403         this.store.on('loadexception', this.onLoadException, this);
7404         this.removeMask = false;
7405     }else{
7406         var um = this.el.getUpdateManager();
7407         um.showLoadIndicator = false; // disable the default indicator
7408         um.on('beforeupdate', this.onBeforeLoad, this);
7409         um.on('update', this.onLoad, this);
7410         um.on('failure', this.onLoad, this);
7411         this.removeMask = true;
7412     }
7413 };
7414
7415 Roo.LoadMask.prototype = {
7416     /**
7417      * @cfg {Boolean} removeMask
7418      * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
7419      * False to persist the mask element reference for multiple uses (e.g., for paged data widgets).  Defaults to false.
7420      */
7421     /**
7422      * @cfg {String} msg
7423      * The text to display in a centered loading message box (defaults to 'Loading...')
7424      */
7425     msg : 'Loading...',
7426     /**
7427      * @cfg {String} msgCls
7428      * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
7429      */
7430     msgCls : 'x-mask-loading',
7431
7432     /**
7433      * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
7434      * @type Boolean
7435      */
7436     disabled: false,
7437
7438     /**
7439      * Disables the mask to prevent it from being displayed
7440      */
7441     disable : function(){
7442        this.disabled = true;
7443     },
7444
7445     /**
7446      * Enables the mask so that it can be displayed
7447      */
7448     enable : function(){
7449         this.disabled = false;
7450     },
7451     
7452     onLoadException : function()
7453     {
7454         Roo.log(arguments);
7455         
7456         if (typeof(arguments[3]) != 'undefined') {
7457             Roo.MessageBox.alert("Error loading",arguments[3]);
7458         } 
7459         /*
7460         try {
7461             if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
7462                 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
7463             }   
7464         } catch(e) {
7465             
7466         }
7467         */
7468     
7469         (function() { this.el.unmask(this.removeMask); }).defer(50, this);
7470     },
7471     // private
7472     onLoad : function()
7473     {
7474         (function() { this.el.unmask(this.removeMask); }).defer(50, this);
7475     },
7476
7477     // private
7478     onBeforeLoad : function(){
7479         if(!this.disabled){
7480             (function() { this.el.mask(this.msg, this.msgCls); }).defer(50, this);
7481         }
7482     },
7483
7484     // private
7485     destroy : function(){
7486         if(this.store){
7487             this.store.un('beforeload', this.onBeforeLoad, this);
7488             this.store.un('load', this.onLoad, this);
7489             this.store.un('loadexception', this.onLoadException, this);
7490         }else{
7491             var um = this.el.getUpdateManager();
7492             um.un('beforeupdate', this.onBeforeLoad, this);
7493             um.un('update', this.onLoad, this);
7494             um.un('failure', this.onLoad, this);
7495         }
7496     }
7497 };/*
7498  * - LGPL
7499  *
7500  * table
7501  * 
7502  */
7503
7504 /**
7505  * @class Roo.bootstrap.Table
7506  * @extends Roo.bootstrap.Component
7507  * Bootstrap Table class
7508  * @cfg {String} cls table class
7509  * @cfg {String} align (left|center|right) Specifies the alignment of a table according to surrounding text
7510  * @cfg {String} bgcolor Specifies the background color for a table
7511  * @cfg {Number} border Specifies whether the table cells should have borders or not
7512  * @cfg {Number} cellpadding Specifies the space between the cell wall and the cell content
7513  * @cfg {Number} cellspacing Specifies the space between cells
7514  * @cfg {String} frame Specifies which parts of the outside borders that should be visible
7515  * @cfg {String} rules Specifies which parts of the inside borders that should be visible
7516  * @cfg {String} sortable Specifies that the table should be sortable
7517  * @cfg {String} summary Specifies a summary of the content of a table
7518  * @cfg {Number} width Specifies the width of a table
7519  * @cfg {String} layout table layout (auto | fixed | initial | inherit)
7520  * 
7521  * @cfg {boolean} striped Should the rows be alternative striped
7522  * @cfg {boolean} bordered Add borders to the table
7523  * @cfg {boolean} hover Add hover highlighting
7524  * @cfg {boolean} condensed Format condensed
7525  * @cfg {boolean} responsive Format condensed
7526  * @cfg {Boolean} loadMask (true|false) default false
7527  * @cfg {Boolean} footerShow (true|false) generate tfoot, default true
7528  * @cfg {Boolean} headerShow (true|false) generate thead, default true
7529  * @cfg {Boolean} rowSelection (true|false) default false
7530  * @cfg {Boolean} cellSelection (true|false) default false
7531  * @cfg {Boolean} scrollBody (true|false) default false - body scrolled / fixed header
7532  * @cfg {Roo.bootstrap.PagingToolbar} footer  a paging toolbar
7533  * @cfg {Boolean} lazyLoad  auto load data while scrolling to the end (default false)
7534  * @cfg {Boolean} auto_hide_footer  auto hide footer if only one page (default false)
7535  
7536  * 
7537  * @constructor
7538  * Create a new Table
7539  * @param {Object} config The config object
7540  */
7541
7542 Roo.bootstrap.Table = function(config){
7543     Roo.bootstrap.Table.superclass.constructor.call(this, config);
7544     
7545   
7546     
7547     // BC...
7548     this.rowSelection = (typeof(config.rowSelection) != 'undefined') ? config.rowSelection : this.rowSelection;
7549     this.cellSelection = (typeof(config.cellSelection) != 'undefined') ? config.cellSelection : this.cellSelection;
7550     this.headerShow = (typeof(config.thead) != 'undefined') ? config.thead : this.headerShow;
7551     this.footerShow = (typeof(config.tfoot) != 'undefined') ? config.tfoot : this.footerShow;
7552     
7553     this.sm = this.sm || {xtype: 'RowSelectionModel'};
7554     if (this.sm) {
7555         this.sm.grid = this;
7556         this.selModel = Roo.factory(this.sm, Roo.bootstrap.Table);
7557         this.sm = this.selModel;
7558         this.sm.xmodule = this.xmodule || false;
7559     }
7560     
7561     if (this.cm && typeof(this.cm.config) == 'undefined') {
7562         this.colModel = new Roo.grid.ColumnModel(this.cm);
7563         this.cm = this.colModel;
7564         this.cm.xmodule = this.xmodule || false;
7565     }
7566     if (this.store) {
7567         this.store= Roo.factory(this.store, Roo.data);
7568         this.ds = this.store;
7569         this.ds.xmodule = this.xmodule || false;
7570          
7571     }
7572     if (this.footer && this.store) {
7573         this.footer.dataSource = this.ds;
7574         this.footer = Roo.factory(this.footer);
7575     }
7576     
7577     /** @private */
7578     this.addEvents({
7579         /**
7580          * @event cellclick
7581          * Fires when a cell is clicked
7582          * @param {Roo.bootstrap.Table} this
7583          * @param {Roo.Element} el
7584          * @param {Number} rowIndex
7585          * @param {Number} columnIndex
7586          * @param {Roo.EventObject} e
7587          */
7588         "cellclick" : true,
7589         /**
7590          * @event celldblclick
7591          * Fires when a cell is double clicked
7592          * @param {Roo.bootstrap.Table} this
7593          * @param {Roo.Element} el
7594          * @param {Number} rowIndex
7595          * @param {Number} columnIndex
7596          * @param {Roo.EventObject} e
7597          */
7598         "celldblclick" : true,
7599         /**
7600          * @event rowclick
7601          * Fires when a row is clicked
7602          * @param {Roo.bootstrap.Table} this
7603          * @param {Roo.Element} el
7604          * @param {Number} rowIndex
7605          * @param {Roo.EventObject} e
7606          */
7607         "rowclick" : true,
7608         /**
7609          * @event rowdblclick
7610          * Fires when a row is double clicked
7611          * @param {Roo.bootstrap.Table} this
7612          * @param {Roo.Element} el
7613          * @param {Number} rowIndex
7614          * @param {Roo.EventObject} e
7615          */
7616         "rowdblclick" : true,
7617         /**
7618          * @event mouseover
7619          * Fires when a mouseover occur
7620          * @param {Roo.bootstrap.Table} this
7621          * @param {Roo.Element} el
7622          * @param {Number} rowIndex
7623          * @param {Number} columnIndex
7624          * @param {Roo.EventObject} e
7625          */
7626         "mouseover" : true,
7627         /**
7628          * @event mouseout
7629          * Fires when a mouseout occur
7630          * @param {Roo.bootstrap.Table} this
7631          * @param {Roo.Element} el
7632          * @param {Number} rowIndex
7633          * @param {Number} columnIndex
7634          * @param {Roo.EventObject} e
7635          */
7636         "mouseout" : true,
7637         /**
7638          * @event rowclass
7639          * Fires when a row is rendered, so you can change add a style to it.
7640          * @param {Roo.bootstrap.Table} this
7641          * @param {Object} rowcfg   contains record  rowIndex colIndex and rowClass - set rowClass to add a style.
7642          */
7643         'rowclass' : true,
7644           /**
7645          * @event rowsrendered
7646          * Fires when all the  rows have been rendered
7647          * @param {Roo.bootstrap.Table} this
7648          */
7649         'rowsrendered' : true,
7650         /**
7651          * @event contextmenu
7652          * The raw contextmenu event for the entire grid.
7653          * @param {Roo.EventObject} e
7654          */
7655         "contextmenu" : true,
7656         /**
7657          * @event rowcontextmenu
7658          * Fires when a row is right clicked
7659          * @param {Roo.bootstrap.Table} this
7660          * @param {Number} rowIndex
7661          * @param {Roo.EventObject} e
7662          */
7663         "rowcontextmenu" : true,
7664         /**
7665          * @event cellcontextmenu
7666          * Fires when a cell is right clicked
7667          * @param {Roo.bootstrap.Table} this
7668          * @param {Number} rowIndex
7669          * @param {Number} cellIndex
7670          * @param {Roo.EventObject} e
7671          */
7672          "cellcontextmenu" : true,
7673          /**
7674          * @event headercontextmenu
7675          * Fires when a header is right clicked
7676          * @param {Roo.bootstrap.Table} this
7677          * @param {Number} columnIndex
7678          * @param {Roo.EventObject} e
7679          */
7680         "headercontextmenu" : true
7681     });
7682 };
7683
7684 Roo.extend(Roo.bootstrap.Table, Roo.bootstrap.Component,  {
7685     
7686     cls: false,
7687     align: false,
7688     bgcolor: false,
7689     border: false,
7690     cellpadding: false,
7691     cellspacing: false,
7692     frame: false,
7693     rules: false,
7694     sortable: false,
7695     summary: false,
7696     width: false,
7697     striped : false,
7698     scrollBody : false,
7699     bordered: false,
7700     hover:  false,
7701     condensed : false,
7702     responsive : false,
7703     sm : false,
7704     cm : false,
7705     store : false,
7706     loadMask : false,
7707     footerShow : true,
7708     headerShow : true,
7709   
7710     rowSelection : false,
7711     cellSelection : false,
7712     layout : false,
7713     
7714     // Roo.Element - the tbody
7715     mainBody: false,
7716     // Roo.Element - thead element
7717     mainHead: false,
7718     
7719     container: false, // used by gridpanel...
7720     
7721     lazyLoad : false,
7722     
7723     CSS : Roo.util.CSS,
7724     
7725     auto_hide_footer : false,
7726     
7727     getAutoCreate : function()
7728     {
7729         var cfg = Roo.apply({}, Roo.bootstrap.Table.superclass.getAutoCreate.call(this));
7730         
7731         cfg = {
7732             tag: 'table',
7733             cls : 'table',
7734             cn : []
7735         };
7736         if (this.scrollBody) {
7737             cfg.cls += ' table-body-fixed';
7738         }    
7739         if (this.striped) {
7740             cfg.cls += ' table-striped';
7741         }
7742         
7743         if (this.hover) {
7744             cfg.cls += ' table-hover';
7745         }
7746         if (this.bordered) {
7747             cfg.cls += ' table-bordered';
7748         }
7749         if (this.condensed) {
7750             cfg.cls += ' table-condensed';
7751         }
7752         if (this.responsive) {
7753             cfg.cls += ' table-responsive';
7754         }
7755         
7756         if (this.cls) {
7757             cfg.cls+=  ' ' +this.cls;
7758         }
7759         
7760         // this lot should be simplifed...
7761         var _t = this;
7762         var cp = [
7763             'align',
7764             'bgcolor',
7765             'border',
7766             'cellpadding',
7767             'cellspacing',
7768             'frame',
7769             'rules',
7770             'sortable',
7771             'summary',
7772             'width'
7773         ].forEach(function(k) {
7774             if (_t[k]) {
7775                 cfg[k] = _t[k];
7776             }
7777         });
7778         
7779         
7780         if (this.layout) {
7781             cfg.style = (typeof(cfg.style) == 'undefined') ? ('table-layout:' + this.layout + ';') : (cfg.style + ('table-layout:' + this.layout + ';'));
7782         }
7783         
7784         if(this.store || this.cm){
7785             if(this.headerShow){
7786                 cfg.cn.push(this.renderHeader());
7787             }
7788             
7789             cfg.cn.push(this.renderBody());
7790             
7791             if(this.footerShow){
7792                 cfg.cn.push(this.renderFooter());
7793             }
7794             // where does this come from?
7795             //cfg.cls+=  ' TableGrid';
7796         }
7797         
7798         return { cn : [ cfg ] };
7799     },
7800     
7801     initEvents : function()
7802     {   
7803         if(!this.store || !this.cm){
7804             return;
7805         }
7806         if (this.selModel) {
7807             this.selModel.initEvents();
7808         }
7809         
7810         
7811         //Roo.log('initEvents with ds!!!!');
7812         
7813         this.mainBody = this.el.select('tbody', true).first();
7814         this.mainHead = this.el.select('thead', true).first();
7815         this.mainFoot = this.el.select('tfoot', true).first();
7816         
7817         
7818         
7819         var _this = this;
7820         
7821         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
7822             e.on('click', _this.sort, _this);
7823         });
7824         
7825         this.mainBody.on("click", this.onClick, this);
7826         this.mainBody.on("dblclick", this.onDblClick, this);
7827         
7828         // why is this done????? = it breaks dialogs??
7829         //this.parent().el.setStyle('position', 'relative');
7830         
7831         
7832         if (this.footer) {
7833             this.footer.parentId = this.id;
7834             this.footer.onRender(this.el.select('tfoot tr td').first(), null);
7835             
7836             if(this.lazyLoad){
7837                 this.el.select('tfoot tr td').first().addClass('hide');
7838             }
7839         } 
7840         
7841         if(this.loadMask) {
7842             this.maskEl = new Roo.LoadMask(this.el, { store : this.ds, msgCls: 'roo-el-mask-msg' });
7843         }
7844         
7845         this.store.on('load', this.onLoad, this);
7846         this.store.on('beforeload', this.onBeforeLoad, this);
7847         this.store.on('update', this.onUpdate, this);
7848         this.store.on('add', this.onAdd, this);
7849         this.store.on("clear", this.clear, this);
7850         
7851         this.el.on("contextmenu", this.onContextMenu, this);
7852         
7853         this.mainBody.on('scroll', this.onBodyScroll, this);
7854         
7855         this.cm.on("headerchange", this.onHeaderChange, this);
7856         
7857         this.cm.on("hiddenchange", this.onHiddenChange, this, arguments);
7858         
7859     },
7860     
7861     onContextMenu : function(e, t)
7862     {
7863         this.processEvent("contextmenu", e);
7864     },
7865     
7866     processEvent : function(name, e)
7867     {
7868         if (name != 'touchstart' ) {
7869             this.fireEvent(name, e);    
7870         }
7871         
7872         var t = e.getTarget();
7873         
7874         var cell = Roo.get(t);
7875         
7876         if(!cell){
7877             return;
7878         }
7879         
7880         if(cell.findParent('tfoot', false, true)){
7881             return;
7882         }
7883         
7884         if(cell.findParent('thead', false, true)){
7885             
7886             if(e.getTarget().nodeName.toLowerCase() != 'th'){
7887                 cell = Roo.get(t).findParent('th', false, true);
7888                 if (!cell) {
7889                     Roo.log("failed to find th in thead?");
7890                     Roo.log(e.getTarget());
7891                     return;
7892                 }
7893             }
7894             
7895             var cellIndex = cell.dom.cellIndex;
7896             
7897             var ename = name == 'touchstart' ? 'click' : name;
7898             this.fireEvent("header" + ename, this, cellIndex, e);
7899             
7900             return;
7901         }
7902         
7903         if(e.getTarget().nodeName.toLowerCase() != 'td'){
7904             cell = Roo.get(t).findParent('td', false, true);
7905             if (!cell) {
7906                 Roo.log("failed to find th in tbody?");
7907                 Roo.log(e.getTarget());
7908                 return;
7909             }
7910         }
7911         
7912         var row = cell.findParent('tr', false, true);
7913         var cellIndex = cell.dom.cellIndex;
7914         var rowIndex = row.dom.rowIndex - 1;
7915         
7916         if(row !== false){
7917             
7918             this.fireEvent("row" + name, this, rowIndex, e);
7919             
7920             if(cell !== false){
7921             
7922                 this.fireEvent("cell" + name, this, rowIndex, cellIndex, e);
7923             }
7924         }
7925         
7926     },
7927     
7928     onMouseover : function(e, el)
7929     {
7930         var cell = Roo.get(el);
7931         
7932         if(!cell){
7933             return;
7934         }
7935         
7936         if(e.getTarget().nodeName.toLowerCase() != 'td'){
7937             cell = cell.findParent('td', false, true);
7938         }
7939         
7940         var row = cell.findParent('tr', false, true);
7941         var cellIndex = cell.dom.cellIndex;
7942         var rowIndex = row.dom.rowIndex - 1; // start from 0
7943         
7944         this.fireEvent('mouseover', this, cell, rowIndex, cellIndex, e);
7945         
7946     },
7947     
7948     onMouseout : function(e, el)
7949     {
7950         var cell = Roo.get(el);
7951         
7952         if(!cell){
7953             return;
7954         }
7955         
7956         if(e.getTarget().nodeName.toLowerCase() != 'td'){
7957             cell = cell.findParent('td', false, true);
7958         }
7959         
7960         var row = cell.findParent('tr', false, true);
7961         var cellIndex = cell.dom.cellIndex;
7962         var rowIndex = row.dom.rowIndex - 1; // start from 0
7963         
7964         this.fireEvent('mouseout', this, cell, rowIndex, cellIndex, e);
7965         
7966     },
7967     
7968     onClick : function(e, el)
7969     {
7970         var cell = Roo.get(el);
7971         
7972         if(!cell || (!this.cellSelection && !this.rowSelection)){
7973             return;
7974         }
7975         
7976         if(e.getTarget().nodeName.toLowerCase() != 'td'){
7977             cell = cell.findParent('td', false, true);
7978         }
7979         
7980         if(!cell || typeof(cell) == 'undefined'){
7981             return;
7982         }
7983         
7984         var row = cell.findParent('tr', false, true);
7985         
7986         if(!row || typeof(row) == 'undefined'){
7987             return;
7988         }
7989         
7990         var cellIndex = cell.dom.cellIndex;
7991         var rowIndex = this.getRowIndex(row);
7992         
7993         // why??? - should these not be based on SelectionModel?
7994         if(this.cellSelection){
7995             this.fireEvent('cellclick', this, cell, rowIndex, cellIndex, e);
7996         }
7997         
7998         if(this.rowSelection){
7999             this.fireEvent('rowclick', this, row, rowIndex, e);
8000         }
8001         
8002         
8003     },
8004         
8005     onDblClick : function(e,el)
8006     {
8007         var cell = Roo.get(el);
8008         
8009         if(!cell || (!this.cellSelection && !this.rowSelection)){
8010             return;
8011         }
8012         
8013         if(e.getTarget().nodeName.toLowerCase() != 'td'){
8014             cell = cell.findParent('td', false, true);
8015         }
8016         
8017         if(!cell || typeof(cell) == 'undefined'){
8018             return;
8019         }
8020         
8021         var row = cell.findParent('tr', false, true);
8022         
8023         if(!row || typeof(row) == 'undefined'){
8024             return;
8025         }
8026         
8027         var cellIndex = cell.dom.cellIndex;
8028         var rowIndex = this.getRowIndex(row);
8029         
8030         if(this.cellSelection){
8031             this.fireEvent('celldblclick', this, cell, rowIndex, cellIndex, e);
8032         }
8033         
8034         if(this.rowSelection){
8035             this.fireEvent('rowdblclick', this, row, rowIndex, e);
8036         }
8037     },
8038     
8039     sort : function(e,el)
8040     {
8041         var col = Roo.get(el);
8042         
8043         if(!col.hasClass('sortable')){
8044             return;
8045         }
8046         
8047         var sort = col.attr('sort');
8048         var dir = 'ASC';
8049         
8050         if(col.select('i', true).first().hasClass('glyphicon-arrow-up')){
8051             dir = 'DESC';
8052         }
8053         
8054         this.store.sortInfo = {field : sort, direction : dir};
8055         
8056         if (this.footer) {
8057             Roo.log("calling footer first");
8058             this.footer.onClick('first');
8059         } else {
8060         
8061             this.store.load({ params : { start : 0 } });
8062         }
8063     },
8064     
8065     renderHeader : function()
8066     {
8067         var header = {
8068             tag: 'thead',
8069             cn : []
8070         };
8071         
8072         var cm = this.cm;
8073         this.totalWidth = 0;
8074         
8075         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
8076             
8077             var config = cm.config[i];
8078             
8079             var c = {
8080                 tag: 'th',
8081                 cls : 'x-hcol-' + i,
8082                 style : '',
8083                 html: cm.getColumnHeader(i)
8084             };
8085             
8086             var hh = '';
8087             
8088             if(typeof(config.sortable) != 'undefined' && config.sortable){
8089                 c.cls = 'sortable';
8090                 c.html = '<i class="glyphicon"></i>' + c.html;
8091             }
8092             
8093             // could use BS4 hidden-..-down 
8094             
8095             if(typeof(config.lgHeader) != 'undefined'){
8096                 hh += '<span class="hidden-xs hidden-sm hidden-md ">' + config.lgHeader + '</span>';
8097             }
8098             
8099             if(typeof(config.mdHeader) != 'undefined'){
8100                 hh += '<span class="hidden-xs hidden-sm hidden-lg">' + config.mdHeader + '</span>';
8101             }
8102             
8103             if(typeof(config.smHeader) != 'undefined'){
8104                 hh += '<span class="hidden-xs hidden-md hidden-lg">' + config.smHeader + '</span>';
8105             }
8106             
8107             if(typeof(config.xsHeader) != 'undefined'){
8108                 hh += '<span class="hidden-sm hidden-md hidden-lg">' + config.xsHeader + '</span>';
8109             }
8110             
8111             if(hh.length){
8112                 c.html = hh;
8113             }
8114             
8115             if(typeof(config.tooltip) != 'undefined'){
8116                 c.tooltip = config.tooltip;
8117             }
8118             
8119             if(typeof(config.colspan) != 'undefined'){
8120                 c.colspan = config.colspan;
8121             }
8122             
8123             if(typeof(config.hidden) != 'undefined' && config.hidden){
8124                 c.style += ' display:none;';
8125             }
8126             
8127             if(typeof(config.dataIndex) != 'undefined'){
8128                 c.sort = config.dataIndex;
8129             }
8130             
8131            
8132             
8133             if(typeof(config.align) != 'undefined' && config.align.length){
8134                 c.style += ' text-align:' + config.align + ';';
8135             }
8136             
8137             if(typeof(config.width) != 'undefined'){
8138                 c.style += ' width:' + config.width + 'px;';
8139                 this.totalWidth += config.width;
8140             } else {
8141                 this.totalWidth += 100; // assume minimum of 100 per column?
8142             }
8143             
8144             if(typeof(config.cls) != 'undefined'){
8145                 c.cls = (typeof(c.cls) == 'undefined') ? config.cls : (c.cls + ' ' + config.cls);
8146             }
8147             
8148             ['xs','sm','md','lg'].map(function(size){
8149                 
8150                 if(typeof(config[size]) == 'undefined'){
8151                     return;
8152                 }
8153                  
8154                 if (!config[size]) { // 0 = hidden
8155                     // BS 4 '0' is treated as hide that column and below.
8156                     c.cls += ' hidden-' + size + ' hidden' + size + '-down';
8157                     return;
8158                 }
8159                 
8160                 c.cls += ' col-' + size + '-' + config[size] + (
8161                     size == 'xs' ? (' col-' + config[size] ) : '' // bs4 col-{num} replaces col-xs
8162                 );
8163                 
8164                 
8165             });
8166             
8167             header.cn.push(c)
8168         }
8169         
8170         return header;
8171     },
8172     
8173     renderBody : function()
8174     {
8175         var body = {
8176             tag: 'tbody',
8177             cn : [
8178                 {
8179                     tag: 'tr',
8180                     cn : [
8181                         {
8182                             tag : 'td',
8183                             colspan :  this.cm.getColumnCount()
8184                         }
8185                     ]
8186                 }
8187             ]
8188         };
8189         
8190         return body;
8191     },
8192     
8193     renderFooter : function()
8194     {
8195         var footer = {
8196             tag: 'tfoot',
8197             cn : [
8198                 {
8199                     tag: 'tr',
8200                     cn : [
8201                         {
8202                             tag : 'td',
8203                             colspan :  this.cm.getColumnCount()
8204                         }
8205                     ]
8206                 }
8207             ]
8208         };
8209         
8210         return footer;
8211     },
8212     
8213     
8214     
8215     onLoad : function()
8216     {
8217 //        Roo.log('ds onload');
8218         this.clear();
8219         
8220         var _this = this;
8221         var cm = this.cm;
8222         var ds = this.store;
8223         
8224         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
8225             e.select('i', true).removeClass(['glyphicon-arrow-up', 'glyphicon-arrow-down']);
8226             if (_this.store.sortInfo) {
8227                     
8228                 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'ASC'){
8229                     e.select('i', true).addClass(['glyphicon-arrow-up']);
8230                 }
8231                 
8232                 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'DESC'){
8233                     e.select('i', true).addClass(['glyphicon-arrow-down']);
8234                 }
8235             }
8236         });
8237         
8238         var tbody =  this.mainBody;
8239               
8240         if(ds.getCount() > 0){
8241             ds.data.each(function(d,rowIndex){
8242                 var row =  this.renderRow(cm, ds, rowIndex);
8243                 
8244                 tbody.createChild(row);
8245                 
8246                 var _this = this;
8247                 
8248                 if(row.cellObjects.length){
8249                     Roo.each(row.cellObjects, function(r){
8250                         _this.renderCellObject(r);
8251                     })
8252                 }
8253                 
8254             }, this);
8255         }
8256         
8257         var tfoot = this.el.select('tfoot', true).first();
8258         
8259         if(this.footerShow && this.auto_hide_footer && this.mainFoot){
8260             
8261             this.mainFoot.setVisibilityMode(Roo.Element.DISPLAY).hide();
8262             
8263             var total = this.ds.getTotalCount();
8264             
8265             if(this.footer.pageSize < total){
8266                 this.mainFoot.show();
8267             }
8268         }
8269         
8270         Roo.each(this.el.select('tbody td', true).elements, function(e){
8271             e.on('mouseover', _this.onMouseover, _this);
8272         });
8273         
8274         Roo.each(this.el.select('tbody td', true).elements, function(e){
8275             e.on('mouseout', _this.onMouseout, _this);
8276         });
8277         this.fireEvent('rowsrendered', this);
8278         
8279         this.autoSize();
8280     },
8281     
8282     
8283     onUpdate : function(ds,record)
8284     {
8285         this.refreshRow(record);
8286         this.autoSize();
8287     },
8288     
8289     onRemove : function(ds, record, index, isUpdate){
8290         if(isUpdate !== true){
8291             this.fireEvent("beforerowremoved", this, index, record);
8292         }
8293         var bt = this.mainBody.dom;
8294         
8295         var rows = this.el.select('tbody > tr', true).elements;
8296         
8297         if(typeof(rows[index]) != 'undefined'){
8298             bt.removeChild(rows[index].dom);
8299         }
8300         
8301 //        if(bt.rows[index]){
8302 //            bt.removeChild(bt.rows[index]);
8303 //        }
8304         
8305         if(isUpdate !== true){
8306             //this.stripeRows(index);
8307             //this.syncRowHeights(index, index);
8308             //this.layout();
8309             this.fireEvent("rowremoved", this, index, record);
8310         }
8311     },
8312     
8313     onAdd : function(ds, records, rowIndex)
8314     {
8315         //Roo.log('on Add called');
8316         // - note this does not handle multiple adding very well..
8317         var bt = this.mainBody.dom;
8318         for (var i =0 ; i < records.length;i++) {
8319             //Roo.log('call insert row Add called on ' + rowIndex + ':' + i);
8320             //Roo.log(records[i]);
8321             //Roo.log(this.store.getAt(rowIndex+i));
8322             this.insertRow(this.store, rowIndex + i, false);
8323             return;
8324         }
8325         
8326     },
8327     
8328     
8329     refreshRow : function(record){
8330         var ds = this.store, index;
8331         if(typeof record == 'number'){
8332             index = record;
8333             record = ds.getAt(index);
8334         }else{
8335             index = ds.indexOf(record);
8336             if (index < 0) {
8337                 return; // should not happen - but seems to 
8338             }
8339         }
8340         this.insertRow(ds, index, true);
8341         this.autoSize();
8342         this.onRemove(ds, record, index+1, true);
8343         this.autoSize();
8344         //this.syncRowHeights(index, index);
8345         //this.layout();
8346         this.fireEvent("rowupdated", this, index, record);
8347     },
8348     
8349     insertRow : function(dm, rowIndex, isUpdate){
8350         
8351         if(!isUpdate){
8352             this.fireEvent("beforerowsinserted", this, rowIndex);
8353         }
8354             //var s = this.getScrollState();
8355         var row = this.renderRow(this.cm, this.store, rowIndex);
8356         // insert before rowIndex..
8357         var e = this.mainBody.createChild(row,this.getRowDom(rowIndex));
8358         
8359         var _this = this;
8360                 
8361         if(row.cellObjects.length){
8362             Roo.each(row.cellObjects, function(r){
8363                 _this.renderCellObject(r);
8364             })
8365         }
8366             
8367         if(!isUpdate){
8368             this.fireEvent("rowsinserted", this, rowIndex);
8369             //this.syncRowHeights(firstRow, lastRow);
8370             //this.stripeRows(firstRow);
8371             //this.layout();
8372         }
8373         
8374     },
8375     
8376     
8377     getRowDom : function(rowIndex)
8378     {
8379         var rows = this.el.select('tbody > tr', true).elements;
8380         
8381         return (typeof(rows[rowIndex]) == 'undefined') ? false : rows[rowIndex];
8382         
8383     },
8384     // returns the object tree for a tr..
8385   
8386     
8387     renderRow : function(cm, ds, rowIndex) 
8388     {
8389         var d = ds.getAt(rowIndex);
8390         
8391         var row = {
8392             tag : 'tr',
8393             cls : 'x-row-' + rowIndex,
8394             cn : []
8395         };
8396             
8397         var cellObjects = [];
8398         
8399         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
8400             var config = cm.config[i];
8401             
8402             var renderer = cm.getRenderer(i);
8403             var value = '';
8404             var id = false;
8405             
8406             if(typeof(renderer) !== 'undefined'){
8407                 value = renderer(d.data[cm.getDataIndex(i)], false, d);
8408             }
8409             // if object are returned, then they are expected to be Roo.bootstrap.Component instances
8410             // and are rendered into the cells after the row is rendered - using the id for the element.
8411             
8412             if(typeof(value) === 'object'){
8413                 id = Roo.id();
8414                 cellObjects.push({
8415                     container : id,
8416                     cfg : value 
8417                 })
8418             }
8419             
8420             var rowcfg = {
8421                 record: d,
8422                 rowIndex : rowIndex,
8423                 colIndex : i,
8424                 rowClass : ''
8425             };
8426
8427             this.fireEvent('rowclass', this, rowcfg);
8428             
8429             var td = {
8430                 tag: 'td',
8431                 cls : rowcfg.rowClass + ' x-col-' + i,
8432                 style: '',
8433                 html: (typeof(value) === 'object') ? '' : value
8434             };
8435             
8436             if (id) {
8437                 td.id = id;
8438             }
8439             
8440             if(typeof(config.colspan) != 'undefined'){
8441                 td.colspan = config.colspan;
8442             }
8443             
8444             if(typeof(config.hidden) != 'undefined' && config.hidden){
8445                 td.style += ' display:none;';
8446             }
8447             
8448             if(typeof(config.align) != 'undefined' && config.align.length){
8449                 td.style += ' text-align:' + config.align + ';';
8450             }
8451             if(typeof(config.valign) != 'undefined' && config.valign.length){
8452                 td.style += ' vertical-align:' + config.valign + ';';
8453             }
8454             
8455             if(typeof(config.width) != 'undefined'){
8456                 td.style += ' width:' +  config.width + 'px;';
8457             }
8458             
8459             if(typeof(config.cursor) != 'undefined'){
8460                 td.style += ' cursor:' +  config.cursor + ';';
8461             }
8462             
8463             if(typeof(config.cls) != 'undefined'){
8464                 td.cls = (typeof(td.cls) == 'undefined') ? config.cls : (td.cls + ' ' + config.cls);
8465             }
8466             
8467             ['xs','sm','md','lg'].map(function(size){
8468                 
8469                 if(typeof(config[size]) == 'undefined'){
8470                     return;
8471                 }
8472                 
8473                 
8474                   
8475                 if (!config[size]) { // 0 = hidden
8476                     // BS 4 '0' is treated as hide that column and below.
8477                     td.cls += ' hidden-' + size + ' hidden' + size + '-down';
8478                     return;
8479                 }
8480                 
8481                 td.cls += ' col-' + size + '-' + config[size] + (
8482                     size == 'xs' ? (' col-' +   config[size] ) : '' // bs4 col-{num} replaces col-xs
8483                 );
8484                  
8485
8486             });
8487             
8488             row.cn.push(td);
8489            
8490         }
8491         
8492         row.cellObjects = cellObjects;
8493         
8494         return row;
8495           
8496     },
8497     
8498     
8499     
8500     onBeforeLoad : function()
8501     {
8502         
8503     },
8504      /**
8505      * Remove all rows
8506      */
8507     clear : function()
8508     {
8509         this.el.select('tbody', true).first().dom.innerHTML = '';
8510     },
8511     /**
8512      * Show or hide a row.
8513      * @param {Number} rowIndex to show or hide
8514      * @param {Boolean} state hide
8515      */
8516     setRowVisibility : function(rowIndex, state)
8517     {
8518         var bt = this.mainBody.dom;
8519         
8520         var rows = this.el.select('tbody > tr', true).elements;
8521         
8522         if(typeof(rows[rowIndex]) == 'undefined'){
8523             return;
8524         }
8525         rows[rowIndex].dom.style.display = state ? '' : 'none';
8526     },
8527     
8528     
8529     getSelectionModel : function(){
8530         if(!this.selModel){
8531             this.selModel = new Roo.bootstrap.Table.RowSelectionModel({grid: this});
8532         }
8533         return this.selModel;
8534     },
8535     /*
8536      * Render the Roo.bootstrap object from renderder
8537      */
8538     renderCellObject : function(r)
8539     {
8540         var _this = this;
8541         
8542         r.cfg.parentId = (typeof(r.container) == 'string') ? r.container : r.container.id;
8543         
8544         var t = r.cfg.render(r.container);
8545         
8546         if(r.cfg.cn){
8547             Roo.each(r.cfg.cn, function(c){
8548                 var child = {
8549                     container: t.getChildContainer(),
8550                     cfg: c
8551                 };
8552                 _this.renderCellObject(child);
8553             })
8554         }
8555     },
8556     
8557     getRowIndex : function(row)
8558     {
8559         var rowIndex = -1;
8560         
8561         Roo.each(this.el.select('tbody > tr', true).elements, function(el, index){
8562             if(el != row){
8563                 return;
8564             }
8565             
8566             rowIndex = index;
8567         });
8568         
8569         return rowIndex;
8570     },
8571      /**
8572      * Returns the grid's underlying element = used by panel.Grid
8573      * @return {Element} The element
8574      */
8575     getGridEl : function(){
8576         return this.el;
8577     },
8578      /**
8579      * Forces a resize - used by panel.Grid
8580      * @return {Element} The element
8581      */
8582     autoSize : function()
8583     {
8584         //var ctr = Roo.get(this.container.dom.parentElement);
8585         var ctr = Roo.get(this.el.dom);
8586         
8587         var thd = this.getGridEl().select('thead',true).first();
8588         var tbd = this.getGridEl().select('tbody', true).first();
8589         var tfd = this.getGridEl().select('tfoot', true).first();
8590         
8591         var cw = ctr.getWidth();
8592         this.getGridEl().select('tfoot tr, tfoot  td',true).setWidth(cw);
8593         
8594         if (tbd) {
8595             
8596             tbd.setWidth(ctr.getWidth());
8597             // if the body has a max height - and then scrolls - we should perhaps set up the height here
8598             // this needs fixing for various usage - currently only hydra job advers I think..
8599             //tdb.setHeight(
8600             //        ctr.getHeight() - ((thd ? thd.getHeight() : 0) + (tfd ? tfd.getHeight() : 0))
8601             //); 
8602             var barsize = (tbd.dom.offsetWidth - tbd.dom.clientWidth);
8603             cw -= barsize;
8604         }
8605         cw = Math.max(cw, this.totalWidth);
8606         this.getGridEl().select('tbody tr',true).setWidth(cw);
8607         
8608         // resize 'expandable coloumn?
8609         
8610         return; // we doe not have a view in this design..
8611         
8612     },
8613     onBodyScroll: function()
8614     {
8615         //Roo.log("body scrolled');" + this.mainBody.dom.scrollLeft);
8616         if(this.mainHead){
8617             this.mainHead.setStyle({
8618                 'position' : 'relative',
8619                 'left': (-1* this.mainBody.dom.scrollLeft) + 'px'
8620             });
8621         }
8622         
8623         if(this.lazyLoad){
8624             
8625             var scrollHeight = this.mainBody.dom.scrollHeight;
8626             
8627             var scrollTop = Math.ceil(this.mainBody.getScroll().top);
8628             
8629             var height = this.mainBody.getHeight();
8630             
8631             if(scrollHeight - height == scrollTop) {
8632                 
8633                 var total = this.ds.getTotalCount();
8634                 
8635                 if(this.footer.cursor + this.footer.pageSize < total){
8636                     
8637                     this.footer.ds.load({
8638                         params : {
8639                             start : this.footer.cursor + this.footer.pageSize,
8640                             limit : this.footer.pageSize
8641                         },
8642                         add : true
8643                     });
8644                 }
8645             }
8646             
8647         }
8648     },
8649     
8650     onHeaderChange : function()
8651     {
8652         var header = this.renderHeader();
8653         var table = this.el.select('table', true).first();
8654         
8655         this.mainHead.remove();
8656         this.mainHead = table.createChild(header, this.mainBody, false);
8657     },
8658     
8659     onHiddenChange : function(colModel, colIndex, hidden)
8660     {
8661         var thSelector = '#' + this.id + ' .x-hcol-' + colIndex;
8662         var tdSelector = '#' + this.id + ' .x-col-' + colIndex;
8663         
8664         this.CSS.updateRule(thSelector, "display", "");
8665         this.CSS.updateRule(tdSelector, "display", "");
8666         
8667         if(hidden){
8668             this.CSS.updateRule(thSelector, "display", "none");
8669             this.CSS.updateRule(tdSelector, "display", "none");
8670         }
8671         
8672         this.onHeaderChange();
8673         this.onLoad();
8674     },
8675     
8676     setColumnWidth: function(col_index, width)
8677     {
8678         // width = "md-2 xs-2..."
8679         if(!this.colModel.config[col_index]) {
8680             return;
8681         }
8682         
8683         var w = width.split(" ");
8684         
8685         var rows = this.el.dom.getElementsByClassName("x-col-"+col_index);
8686         
8687         var h_row = this.el.dom.getElementsByClassName("x-hcol-"+col_index);
8688         
8689         
8690         for(var j = 0; j < w.length; j++) {
8691             
8692             if(!w[j]) {
8693                 continue;
8694             }
8695             
8696             var size_cls = w[j].split("-");
8697             
8698             if(!Number.isInteger(size_cls[1] * 1)) {
8699                 continue;
8700             }
8701             
8702             if(!this.colModel.config[col_index][size_cls[0]]) {
8703                 continue;
8704             }
8705             
8706             if(!h_row[0].classList.contains("col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]])) {
8707                 continue;
8708             }
8709             
8710             h_row[0].classList.replace(
8711                 "col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]],
8712                 "col-"+size_cls[0]+"-"+size_cls[1]
8713             );
8714             
8715             for(var i = 0; i < rows.length; i++) {
8716                 
8717                 var size_cls = w[j].split("-");
8718                 
8719                 if(!Number.isInteger(size_cls[1] * 1)) {
8720                     continue;
8721                 }
8722                 
8723                 if(!this.colModel.config[col_index][size_cls[0]]) {
8724                     continue;
8725                 }
8726                 
8727                 if(!rows[i].classList.contains("col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]])) {
8728                     continue;
8729                 }
8730                 
8731                 rows[i].classList.replace(
8732                     "col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]],
8733                     "col-"+size_cls[0]+"-"+size_cls[1]
8734                 );
8735             }
8736             
8737             this.colModel.config[col_index][size_cls[0]] = size_cls[1];
8738         }
8739     }
8740 });
8741
8742  
8743
8744  /*
8745  * - LGPL
8746  *
8747  * table cell
8748  * 
8749  */
8750
8751 /**
8752  * @class Roo.bootstrap.TableCell
8753  * @extends Roo.bootstrap.Component
8754  * Bootstrap TableCell class
8755  * @cfg {String} html cell contain text
8756  * @cfg {String} cls cell class
8757  * @cfg {String} tag cell tag (td|th) default td
8758  * @cfg {String} abbr Specifies an abbreviated version of the content in a cell
8759  * @cfg {String} align Aligns the content in a cell
8760  * @cfg {String} axis Categorizes cells
8761  * @cfg {String} bgcolor Specifies the background color of a cell
8762  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
8763  * @cfg {Number} colspan Specifies the number of columns a cell should span
8764  * @cfg {String} headers Specifies one or more header cells a cell is related to
8765  * @cfg {Number} height Sets the height of a cell
8766  * @cfg {String} nowrap Specifies that the content inside a cell should not wrap
8767  * @cfg {Number} rowspan Sets the number of rows a cell should span
8768  * @cfg {String} scope Defines a way to associate header cells and data cells in a table
8769  * @cfg {String} valign Vertical aligns the content in a cell
8770  * @cfg {Number} width Specifies the width of a cell
8771  * 
8772  * @constructor
8773  * Create a new TableCell
8774  * @param {Object} config The config object
8775  */
8776
8777 Roo.bootstrap.TableCell = function(config){
8778     Roo.bootstrap.TableCell.superclass.constructor.call(this, config);
8779 };
8780
8781 Roo.extend(Roo.bootstrap.TableCell, Roo.bootstrap.Component,  {
8782     
8783     html: false,
8784     cls: false,
8785     tag: false,
8786     abbr: false,
8787     align: false,
8788     axis: false,
8789     bgcolor: false,
8790     charoff: false,
8791     colspan: false,
8792     headers: false,
8793     height: false,
8794     nowrap: false,
8795     rowspan: false,
8796     scope: false,
8797     valign: false,
8798     width: false,
8799     
8800     
8801     getAutoCreate : function(){
8802         var cfg = Roo.apply({}, Roo.bootstrap.TableCell.superclass.getAutoCreate.call(this));
8803         
8804         cfg = {
8805             tag: 'td'
8806         };
8807         
8808         if(this.tag){
8809             cfg.tag = this.tag;
8810         }
8811         
8812         if (this.html) {
8813             cfg.html=this.html
8814         }
8815         if (this.cls) {
8816             cfg.cls=this.cls
8817         }
8818         if (this.abbr) {
8819             cfg.abbr=this.abbr
8820         }
8821         if (this.align) {
8822             cfg.align=this.align
8823         }
8824         if (this.axis) {
8825             cfg.axis=this.axis
8826         }
8827         if (this.bgcolor) {
8828             cfg.bgcolor=this.bgcolor
8829         }
8830         if (this.charoff) {
8831             cfg.charoff=this.charoff
8832         }
8833         if (this.colspan) {
8834             cfg.colspan=this.colspan
8835         }
8836         if (this.headers) {
8837             cfg.headers=this.headers
8838         }
8839         if (this.height) {
8840             cfg.height=this.height
8841         }
8842         if (this.nowrap) {
8843             cfg.nowrap=this.nowrap
8844         }
8845         if (this.rowspan) {
8846             cfg.rowspan=this.rowspan
8847         }
8848         if (this.scope) {
8849             cfg.scope=this.scope
8850         }
8851         if (this.valign) {
8852             cfg.valign=this.valign
8853         }
8854         if (this.width) {
8855             cfg.width=this.width
8856         }
8857         
8858         
8859         return cfg;
8860     }
8861    
8862 });
8863
8864  
8865
8866  /*
8867  * - LGPL
8868  *
8869  * table row
8870  * 
8871  */
8872
8873 /**
8874  * @class Roo.bootstrap.TableRow
8875  * @extends Roo.bootstrap.Component
8876  * Bootstrap TableRow class
8877  * @cfg {String} cls row class
8878  * @cfg {String} align Aligns the content in a table row
8879  * @cfg {String} bgcolor Specifies a background color for a table row
8880  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
8881  * @cfg {String} valign Vertical aligns the content in a table row
8882  * 
8883  * @constructor
8884  * Create a new TableRow
8885  * @param {Object} config The config object
8886  */
8887
8888 Roo.bootstrap.TableRow = function(config){
8889     Roo.bootstrap.TableRow.superclass.constructor.call(this, config);
8890 };
8891
8892 Roo.extend(Roo.bootstrap.TableRow, Roo.bootstrap.Component,  {
8893     
8894     cls: false,
8895     align: false,
8896     bgcolor: false,
8897     charoff: false,
8898     valign: false,
8899     
8900     getAutoCreate : function(){
8901         var cfg = Roo.apply({}, Roo.bootstrap.TableRow.superclass.getAutoCreate.call(this));
8902         
8903         cfg = {
8904             tag: 'tr'
8905         };
8906             
8907         if(this.cls){
8908             cfg.cls = this.cls;
8909         }
8910         if(this.align){
8911             cfg.align = this.align;
8912         }
8913         if(this.bgcolor){
8914             cfg.bgcolor = this.bgcolor;
8915         }
8916         if(this.charoff){
8917             cfg.charoff = this.charoff;
8918         }
8919         if(this.valign){
8920             cfg.valign = this.valign;
8921         }
8922         
8923         return cfg;
8924     }
8925    
8926 });
8927
8928  
8929
8930  /*
8931  * - LGPL
8932  *
8933  * table body
8934  * 
8935  */
8936
8937 /**
8938  * @class Roo.bootstrap.TableBody
8939  * @extends Roo.bootstrap.Component
8940  * Bootstrap TableBody class
8941  * @cfg {String} cls element class
8942  * @cfg {String} tag element tag (thead|tbody|tfoot) default tbody
8943  * @cfg {String} align Aligns the content inside the element
8944  * @cfg {Number} charoff Sets the number of characters the content inside the element will be aligned from the character specified by the char attribute
8945  * @cfg {String} valign Vertical aligns the content inside the <tbody> element
8946  * 
8947  * @constructor
8948  * Create a new TableBody
8949  * @param {Object} config The config object
8950  */
8951
8952 Roo.bootstrap.TableBody = function(config){
8953     Roo.bootstrap.TableBody.superclass.constructor.call(this, config);
8954 };
8955
8956 Roo.extend(Roo.bootstrap.TableBody, Roo.bootstrap.Component,  {
8957     
8958     cls: false,
8959     tag: false,
8960     align: false,
8961     charoff: false,
8962     valign: false,
8963     
8964     getAutoCreate : function(){
8965         var cfg = Roo.apply({}, Roo.bootstrap.TableBody.superclass.getAutoCreate.call(this));
8966         
8967         cfg = {
8968             tag: 'tbody'
8969         };
8970             
8971         if (this.cls) {
8972             cfg.cls=this.cls
8973         }
8974         if(this.tag){
8975             cfg.tag = this.tag;
8976         }
8977         
8978         if(this.align){
8979             cfg.align = this.align;
8980         }
8981         if(this.charoff){
8982             cfg.charoff = this.charoff;
8983         }
8984         if(this.valign){
8985             cfg.valign = this.valign;
8986         }
8987         
8988         return cfg;
8989     }
8990     
8991     
8992 //    initEvents : function()
8993 //    {
8994 //        
8995 //        if(!this.store){
8996 //            return;
8997 //        }
8998 //        
8999 //        this.store = Roo.factory(this.store, Roo.data);
9000 //        this.store.on('load', this.onLoad, this);
9001 //        
9002 //        this.store.load();
9003 //        
9004 //    },
9005 //    
9006 //    onLoad: function () 
9007 //    {   
9008 //        this.fireEvent('load', this);
9009 //    }
9010 //    
9011 //   
9012 });
9013
9014  
9015
9016  /*
9017  * Based on:
9018  * Ext JS Library 1.1.1
9019  * Copyright(c) 2006-2007, Ext JS, LLC.
9020  *
9021  * Originally Released Under LGPL - original licence link has changed is not relivant.
9022  *
9023  * Fork - LGPL
9024  * <script type="text/javascript">
9025  */
9026
9027 // as we use this in bootstrap.
9028 Roo.namespace('Roo.form');
9029  /**
9030  * @class Roo.form.Action
9031  * Internal Class used to handle form actions
9032  * @constructor
9033  * @param {Roo.form.BasicForm} el The form element or its id
9034  * @param {Object} config Configuration options
9035  */
9036
9037  
9038  
9039 // define the action interface
9040 Roo.form.Action = function(form, options){
9041     this.form = form;
9042     this.options = options || {};
9043 };
9044 /**
9045  * Client Validation Failed
9046  * @const 
9047  */
9048 Roo.form.Action.CLIENT_INVALID = 'client';
9049 /**
9050  * Server Validation Failed
9051  * @const 
9052  */
9053 Roo.form.Action.SERVER_INVALID = 'server';
9054  /**
9055  * Connect to Server Failed
9056  * @const 
9057  */
9058 Roo.form.Action.CONNECT_FAILURE = 'connect';
9059 /**
9060  * Reading Data from Server Failed
9061  * @const 
9062  */
9063 Roo.form.Action.LOAD_FAILURE = 'load';
9064
9065 Roo.form.Action.prototype = {
9066     type : 'default',
9067     failureType : undefined,
9068     response : undefined,
9069     result : undefined,
9070
9071     // interface method
9072     run : function(options){
9073
9074     },
9075
9076     // interface method
9077     success : function(response){
9078
9079     },
9080
9081     // interface method
9082     handleResponse : function(response){
9083
9084     },
9085
9086     // default connection failure
9087     failure : function(response){
9088         
9089         this.response = response;
9090         this.failureType = Roo.form.Action.CONNECT_FAILURE;
9091         this.form.afterAction(this, false);
9092     },
9093
9094     processResponse : function(response){
9095         this.response = response;
9096         if(!response.responseText){
9097             return true;
9098         }
9099         this.result = this.handleResponse(response);
9100         return this.result;
9101     },
9102
9103     // utility functions used internally
9104     getUrl : function(appendParams){
9105         var url = this.options.url || this.form.url || this.form.el.dom.action;
9106         if(appendParams){
9107             var p = this.getParams();
9108             if(p){
9109                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
9110             }
9111         }
9112         return url;
9113     },
9114
9115     getMethod : function(){
9116         return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
9117     },
9118
9119     getParams : function(){
9120         var bp = this.form.baseParams;
9121         var p = this.options.params;
9122         if(p){
9123             if(typeof p == "object"){
9124                 p = Roo.urlEncode(Roo.applyIf(p, bp));
9125             }else if(typeof p == 'string' && bp){
9126                 p += '&' + Roo.urlEncode(bp);
9127             }
9128         }else if(bp){
9129             p = Roo.urlEncode(bp);
9130         }
9131         return p;
9132     },
9133
9134     createCallback : function(){
9135         return {
9136             success: this.success,
9137             failure: this.failure,
9138             scope: this,
9139             timeout: (this.form.timeout*1000),
9140             upload: this.form.fileUpload ? this.success : undefined
9141         };
9142     }
9143 };
9144
9145 Roo.form.Action.Submit = function(form, options){
9146     Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
9147 };
9148
9149 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
9150     type : 'submit',
9151
9152     haveProgress : false,
9153     uploadComplete : false,
9154     
9155     // uploadProgress indicator.
9156     uploadProgress : function()
9157     {
9158         if (!this.form.progressUrl) {
9159             return;
9160         }
9161         
9162         if (!this.haveProgress) {
9163             Roo.MessageBox.progress("Uploading", "Uploading");
9164         }
9165         if (this.uploadComplete) {
9166            Roo.MessageBox.hide();
9167            return;
9168         }
9169         
9170         this.haveProgress = true;
9171    
9172         var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
9173         
9174         var c = new Roo.data.Connection();
9175         c.request({
9176             url : this.form.progressUrl,
9177             params: {
9178                 id : uid
9179             },
9180             method: 'GET',
9181             success : function(req){
9182                //console.log(data);
9183                 var rdata = false;
9184                 var edata;
9185                 try  {
9186                    rdata = Roo.decode(req.responseText)
9187                 } catch (e) {
9188                     Roo.log("Invalid data from server..");
9189                     Roo.log(edata);
9190                     return;
9191                 }
9192                 if (!rdata || !rdata.success) {
9193                     Roo.log(rdata);
9194                     Roo.MessageBox.alert(Roo.encode(rdata));
9195                     return;
9196                 }
9197                 var data = rdata.data;
9198                 
9199                 if (this.uploadComplete) {
9200                    Roo.MessageBox.hide();
9201                    return;
9202                 }
9203                    
9204                 if (data){
9205                     Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
9206                        Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
9207                     );
9208                 }
9209                 this.uploadProgress.defer(2000,this);
9210             },
9211        
9212             failure: function(data) {
9213                 Roo.log('progress url failed ');
9214                 Roo.log(data);
9215             },
9216             scope : this
9217         });
9218            
9219     },
9220     
9221     
9222     run : function()
9223     {
9224         // run get Values on the form, so it syncs any secondary forms.
9225         this.form.getValues();
9226         
9227         var o = this.options;
9228         var method = this.getMethod();
9229         var isPost = method == 'POST';
9230         if(o.clientValidation === false || this.form.isValid()){
9231             
9232             if (this.form.progressUrl) {
9233                 this.form.findField('UPLOAD_IDENTIFIER').setValue(
9234                     (new Date() * 1) + '' + Math.random());
9235                     
9236             } 
9237             
9238             
9239             Roo.Ajax.request(Roo.apply(this.createCallback(), {
9240                 form:this.form.el.dom,
9241                 url:this.getUrl(!isPost),
9242                 method: method,
9243                 params:isPost ? this.getParams() : null,
9244                 isUpload: this.form.fileUpload,
9245                 formData : this.form.formData
9246             }));
9247             
9248             this.uploadProgress();
9249
9250         }else if (o.clientValidation !== false){ // client validation failed
9251             this.failureType = Roo.form.Action.CLIENT_INVALID;
9252             this.form.afterAction(this, false);
9253         }
9254     },
9255
9256     success : function(response)
9257     {
9258         this.uploadComplete= true;
9259         if (this.haveProgress) {
9260             Roo.MessageBox.hide();
9261         }
9262         
9263         
9264         var result = this.processResponse(response);
9265         if(result === true || result.success){
9266             this.form.afterAction(this, true);
9267             return;
9268         }
9269         if(result.errors){
9270             this.form.markInvalid(result.errors);
9271             this.failureType = Roo.form.Action.SERVER_INVALID;
9272         }
9273         this.form.afterAction(this, false);
9274     },
9275     failure : function(response)
9276     {
9277         this.uploadComplete= true;
9278         if (this.haveProgress) {
9279             Roo.MessageBox.hide();
9280         }
9281         
9282         this.response = response;
9283         this.failureType = Roo.form.Action.CONNECT_FAILURE;
9284         this.form.afterAction(this, false);
9285     },
9286     
9287     handleResponse : function(response){
9288         if(this.form.errorReader){
9289             var rs = this.form.errorReader.read(response);
9290             var errors = [];
9291             if(rs.records){
9292                 for(var i = 0, len = rs.records.length; i < len; i++) {
9293                     var r = rs.records[i];
9294                     errors[i] = r.data;
9295                 }
9296             }
9297             if(errors.length < 1){
9298                 errors = null;
9299             }
9300             return {
9301                 success : rs.success,
9302                 errors : errors
9303             };
9304         }
9305         var ret = false;
9306         try {
9307             ret = Roo.decode(response.responseText);
9308         } catch (e) {
9309             ret = {
9310                 success: false,
9311                 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
9312                 errors : []
9313             };
9314         }
9315         return ret;
9316         
9317     }
9318 });
9319
9320
9321 Roo.form.Action.Load = function(form, options){
9322     Roo.form.Action.Load.superclass.constructor.call(this, form, options);
9323     this.reader = this.form.reader;
9324 };
9325
9326 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
9327     type : 'load',
9328
9329     run : function(){
9330         
9331         Roo.Ajax.request(Roo.apply(
9332                 this.createCallback(), {
9333                     method:this.getMethod(),
9334                     url:this.getUrl(false),
9335                     params:this.getParams()
9336         }));
9337     },
9338
9339     success : function(response){
9340         
9341         var result = this.processResponse(response);
9342         if(result === true || !result.success || !result.data){
9343             this.failureType = Roo.form.Action.LOAD_FAILURE;
9344             this.form.afterAction(this, false);
9345             return;
9346         }
9347         this.form.clearInvalid();
9348         this.form.setValues(result.data);
9349         this.form.afterAction(this, true);
9350     },
9351
9352     handleResponse : function(response){
9353         if(this.form.reader){
9354             var rs = this.form.reader.read(response);
9355             var data = rs.records && rs.records[0] ? rs.records[0].data : null;
9356             return {
9357                 success : rs.success,
9358                 data : data
9359             };
9360         }
9361         return Roo.decode(response.responseText);
9362     }
9363 });
9364
9365 Roo.form.Action.ACTION_TYPES = {
9366     'load' : Roo.form.Action.Load,
9367     'submit' : Roo.form.Action.Submit
9368 };/*
9369  * - LGPL
9370  *
9371  * form
9372  *
9373  */
9374
9375 /**
9376  * @class Roo.bootstrap.Form
9377  * @extends Roo.bootstrap.Component
9378  * Bootstrap Form class
9379  * @cfg {String} method  GET | POST (default POST)
9380  * @cfg {String} labelAlign top | left (default top)
9381  * @cfg {String} align left  | right - for navbars
9382  * @cfg {Boolean} loadMask load mask when submit (default true)
9383
9384  *
9385  * @constructor
9386  * Create a new Form
9387  * @param {Object} config The config object
9388  */
9389
9390
9391 Roo.bootstrap.Form = function(config){
9392     
9393     Roo.bootstrap.Form.superclass.constructor.call(this, config);
9394     
9395     Roo.bootstrap.Form.popover.apply();
9396     
9397     this.addEvents({
9398         /**
9399          * @event clientvalidation
9400          * If the monitorValid config option is true, this event fires repetitively to notify of valid state
9401          * @param {Form} this
9402          * @param {Boolean} valid true if the form has passed client-side validation
9403          */
9404         clientvalidation: true,
9405         /**
9406          * @event beforeaction
9407          * Fires before any action is performed. Return false to cancel the action.
9408          * @param {Form} this
9409          * @param {Action} action The action to be performed
9410          */
9411         beforeaction: true,
9412         /**
9413          * @event actionfailed
9414          * Fires when an action fails.
9415          * @param {Form} this
9416          * @param {Action} action The action that failed
9417          */
9418         actionfailed : true,
9419         /**
9420          * @event actioncomplete
9421          * Fires when an action is completed.
9422          * @param {Form} this
9423          * @param {Action} action The action that completed
9424          */
9425         actioncomplete : true
9426     });
9427 };
9428
9429 Roo.extend(Roo.bootstrap.Form, Roo.bootstrap.Component,  {
9430
9431      /**
9432      * @cfg {String} method
9433      * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
9434      */
9435     method : 'POST',
9436     /**
9437      * @cfg {String} url
9438      * The URL to use for form actions if one isn't supplied in the action options.
9439      */
9440     /**
9441      * @cfg {Boolean} fileUpload
9442      * Set to true if this form is a file upload.
9443      */
9444
9445     /**
9446      * @cfg {Object} baseParams
9447      * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
9448      */
9449
9450     /**
9451      * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
9452      */
9453     timeout: 30,
9454     /**
9455      * @cfg {Sting} align (left|right) for navbar forms
9456      */
9457     align : 'left',
9458
9459     // private
9460     activeAction : null,
9461
9462     /**
9463      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
9464      * element by passing it or its id or mask the form itself by passing in true.
9465      * @type Mixed
9466      */
9467     waitMsgTarget : false,
9468
9469     loadMask : true,
9470     
9471     /**
9472      * @cfg {Boolean} errorMask (true|false) default false
9473      */
9474     errorMask : false,
9475     
9476     /**
9477      * @cfg {Number} maskOffset Default 100
9478      */
9479     maskOffset : 100,
9480     
9481     /**
9482      * @cfg {Boolean} maskBody
9483      */
9484     maskBody : false,
9485
9486     getAutoCreate : function(){
9487
9488         var cfg = {
9489             tag: 'form',
9490             method : this.method || 'POST',
9491             id : this.id || Roo.id(),
9492             cls : ''
9493         };
9494         if (this.parent().xtype.match(/^Nav/)) {
9495             cfg.cls = 'navbar-form form-inline navbar-' + this.align;
9496
9497         }
9498
9499         if (this.labelAlign == 'left' ) {
9500             cfg.cls += ' form-horizontal';
9501         }
9502
9503
9504         return cfg;
9505     },
9506     initEvents : function()
9507     {
9508         this.el.on('submit', this.onSubmit, this);
9509         // this was added as random key presses on the form where triggering form submit.
9510         this.el.on('keypress', function(e) {
9511             if (e.getCharCode() != 13) {
9512                 return true;
9513             }
9514             // we might need to allow it for textareas.. and some other items.
9515             // check e.getTarget().
9516
9517             if(e.getTarget().nodeName.toLowerCase() === 'textarea'){
9518                 return true;
9519             }
9520
9521             Roo.log("keypress blocked");
9522
9523             e.preventDefault();
9524             return false;
9525         });
9526         
9527     },
9528     // private
9529     onSubmit : function(e){
9530         e.stopEvent();
9531     },
9532
9533      /**
9534      * Returns true if client-side validation on the form is successful.
9535      * @return Boolean
9536      */
9537     isValid : function(){
9538         var items = this.getItems();
9539         var valid = true;
9540         var target = false;
9541         
9542         items.each(function(f){
9543             
9544             if(f.validate()){
9545                 return;
9546             }
9547             
9548             Roo.log('invalid field: ' + f.name);
9549             
9550             valid = false;
9551
9552             if(!target && f.el.isVisible(true)){
9553                 target = f;
9554             }
9555            
9556         });
9557         
9558         if(this.errorMask && !valid){
9559             Roo.bootstrap.Form.popover.mask(this, target);
9560         }
9561         
9562         return valid;
9563     },
9564     
9565     /**
9566      * Returns true if any fields in this form have changed since their original load.
9567      * @return Boolean
9568      */
9569     isDirty : function(){
9570         var dirty = false;
9571         var items = this.getItems();
9572         items.each(function(f){
9573            if(f.isDirty()){
9574                dirty = true;
9575                return false;
9576            }
9577            return true;
9578         });
9579         return dirty;
9580     },
9581      /**
9582      * Performs a predefined action (submit or load) or custom actions you define on this form.
9583      * @param {String} actionName The name of the action type
9584      * @param {Object} options (optional) The options to pass to the action.  All of the config options listed
9585      * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
9586      * accept other config options):
9587      * <pre>
9588 Property          Type             Description
9589 ----------------  ---------------  ----------------------------------------------------------------------------------
9590 url               String           The url for the action (defaults to the form's url)
9591 method            String           The form method to use (defaults to the form's method, or POST if not defined)
9592 params            String/Object    The params to pass (defaults to the form's baseParams, or none if not defined)
9593 clientValidation  Boolean          Applies to submit only.  Pass true to call form.isValid() prior to posting to
9594                                    validate the form on the client (defaults to false)
9595      * </pre>
9596      * @return {BasicForm} this
9597      */
9598     doAction : function(action, options){
9599         if(typeof action == 'string'){
9600             action = new Roo.form.Action.ACTION_TYPES[action](this, options);
9601         }
9602         if(this.fireEvent('beforeaction', this, action) !== false){
9603             this.beforeAction(action);
9604             action.run.defer(100, action);
9605         }
9606         return this;
9607     },
9608
9609     // private
9610     beforeAction : function(action){
9611         var o = action.options;
9612         
9613         if(this.loadMask){
9614             
9615             if(this.maskBody){
9616                 Roo.get(document.body).mask(o.waitMsg || "Sending", 'x-mask-loading')
9617             } else {
9618                 this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
9619             }
9620         }
9621         // not really supported yet.. ??
9622
9623         //if(this.waitMsgTarget === true){
9624         //  this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
9625         //}else if(this.waitMsgTarget){
9626         //    this.waitMsgTarget = Roo.get(this.waitMsgTarget);
9627         //    this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
9628         //}else {
9629         //    Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
9630        // }
9631
9632     },
9633
9634     // private
9635     afterAction : function(action, success){
9636         this.activeAction = null;
9637         var o = action.options;
9638
9639         if(this.loadMask){
9640             
9641             if(this.maskBody){
9642                 Roo.get(document.body).unmask();
9643             } else {
9644                 this.el.unmask();
9645             }
9646         }
9647         
9648         //if(this.waitMsgTarget === true){
9649 //            this.el.unmask();
9650         //}else if(this.waitMsgTarget){
9651         //    this.waitMsgTarget.unmask();
9652         //}else{
9653         //    Roo.MessageBox.updateProgress(1);
9654         //    Roo.MessageBox.hide();
9655        // }
9656         //
9657         if(success){
9658             if(o.reset){
9659                 this.reset();
9660             }
9661             Roo.callback(o.success, o.scope, [this, action]);
9662             this.fireEvent('actioncomplete', this, action);
9663
9664         }else{
9665
9666             // failure condition..
9667             // we have a scenario where updates need confirming.
9668             // eg. if a locking scenario exists..
9669             // we look for { errors : { needs_confirm : true }} in the response.
9670             if (
9671                 (typeof(action.result) != 'undefined')  &&
9672                 (typeof(action.result.errors) != 'undefined')  &&
9673                 (typeof(action.result.errors.needs_confirm) != 'undefined')
9674            ){
9675                 var _t = this;
9676                 Roo.log("not supported yet");
9677                  /*
9678
9679                 Roo.MessageBox.confirm(
9680                     "Change requires confirmation",
9681                     action.result.errorMsg,
9682                     function(r) {
9683                         if (r != 'yes') {
9684                             return;
9685                         }
9686                         _t.doAction('submit', { params :  { _submit_confirmed : 1 } }  );
9687                     }
9688
9689                 );
9690                 */
9691
9692
9693                 return;
9694             }
9695
9696             Roo.callback(o.failure, o.scope, [this, action]);
9697             // show an error message if no failed handler is set..
9698             if (!this.hasListener('actionfailed')) {
9699                 Roo.log("need to add dialog support");
9700                 /*
9701                 Roo.MessageBox.alert("Error",
9702                     (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
9703                         action.result.errorMsg :
9704                         "Saving Failed, please check your entries or try again"
9705                 );
9706                 */
9707             }
9708
9709             this.fireEvent('actionfailed', this, action);
9710         }
9711
9712     },
9713     /**
9714      * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
9715      * @param {String} id The value to search for
9716      * @return Field
9717      */
9718     findField : function(id){
9719         var items = this.getItems();
9720         var field = items.get(id);
9721         if(!field){
9722              items.each(function(f){
9723                 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
9724                     field = f;
9725                     return false;
9726                 }
9727                 return true;
9728             });
9729         }
9730         return field || null;
9731     },
9732      /**
9733      * Mark fields in this form invalid in bulk.
9734      * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
9735      * @return {BasicForm} this
9736      */
9737     markInvalid : function(errors){
9738         if(errors instanceof Array){
9739             for(var i = 0, len = errors.length; i < len; i++){
9740                 var fieldError = errors[i];
9741                 var f = this.findField(fieldError.id);
9742                 if(f){
9743                     f.markInvalid(fieldError.msg);
9744                 }
9745             }
9746         }else{
9747             var field, id;
9748             for(id in errors){
9749                 if(typeof errors[id] != 'function' && (field = this.findField(id))){
9750                     field.markInvalid(errors[id]);
9751                 }
9752             }
9753         }
9754         //Roo.each(this.childForms || [], function (f) {
9755         //    f.markInvalid(errors);
9756         //});
9757
9758         return this;
9759     },
9760
9761     /**
9762      * Set values for fields in this form in bulk.
9763      * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
9764      * @return {BasicForm} this
9765      */
9766     setValues : function(values){
9767         if(values instanceof Array){ // array of objects
9768             for(var i = 0, len = values.length; i < len; i++){
9769                 var v = values[i];
9770                 var f = this.findField(v.id);
9771                 if(f){
9772                     f.setValue(v.value);
9773                     if(this.trackResetOnLoad){
9774                         f.originalValue = f.getValue();
9775                     }
9776                 }
9777             }
9778         }else{ // object hash
9779             var field, id;
9780             for(id in values){
9781                 if(typeof values[id] != 'function' && (field = this.findField(id))){
9782
9783                     if (field.setFromData &&
9784                         field.valueField &&
9785                         field.displayField &&
9786                         // combos' with local stores can
9787                         // be queried via setValue()
9788                         // to set their value..
9789                         (field.store && !field.store.isLocal)
9790                         ) {
9791                         // it's a combo
9792                         var sd = { };
9793                         sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
9794                         sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
9795                         field.setFromData(sd);
9796
9797                     } else if(field.setFromData && (field.store && !field.store.isLocal)) {
9798                         
9799                         field.setFromData(values);
9800                         
9801                     } else {
9802                         field.setValue(values[id]);
9803                     }
9804
9805
9806                     if(this.trackResetOnLoad){
9807                         field.originalValue = field.getValue();
9808                     }
9809                 }
9810             }
9811         }
9812
9813         //Roo.each(this.childForms || [], function (f) {
9814         //    f.setValues(values);
9815         //});
9816
9817         return this;
9818     },
9819
9820     /**
9821      * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
9822      * they are returned as an array.
9823      * @param {Boolean} asString
9824      * @return {Object}
9825      */
9826     getValues : function(asString){
9827         //if (this.childForms) {
9828             // copy values from the child forms
9829         //    Roo.each(this.childForms, function (f) {
9830         //        this.setValues(f.getValues());
9831         //    }, this);
9832         //}
9833
9834
9835
9836         var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
9837         if(asString === true){
9838             return fs;
9839         }
9840         return Roo.urlDecode(fs);
9841     },
9842
9843     /**
9844      * Returns the fields in this form as an object with key/value pairs.
9845      * This differs from getValues as it calls getValue on each child item, rather than using dom data.
9846      * @return {Object}
9847      */
9848     getFieldValues : function(with_hidden)
9849     {
9850         var items = this.getItems();
9851         var ret = {};
9852         items.each(function(f){
9853             
9854             if (!f.getName()) {
9855                 return;
9856             }
9857             
9858             var v = f.getValue();
9859             
9860             if (f.inputType =='radio') {
9861                 if (typeof(ret[f.getName()]) == 'undefined') {
9862                     ret[f.getName()] = ''; // empty..
9863                 }
9864
9865                 if (!f.el.dom.checked) {
9866                     return;
9867
9868                 }
9869                 v = f.el.dom.value;
9870
9871             }
9872             
9873             if(f.xtype == 'MoneyField'){
9874                 ret[f.currencyName] = f.getCurrency();
9875             }
9876
9877             // not sure if this supported any more..
9878             if ((typeof(v) == 'object') && f.getRawValue) {
9879                 v = f.getRawValue() ; // dates..
9880             }
9881             // combo boxes where name != hiddenName...
9882             if (f.name !== false && f.name != '' && f.name != f.getName()) {
9883                 ret[f.name] = f.getRawValue();
9884             }
9885             ret[f.getName()] = v;
9886         });
9887
9888         return ret;
9889     },
9890
9891     /**
9892      * Clears all invalid messages in this form.
9893      * @return {BasicForm} this
9894      */
9895     clearInvalid : function(){
9896         var items = this.getItems();
9897
9898         items.each(function(f){
9899            f.clearInvalid();
9900         });
9901
9902         return this;
9903     },
9904
9905     /**
9906      * Resets this form.
9907      * @return {BasicForm} this
9908      */
9909     reset : function(){
9910         var items = this.getItems();
9911         items.each(function(f){
9912             f.reset();
9913         });
9914
9915         Roo.each(this.childForms || [], function (f) {
9916             f.reset();
9917         });
9918
9919
9920         return this;
9921     },
9922     
9923     getItems : function()
9924     {
9925         var r=new Roo.util.MixedCollection(false, function(o){
9926             return o.id || (o.id = Roo.id());
9927         });
9928         var iter = function(el) {
9929             if (el.inputEl) {
9930                 r.add(el);
9931             }
9932             if (!el.items) {
9933                 return;
9934             }
9935             Roo.each(el.items,function(e) {
9936                 iter(e);
9937             });
9938         };
9939
9940         iter(this);
9941         return r;
9942     },
9943     
9944     hideFields : function(items)
9945     {
9946         Roo.each(items, function(i){
9947             
9948             var f = this.findField(i);
9949             
9950             if(!f){
9951                 return;
9952             }
9953             
9954             f.hide();
9955             
9956         }, this);
9957     },
9958     
9959     showFields : function(items)
9960     {
9961         Roo.each(items, function(i){
9962             
9963             var f = this.findField(i);
9964             
9965             if(!f){
9966                 return;
9967             }
9968             
9969             f.show();
9970             
9971         }, this);
9972     }
9973
9974 });
9975
9976 Roo.apply(Roo.bootstrap.Form, {
9977     
9978     popover : {
9979         
9980         padding : 5,
9981         
9982         isApplied : false,
9983         
9984         isMasked : false,
9985         
9986         form : false,
9987         
9988         target : false,
9989         
9990         toolTip : false,
9991         
9992         intervalID : false,
9993         
9994         maskEl : false,
9995         
9996         apply : function()
9997         {
9998             if(this.isApplied){
9999                 return;
10000             }
10001             
10002             this.maskEl = {
10003                 top : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-top-mask" }, true),
10004                 left : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-left-mask" }, true),
10005                 bottom : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-bottom-mask" }, true),
10006                 right : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-right-mask" }, true)
10007             };
10008             
10009             this.maskEl.top.enableDisplayMode("block");
10010             this.maskEl.left.enableDisplayMode("block");
10011             this.maskEl.bottom.enableDisplayMode("block");
10012             this.maskEl.right.enableDisplayMode("block");
10013             
10014             this.toolTip = new Roo.bootstrap.Tooltip({
10015                 cls : 'roo-form-error-popover',
10016                 alignment : {
10017                     'left' : ['r-l', [-2,0], 'right'],
10018                     'right' : ['l-r', [2,0], 'left'],
10019                     'bottom' : ['tl-bl', [0,2], 'top'],
10020                     'top' : [ 'bl-tl', [0,-2], 'bottom']
10021                 }
10022             });
10023             
10024             this.toolTip.render(Roo.get(document.body));
10025
10026             this.toolTip.el.enableDisplayMode("block");
10027             
10028             Roo.get(document.body).on('click', function(){
10029                 this.unmask();
10030             }, this);
10031             
10032             Roo.get(document.body).on('touchstart', function(){
10033                 this.unmask();
10034             }, this);
10035             
10036             this.isApplied = true
10037         },
10038         
10039         mask : function(form, target)
10040         {
10041             this.form = form;
10042             
10043             this.target = target;
10044             
10045             if(!this.form.errorMask || !target.el){
10046                 return;
10047             }
10048             
10049             var scrollable = this.target.el.findScrollableParent() || this.target.el.findParent('div.modal', 100, true) || Roo.get(document.body);
10050             
10051             Roo.log(scrollable);
10052             
10053             var ot = this.target.el.calcOffsetsTo(scrollable);
10054             
10055             var scrollTo = ot[1] - this.form.maskOffset;
10056             
10057             scrollTo = Math.min(scrollTo, scrollable.dom.scrollHeight);
10058             
10059             scrollable.scrollTo('top', scrollTo);
10060             
10061             var box = this.target.el.getBox();
10062             Roo.log(box);
10063             var zIndex = Roo.bootstrap.Modal.zIndex++;
10064
10065             
10066             this.maskEl.top.setStyle('position', 'absolute');
10067             this.maskEl.top.setStyle('z-index', zIndex);
10068             this.maskEl.top.setSize(Roo.lib.Dom.getDocumentWidth(), box.y - this.padding);
10069             this.maskEl.top.setLeft(0);
10070             this.maskEl.top.setTop(0);
10071             this.maskEl.top.show();
10072             
10073             this.maskEl.left.setStyle('position', 'absolute');
10074             this.maskEl.left.setStyle('z-index', zIndex);
10075             this.maskEl.left.setSize(box.x - this.padding, box.height + this.padding * 2);
10076             this.maskEl.left.setLeft(0);
10077             this.maskEl.left.setTop(box.y - this.padding);
10078             this.maskEl.left.show();
10079
10080             this.maskEl.bottom.setStyle('position', 'absolute');
10081             this.maskEl.bottom.setStyle('z-index', zIndex);
10082             this.maskEl.bottom.setSize(Roo.lib.Dom.getDocumentWidth(), Roo.lib.Dom.getDocumentHeight() - box.bottom - this.padding);
10083             this.maskEl.bottom.setLeft(0);
10084             this.maskEl.bottom.setTop(box.bottom + this.padding);
10085             this.maskEl.bottom.show();
10086
10087             this.maskEl.right.setStyle('position', 'absolute');
10088             this.maskEl.right.setStyle('z-index', zIndex);
10089             this.maskEl.right.setSize(Roo.lib.Dom.getDocumentWidth() - box.right - this.padding, box.height + this.padding * 2);
10090             this.maskEl.right.setLeft(box.right + this.padding);
10091             this.maskEl.right.setTop(box.y - this.padding);
10092             this.maskEl.right.show();
10093
10094             this.toolTip.bindEl = this.target.el;
10095
10096             this.toolTip.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
10097
10098             var tip = this.target.blankText;
10099
10100             if(this.target.getValue() !== '' ) {
10101                 
10102                 if (this.target.invalidText.length) {
10103                     tip = this.target.invalidText;
10104                 } else if (this.target.regexText.length){
10105                     tip = this.target.regexText;
10106                 }
10107             }
10108
10109             this.toolTip.show(tip);
10110
10111             this.intervalID = window.setInterval(function() {
10112                 Roo.bootstrap.Form.popover.unmask();
10113             }, 10000);
10114
10115             window.onwheel = function(){ return false;};
10116             
10117             (function(){ this.isMasked = true; }).defer(500, this);
10118             
10119         },
10120         
10121         unmask : function()
10122         {
10123             if(!this.isApplied || !this.isMasked || !this.form || !this.target || !this.form.errorMask){
10124                 return;
10125             }
10126             
10127             this.maskEl.top.setStyle('position', 'absolute');
10128             this.maskEl.top.setSize(0, 0).setXY([0, 0]);
10129             this.maskEl.top.hide();
10130
10131             this.maskEl.left.setStyle('position', 'absolute');
10132             this.maskEl.left.setSize(0, 0).setXY([0, 0]);
10133             this.maskEl.left.hide();
10134
10135             this.maskEl.bottom.setStyle('position', 'absolute');
10136             this.maskEl.bottom.setSize(0, 0).setXY([0, 0]);
10137             this.maskEl.bottom.hide();
10138
10139             this.maskEl.right.setStyle('position', 'absolute');
10140             this.maskEl.right.setSize(0, 0).setXY([0, 0]);
10141             this.maskEl.right.hide();
10142             
10143             this.toolTip.hide();
10144             
10145             this.toolTip.el.hide();
10146             
10147             window.onwheel = function(){ return true;};
10148             
10149             if(this.intervalID){
10150                 window.clearInterval(this.intervalID);
10151                 this.intervalID = false;
10152             }
10153             
10154             this.isMasked = false;
10155             
10156         }
10157         
10158     }
10159     
10160 });
10161
10162 /*
10163  * Based on:
10164  * Ext JS Library 1.1.1
10165  * Copyright(c) 2006-2007, Ext JS, LLC.
10166  *
10167  * Originally Released Under LGPL - original licence link has changed is not relivant.
10168  *
10169  * Fork - LGPL
10170  * <script type="text/javascript">
10171  */
10172 /**
10173  * @class Roo.form.VTypes
10174  * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
10175  * @singleton
10176  */
10177 Roo.form.VTypes = function(){
10178     // closure these in so they are only created once.
10179     var alpha = /^[a-zA-Z_]+$/;
10180     var alphanum = /^[a-zA-Z0-9_]+$/;
10181     var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,24}$/;
10182     var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
10183
10184     // All these messages and functions are configurable
10185     return {
10186         /**
10187          * The function used to validate email addresses
10188          * @param {String} value The email address
10189          */
10190         'email' : function(v){
10191             return email.test(v);
10192         },
10193         /**
10194          * The error text to display when the email validation function returns false
10195          * @type String
10196          */
10197         'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
10198         /**
10199          * The keystroke filter mask to be applied on email input
10200          * @type RegExp
10201          */
10202         'emailMask' : /[a-z0-9_\.\-@]/i,
10203
10204         /**
10205          * The function used to validate URLs
10206          * @param {String} value The URL
10207          */
10208         'url' : function(v){
10209             return url.test(v);
10210         },
10211         /**
10212          * The error text to display when the url validation function returns false
10213          * @type String
10214          */
10215         'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
10216         
10217         /**
10218          * The function used to validate alpha values
10219          * @param {String} value The value
10220          */
10221         'alpha' : function(v){
10222             return alpha.test(v);
10223         },
10224         /**
10225          * The error text to display when the alpha validation function returns false
10226          * @type String
10227          */
10228         'alphaText' : 'This field should only contain letters and _',
10229         /**
10230          * The keystroke filter mask to be applied on alpha input
10231          * @type RegExp
10232          */
10233         'alphaMask' : /[a-z_]/i,
10234
10235         /**
10236          * The function used to validate alphanumeric values
10237          * @param {String} value The value
10238          */
10239         'alphanum' : function(v){
10240             return alphanum.test(v);
10241         },
10242         /**
10243          * The error text to display when the alphanumeric validation function returns false
10244          * @type String
10245          */
10246         'alphanumText' : 'This field should only contain letters, numbers and _',
10247         /**
10248          * The keystroke filter mask to be applied on alphanumeric input
10249          * @type RegExp
10250          */
10251         'alphanumMask' : /[a-z0-9_]/i
10252     };
10253 }();/*
10254  * - LGPL
10255  *
10256  * Input
10257  * 
10258  */
10259
10260 /**
10261  * @class Roo.bootstrap.Input
10262  * @extends Roo.bootstrap.Component
10263  * Bootstrap Input class
10264  * @cfg {Boolean} disabled is it disabled
10265  * @cfg {String} (button|checkbox|email|file|hidden|image|number|password|radio|range|reset|search|submit|text) inputType 
10266  * @cfg {String} name name of the input
10267  * @cfg {string} fieldLabel - the label associated
10268  * @cfg {string} placeholder - placeholder to put in text.
10269  * @cfg {string}  before - input group add on before
10270  * @cfg {string} after - input group add on after
10271  * @cfg {string} size - (lg|sm) or leave empty..
10272  * @cfg {Number} xs colspan out of 12 for mobile-sized screens
10273  * @cfg {Number} sm colspan out of 12 for tablet-sized screens
10274  * @cfg {Number} md colspan out of 12 for computer-sized screens
10275  * @cfg {Number} lg colspan out of 12 for large computer-sized screens
10276  * @cfg {string} value default value of the input
10277  * @cfg {Number} labelWidth set the width of label 
10278  * @cfg {Number} labellg set the width of label (1-12)
10279  * @cfg {Number} labelmd set the width of label (1-12)
10280  * @cfg {Number} labelsm set the width of label (1-12)
10281  * @cfg {Number} labelxs set the width of label (1-12)
10282  * @cfg {String} labelAlign (top|left)
10283  * @cfg {Boolean} readOnly Specifies that the field should be read-only
10284  * @cfg {String} autocomplete - default is new-password see: https://developers.google.com/web/fundamentals/input/form/label-and-name-inputs?hl=en
10285  * @cfg {String} indicatorpos (left|right) default left
10286  * @cfg {String} capture (user|camera) use for file input only. (default empty)
10287  * @cfg {String} accept (image|video|audio) use for file input only. (default empty)
10288  * @cfg {Boolean} preventMark Do not show tick or cross if error/success
10289
10290  * @cfg {String} align (left|center|right) Default left
10291  * @cfg {Boolean} forceFeedback (true|false) Default false
10292  * 
10293  * @constructor
10294  * Create a new Input
10295  * @param {Object} config The config object
10296  */
10297
10298 Roo.bootstrap.Input = function(config){
10299     
10300     Roo.bootstrap.Input.superclass.constructor.call(this, config);
10301     
10302     this.addEvents({
10303         /**
10304          * @event focus
10305          * Fires when this field receives input focus.
10306          * @param {Roo.form.Field} this
10307          */
10308         focus : true,
10309         /**
10310          * @event blur
10311          * Fires when this field loses input focus.
10312          * @param {Roo.form.Field} this
10313          */
10314         blur : true,
10315         /**
10316          * @event specialkey
10317          * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
10318          * {@link Roo.EventObject#getKey} to determine which key was pressed.
10319          * @param {Roo.form.Field} this
10320          * @param {Roo.EventObject} e The event object
10321          */
10322         specialkey : true,
10323         /**
10324          * @event change
10325          * Fires just before the field blurs if the field value has changed.
10326          * @param {Roo.form.Field} this
10327          * @param {Mixed} newValue The new value
10328          * @param {Mixed} oldValue The original value
10329          */
10330         change : true,
10331         /**
10332          * @event invalid
10333          * Fires after the field has been marked as invalid.
10334          * @param {Roo.form.Field} this
10335          * @param {String} msg The validation message
10336          */
10337         invalid : true,
10338         /**
10339          * @event valid
10340          * Fires after the field has been validated with no errors.
10341          * @param {Roo.form.Field} this
10342          */
10343         valid : true,
10344          /**
10345          * @event keyup
10346          * Fires after the key up
10347          * @param {Roo.form.Field} this
10348          * @param {Roo.EventObject}  e The event Object
10349          */
10350         keyup : true
10351     });
10352 };
10353
10354 Roo.extend(Roo.bootstrap.Input, Roo.bootstrap.Component,  {
10355      /**
10356      * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
10357       automatic validation (defaults to "keyup").
10358      */
10359     validationEvent : "keyup",
10360      /**
10361      * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
10362      */
10363     validateOnBlur : true,
10364     /**
10365      * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
10366      */
10367     validationDelay : 250,
10368      /**
10369      * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
10370      */
10371     focusClass : "x-form-focus",  // not needed???
10372     
10373        
10374     /**
10375      * @cfg {String} invalidClass DEPRICATED - code uses BS4 - is-valid / is-invalid
10376      */
10377     invalidClass : "has-warning",
10378     
10379     /**
10380      * @cfg {String} validClass DEPRICATED - code uses BS4 - is-valid / is-invalid
10381      */
10382     validClass : "has-success",
10383     
10384     /**
10385      * @cfg {Boolean} hasFeedback (true|false) default true
10386      */
10387     hasFeedback : true,
10388     
10389     /**
10390      * @cfg {String} invalidFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
10391      */
10392     invalidFeedbackClass : "glyphicon-warning-sign",
10393     
10394     /**
10395      * @cfg {String} validFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
10396      */
10397     validFeedbackClass : "glyphicon-ok",
10398     
10399     /**
10400      * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
10401      */
10402     selectOnFocus : false,
10403     
10404      /**
10405      * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
10406      */
10407     maskRe : null,
10408        /**
10409      * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
10410      */
10411     vtype : null,
10412     
10413       /**
10414      * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
10415      */
10416     disableKeyFilter : false,
10417     
10418        /**
10419      * @cfg {Boolean} disabled True to disable the field (defaults to false).
10420      */
10421     disabled : false,
10422      /**
10423      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
10424      */
10425     allowBlank : true,
10426     /**
10427      * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
10428      */
10429     blankText : "Please complete this mandatory field",
10430     
10431      /**
10432      * @cfg {Number} minLength Minimum input field length required (defaults to 0)
10433      */
10434     minLength : 0,
10435     /**
10436      * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
10437      */
10438     maxLength : Number.MAX_VALUE,
10439     /**
10440      * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
10441      */
10442     minLengthText : "The minimum length for this field is {0}",
10443     /**
10444      * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
10445      */
10446     maxLengthText : "The maximum length for this field is {0}",
10447   
10448     
10449     /**
10450      * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
10451      * If available, this function will be called only after the basic validators all return true, and will be passed the
10452      * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
10453      */
10454     validator : null,
10455     /**
10456      * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
10457      * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
10458      * current field value.  If the test fails, the field will be marked invalid using {@link #regexText}.
10459      */
10460     regex : null,
10461     /**
10462      * @cfg {String} regexText -- Depricated - use Invalid Text
10463      */
10464     regexText : "",
10465     
10466     /**
10467      * @cfg {String} invalidText The error text to display if {@link #validator} test fails during validation (defaults to "")
10468      */
10469     invalidText : "",
10470     
10471     
10472     
10473     autocomplete: false,
10474     
10475     
10476     fieldLabel : '',
10477     inputType : 'text',
10478     
10479     name : false,
10480     placeholder: false,
10481     before : false,
10482     after : false,
10483     size : false,
10484     hasFocus : false,
10485     preventMark: false,
10486     isFormField : true,
10487     value : '',
10488     labelWidth : 2,
10489     labelAlign : false,
10490     readOnly : false,
10491     align : false,
10492     formatedValue : false,
10493     forceFeedback : false,
10494     
10495     indicatorpos : 'left',
10496     
10497     labellg : 0,
10498     labelmd : 0,
10499     labelsm : 0,
10500     labelxs : 0,
10501     
10502     capture : '',
10503     accept : '',
10504     
10505     parentLabelAlign : function()
10506     {
10507         var parent = this;
10508         while (parent.parent()) {
10509             parent = parent.parent();
10510             if (typeof(parent.labelAlign) !='undefined') {
10511                 return parent.labelAlign;
10512             }
10513         }
10514         return 'left';
10515         
10516     },
10517     
10518     getAutoCreate : function()
10519     {
10520         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
10521         
10522         var id = Roo.id();
10523         
10524         var cfg = {};
10525         
10526         if(this.inputType != 'hidden'){
10527             cfg.cls = 'form-group' //input-group
10528         }
10529         
10530         var input =  {
10531             tag: 'input',
10532             id : id,
10533             type : this.inputType,
10534             value : this.value,
10535             cls : 'form-control',
10536             placeholder : this.placeholder || '',
10537             autocomplete : this.autocomplete || 'new-password'
10538         };
10539         if (this.inputType == 'file') {
10540             input.style = 'overflow:hidden'; // why not in CSS?
10541         }
10542         
10543         if(this.capture.length){
10544             input.capture = this.capture;
10545         }
10546         
10547         if(this.accept.length){
10548             input.accept = this.accept + "/*";
10549         }
10550         
10551         if(this.align){
10552             input.style = (typeof(input.style) == 'undefined') ? ('text-align:' + this.align) : (input.style + 'text-align:' + this.align);
10553         }
10554         
10555         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
10556             input.maxLength = this.maxLength;
10557         }
10558         
10559         if (this.disabled) {
10560             input.disabled=true;
10561         }
10562         
10563         if (this.readOnly) {
10564             input.readonly=true;
10565         }
10566         
10567         if (this.name) {
10568             input.name = this.name;
10569         }
10570         
10571         if (this.size) {
10572             input.cls += ' input-' + this.size;
10573         }
10574         
10575         var settings=this;
10576         ['xs','sm','md','lg'].map(function(size){
10577             if (settings[size]) {
10578                 cfg.cls += ' col-' + size + '-' + settings[size];
10579             }
10580         });
10581         
10582         var inputblock = input;
10583         
10584         var feedback = {
10585             tag: 'span',
10586             cls: 'glyphicon form-control-feedback'
10587         };
10588             
10589         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
10590             
10591             inputblock = {
10592                 cls : 'has-feedback',
10593                 cn :  [
10594                     input,
10595                     feedback
10596                 ] 
10597             };  
10598         }
10599         
10600         if (this.before || this.after) {
10601             
10602             inputblock = {
10603                 cls : 'input-group',
10604                 cn :  [] 
10605             };
10606             
10607             if (this.before && typeof(this.before) == 'string') {
10608                 
10609                 inputblock.cn.push({
10610                     tag :'span',
10611                     cls : 'roo-input-before input-group-addon input-group-prepend input-group-text',
10612                     html : this.before
10613                 });
10614             }
10615             if (this.before && typeof(this.before) == 'object') {
10616                 this.before = Roo.factory(this.before);
10617                 
10618                 inputblock.cn.push({
10619                     tag :'span',
10620                     cls : 'roo-input-before input-group-prepend   input-group-' +
10621                         (this.before.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
10622                 });
10623             }
10624             
10625             inputblock.cn.push(input);
10626             
10627             if (this.after && typeof(this.after) == 'string') {
10628                 inputblock.cn.push({
10629                     tag :'span',
10630                     cls : 'roo-input-after input-group-append input-group-text input-group-addon',
10631                     html : this.after
10632                 });
10633             }
10634             if (this.after && typeof(this.after) == 'object') {
10635                 this.after = Roo.factory(this.after);
10636                 
10637                 inputblock.cn.push({
10638                     tag :'span',
10639                     cls : 'roo-input-after input-group-append  input-group-' +
10640                         (this.after.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
10641                 });
10642             }
10643             
10644             if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
10645                 inputblock.cls += ' has-feedback';
10646                 inputblock.cn.push(feedback);
10647             }
10648         };
10649         var indicator = {
10650             tag : 'i',
10651             cls : 'roo-required-indicator ' + (this.indicatorpos == 'right'  ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
10652             tooltip : 'This field is required'
10653         };
10654         if (this.allowBlank ) {
10655             indicator.style = this.allowBlank ? ' display:none' : '';
10656         }
10657         if (align ==='left' && this.fieldLabel.length) {
10658             
10659             cfg.cls += ' roo-form-group-label-left'  + (Roo.bootstrap.version == 4 ? ' row' : '');
10660             
10661             cfg.cn = [
10662                 indicator,
10663                 {
10664                     tag: 'label',
10665                     'for' :  id,
10666                     cls : 'control-label col-form-label',
10667                     html : this.fieldLabel
10668
10669                 },
10670                 {
10671                     cls : "", 
10672                     cn: [
10673                         inputblock
10674                     ]
10675                 }
10676             ];
10677             
10678             var labelCfg = cfg.cn[1];
10679             var contentCfg = cfg.cn[2];
10680             
10681             if(this.indicatorpos == 'right'){
10682                 cfg.cn = [
10683                     {
10684                         tag: 'label',
10685                         'for' :  id,
10686                         cls : 'control-label col-form-label',
10687                         cn : [
10688                             {
10689                                 tag : 'span',
10690                                 html : this.fieldLabel
10691                             },
10692                             indicator
10693                         ]
10694                     },
10695                     {
10696                         cls : "",
10697                         cn: [
10698                             inputblock
10699                         ]
10700                     }
10701
10702                 ];
10703                 
10704                 labelCfg = cfg.cn[0];
10705                 contentCfg = cfg.cn[1];
10706             
10707             }
10708             
10709             if(this.labelWidth > 12){
10710                 labelCfg.style = "width: " + this.labelWidth + 'px';
10711             }
10712             
10713             if(this.labelWidth < 13 && this.labelmd == 0){
10714                 this.labelmd = this.labelWidth;
10715             }
10716             
10717             if(this.labellg > 0){
10718                 labelCfg.cls += ' col-lg-' + this.labellg;
10719                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
10720             }
10721             
10722             if(this.labelmd > 0){
10723                 labelCfg.cls += ' col-md-' + this.labelmd;
10724                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
10725             }
10726             
10727             if(this.labelsm > 0){
10728                 labelCfg.cls += ' col-sm-' + this.labelsm;
10729                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
10730             }
10731             
10732             if(this.labelxs > 0){
10733                 labelCfg.cls += ' col-xs-' + this.labelxs;
10734                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
10735             }
10736             
10737             
10738         } else if ( this.fieldLabel.length) {
10739                 
10740             
10741             
10742             cfg.cn = [
10743                 {
10744                     tag : 'i',
10745                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
10746                     tooltip : 'This field is required',
10747                     style : this.allowBlank ? ' display:none' : '' 
10748                 },
10749                 {
10750                     tag: 'label',
10751                    //cls : 'input-group-addon',
10752                     html : this.fieldLabel
10753
10754                 },
10755
10756                inputblock
10757
10758            ];
10759            
10760            if(this.indicatorpos == 'right'){
10761        
10762                 cfg.cn = [
10763                     {
10764                         tag: 'label',
10765                        //cls : 'input-group-addon',
10766                         html : this.fieldLabel
10767
10768                     },
10769                     {
10770                         tag : 'i',
10771                         cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
10772                         tooltip : 'This field is required',
10773                         style : this.allowBlank ? ' display:none' : '' 
10774                     },
10775
10776                    inputblock
10777
10778                ];
10779
10780             }
10781
10782         } else {
10783             
10784             cfg.cn = [
10785
10786                     inputblock
10787
10788             ];
10789                 
10790                 
10791         };
10792         
10793         if (this.parentType === 'Navbar' &&  this.parent().bar) {
10794            cfg.cls += ' navbar-form';
10795         }
10796         
10797         if (this.parentType === 'NavGroup' && !(Roo.bootstrap.version == 4 && this.parent().form)) {
10798             // on BS4 we do this only if not form 
10799             cfg.cls += ' navbar-form';
10800             cfg.tag = 'li';
10801         }
10802         
10803         return cfg;
10804         
10805     },
10806     /**
10807      * return the real input element.
10808      */
10809     inputEl: function ()
10810     {
10811         return this.el.select('input.form-control',true).first();
10812     },
10813     
10814     tooltipEl : function()
10815     {
10816         return this.inputEl();
10817     },
10818     
10819     indicatorEl : function()
10820     {
10821         if (Roo.bootstrap.version == 4) {
10822             return false; // not enabled in v4 yet.
10823         }
10824         
10825         var indicator = this.el.select('i.roo-required-indicator',true).first();
10826         
10827         if(!indicator){
10828             return false;
10829         }
10830         
10831         return indicator;
10832         
10833     },
10834     
10835     setDisabled : function(v)
10836     {
10837         var i  = this.inputEl().dom;
10838         if (!v) {
10839             i.removeAttribute('disabled');
10840             return;
10841             
10842         }
10843         i.setAttribute('disabled','true');
10844     },
10845     initEvents : function()
10846     {
10847           
10848         this.inputEl().on("keydown" , this.fireKey,  this);
10849         this.inputEl().on("focus", this.onFocus,  this);
10850         this.inputEl().on("blur", this.onBlur,  this);
10851         
10852         this.inputEl().relayEvent('keyup', this);
10853         
10854         this.indicator = this.indicatorEl();
10855         
10856         if(this.indicator){
10857             this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible'); // changed from invisible??? - 
10858         }
10859  
10860         // reference to original value for reset
10861         this.originalValue = this.getValue();
10862         //Roo.form.TextField.superclass.initEvents.call(this);
10863         if(this.validationEvent == 'keyup'){
10864             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
10865             this.inputEl().on('keyup', this.filterValidation, this);
10866         }
10867         else if(this.validationEvent !== false){
10868             this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
10869         }
10870         
10871         if(this.selectOnFocus){
10872             this.on("focus", this.preFocus, this);
10873             
10874         }
10875         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
10876             this.inputEl().on("keypress", this.filterKeys, this);
10877         } else {
10878             this.inputEl().relayEvent('keypress', this);
10879         }
10880        /* if(this.grow){
10881             this.el.on("keyup", this.onKeyUp,  this, {buffer:50});
10882             this.el.on("click", this.autoSize,  this);
10883         }
10884         */
10885         if(this.inputEl().is('input[type=password]') && Roo.isSafari){
10886             this.inputEl().on('keydown', this.SafariOnKeyDown, this);
10887         }
10888         
10889         if (typeof(this.before) == 'object') {
10890             this.before.render(this.el.select('.roo-input-before',true).first());
10891         }
10892         if (typeof(this.after) == 'object') {
10893             this.after.render(this.el.select('.roo-input-after',true).first());
10894         }
10895         
10896         this.inputEl().on('change', this.onChange, this);
10897         
10898     },
10899     filterValidation : function(e){
10900         if(!e.isNavKeyPress()){
10901             this.validationTask.delay(this.validationDelay);
10902         }
10903     },
10904      /**
10905      * Validates the field value
10906      * @return {Boolean} True if the value is valid, else false
10907      */
10908     validate : function(){
10909         //if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
10910         if(this.disabled || this.validateValue(this.getRawValue())){
10911             this.markValid();
10912             return true;
10913         }
10914         
10915         this.markInvalid();
10916         return false;
10917     },
10918     
10919     
10920     /**
10921      * Validates a value according to the field's validation rules and marks the field as invalid
10922      * if the validation fails
10923      * @param {Mixed} value The value to validate
10924      * @return {Boolean} True if the value is valid, else false
10925      */
10926     validateValue : function(value)
10927     {
10928         if(this.getVisibilityEl().hasClass('hidden')){
10929             return true;
10930         }
10931         
10932         if(value.length < 1)  { // if it's blank
10933             if(this.allowBlank){
10934                 return true;
10935             }
10936             return false;
10937         }
10938         
10939         if(value.length < this.minLength){
10940             return false;
10941         }
10942         if(value.length > this.maxLength){
10943             return false;
10944         }
10945         if(this.vtype){
10946             var vt = Roo.form.VTypes;
10947             if(!vt[this.vtype](value, this)){
10948                 return false;
10949             }
10950         }
10951         if(typeof this.validator == "function"){
10952             var msg = this.validator(value);
10953             if(msg !== true){
10954                 return false;
10955             }
10956             if (typeof(msg) == 'string') {
10957                 this.invalidText = msg;
10958             }
10959         }
10960         
10961         if(this.regex && !this.regex.test(value)){
10962             return false;
10963         }
10964         
10965         return true;
10966     },
10967     
10968      // private
10969     fireKey : function(e){
10970         //Roo.log('field ' + e.getKey());
10971         if(e.isNavKeyPress()){
10972             this.fireEvent("specialkey", this, e);
10973         }
10974     },
10975     focus : function (selectText){
10976         if(this.rendered){
10977             this.inputEl().focus();
10978             if(selectText === true){
10979                 this.inputEl().dom.select();
10980             }
10981         }
10982         return this;
10983     } ,
10984     
10985     onFocus : function(){
10986         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
10987            // this.el.addClass(this.focusClass);
10988         }
10989         if(!this.hasFocus){
10990             this.hasFocus = true;
10991             this.startValue = this.getValue();
10992             this.fireEvent("focus", this);
10993         }
10994     },
10995     
10996     beforeBlur : Roo.emptyFn,
10997
10998     
10999     // private
11000     onBlur : function(){
11001         this.beforeBlur();
11002         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
11003             //this.el.removeClass(this.focusClass);
11004         }
11005         this.hasFocus = false;
11006         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
11007             this.validate();
11008         }
11009         var v = this.getValue();
11010         if(String(v) !== String(this.startValue)){
11011             this.fireEvent('change', this, v, this.startValue);
11012         }
11013         this.fireEvent("blur", this);
11014     },
11015     
11016     onChange : function(e)
11017     {
11018         var v = this.getValue();
11019         if(String(v) !== String(this.startValue)){
11020             this.fireEvent('change', this, v, this.startValue);
11021         }
11022         
11023     },
11024     
11025     /**
11026      * Resets the current field value to the originally loaded value and clears any validation messages
11027      */
11028     reset : function(){
11029         this.setValue(this.originalValue);
11030         this.validate();
11031     },
11032      /**
11033      * Returns the name of the field
11034      * @return {Mixed} name The name field
11035      */
11036     getName: function(){
11037         return this.name;
11038     },
11039      /**
11040      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
11041      * @return {Mixed} value The field value
11042      */
11043     getValue : function(){
11044         
11045         var v = this.inputEl().getValue();
11046         
11047         return v;
11048     },
11049     /**
11050      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
11051      * @return {Mixed} value The field value
11052      */
11053     getRawValue : function(){
11054         var v = this.inputEl().getValue();
11055         
11056         return v;
11057     },
11058     
11059     /**
11060      * Sets the underlying DOM field's value directly, bypassing validation.  To set the value with validation see {@link #setValue}.
11061      * @param {Mixed} value The value to set
11062      */
11063     setRawValue : function(v){
11064         return this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
11065     },
11066     
11067     selectText : function(start, end){
11068         var v = this.getRawValue();
11069         if(v.length > 0){
11070             start = start === undefined ? 0 : start;
11071             end = end === undefined ? v.length : end;
11072             var d = this.inputEl().dom;
11073             if(d.setSelectionRange){
11074                 d.setSelectionRange(start, end);
11075             }else if(d.createTextRange){
11076                 var range = d.createTextRange();
11077                 range.moveStart("character", start);
11078                 range.moveEnd("character", v.length-end);
11079                 range.select();
11080             }
11081         }
11082     },
11083     
11084     /**
11085      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
11086      * @param {Mixed} value The value to set
11087      */
11088     setValue : function(v){
11089         this.value = v;
11090         if(this.rendered){
11091             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
11092             this.validate();
11093         }
11094     },
11095     
11096     /*
11097     processValue : function(value){
11098         if(this.stripCharsRe){
11099             var newValue = value.replace(this.stripCharsRe, '');
11100             if(newValue !== value){
11101                 this.setRawValue(newValue);
11102                 return newValue;
11103             }
11104         }
11105         return value;
11106     },
11107   */
11108     preFocus : function(){
11109         
11110         if(this.selectOnFocus){
11111             this.inputEl().dom.select();
11112         }
11113     },
11114     filterKeys : function(e){
11115         var k = e.getKey();
11116         if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
11117             return;
11118         }
11119         var c = e.getCharCode(), cc = String.fromCharCode(c);
11120         if(Roo.isIE && (e.isSpecialKey() || !cc)){
11121             return;
11122         }
11123         if(!this.maskRe.test(cc)){
11124             e.stopEvent();
11125         }
11126     },
11127      /**
11128      * Clear any invalid styles/messages for this field
11129      */
11130     clearInvalid : function(){
11131         
11132         if(!this.el || this.preventMark){ // not rendered
11133             return;
11134         }
11135         
11136         
11137         this.el.removeClass([this.invalidClass, 'is-invalid']);
11138         
11139         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
11140             
11141             var feedback = this.el.select('.form-control-feedback', true).first();
11142             
11143             if(feedback){
11144                 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
11145             }
11146             
11147         }
11148         
11149         if(this.indicator){
11150             this.indicator.removeClass('visible');
11151             this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
11152         }
11153         
11154         this.fireEvent('valid', this);
11155     },
11156     
11157      /**
11158      * Mark this field as valid
11159      */
11160     markValid : function()
11161     {
11162         if(!this.el  || this.preventMark){ // not rendered...
11163             return;
11164         }
11165         
11166         this.el.removeClass([this.invalidClass, this.validClass]);
11167         this.inputEl().removeClass(['is-valid', 'is-invalid']);
11168
11169         var feedback = this.el.select('.form-control-feedback', true).first();
11170             
11171         if(feedback){
11172             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
11173         }
11174         
11175         if(this.indicator){
11176             this.indicator.removeClass('visible');
11177             this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
11178         }
11179         
11180         if(this.disabled){
11181             return;
11182         }
11183         
11184            
11185         if(this.allowBlank && !this.getRawValue().length){
11186             return;
11187         }
11188         if (Roo.bootstrap.version == 3) {
11189             this.el.addClass(this.validClass);
11190         } else {
11191             this.inputEl().addClass('is-valid');
11192         }
11193
11194         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
11195             
11196             var feedback = this.el.select('.form-control-feedback', true).first();
11197             
11198             if(feedback){
11199                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
11200                 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
11201             }
11202             
11203         }
11204         
11205         this.fireEvent('valid', this);
11206     },
11207     
11208      /**
11209      * Mark this field as invalid
11210      * @param {String} msg The validation message
11211      */
11212     markInvalid : function(msg)
11213     {
11214         if(!this.el  || this.preventMark){ // not rendered
11215             return;
11216         }
11217         
11218         this.el.removeClass([this.invalidClass, this.validClass]);
11219         this.inputEl().removeClass(['is-valid', 'is-invalid']);
11220         
11221         var feedback = this.el.select('.form-control-feedback', true).first();
11222             
11223         if(feedback){
11224             this.el.select('.form-control-feedback', true).first().removeClass(
11225                     [this.invalidFeedbackClass, this.validFeedbackClass]);
11226         }
11227
11228         if(this.disabled){
11229             return;
11230         }
11231         
11232         if(this.allowBlank && !this.getRawValue().length){
11233             return;
11234         }
11235         
11236         if(this.indicator){
11237             this.indicator.removeClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
11238             this.indicator.addClass('visible');
11239         }
11240         if (Roo.bootstrap.version == 3) {
11241             this.el.addClass(this.invalidClass);
11242         } else {
11243             this.inputEl().addClass('is-invalid');
11244         }
11245         
11246         
11247         
11248         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
11249             
11250             var feedback = this.el.select('.form-control-feedback', true).first();
11251             
11252             if(feedback){
11253                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
11254                 
11255                 if(this.getValue().length || this.forceFeedback){
11256                     this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
11257                 }
11258                 
11259             }
11260             
11261         }
11262         
11263         this.fireEvent('invalid', this, msg);
11264     },
11265     // private
11266     SafariOnKeyDown : function(event)
11267     {
11268         // this is a workaround for a password hang bug on chrome/ webkit.
11269         if (this.inputEl().dom.type != 'password') {
11270             return;
11271         }
11272         
11273         var isSelectAll = false;
11274         
11275         if(this.inputEl().dom.selectionEnd > 0){
11276             isSelectAll = (this.inputEl().dom.selectionEnd - this.inputEl().dom.selectionStart - this.getValue().length == 0) ? true : false;
11277         }
11278         if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
11279             event.preventDefault();
11280             this.setValue('');
11281             return;
11282         }
11283         
11284         if(isSelectAll  && event.getCharCode() > 31 && !event.ctrlKey) { // not backspace and delete key (or ctrl-v)
11285             
11286             event.preventDefault();
11287             // this is very hacky as keydown always get's upper case.
11288             //
11289             var cc = String.fromCharCode(event.getCharCode());
11290             this.setValue( event.shiftKey ?  cc : cc.toLowerCase());
11291             
11292         }
11293     },
11294     adjustWidth : function(tag, w){
11295         tag = tag.toLowerCase();
11296         if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
11297             if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
11298                 if(tag == 'input'){
11299                     return w + 2;
11300                 }
11301                 if(tag == 'textarea'){
11302                     return w-2;
11303                 }
11304             }else if(Roo.isOpera){
11305                 if(tag == 'input'){
11306                     return w + 2;
11307                 }
11308                 if(tag == 'textarea'){
11309                     return w-2;
11310                 }
11311             }
11312         }
11313         return w;
11314     },
11315     
11316     setFieldLabel : function(v)
11317     {
11318         if(!this.rendered){
11319             return;
11320         }
11321         
11322         if(this.indicatorEl()){
11323             var ar = this.el.select('label > span',true);
11324             
11325             if (ar.elements.length) {
11326                 this.el.select('label > span',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
11327                 this.fieldLabel = v;
11328                 return;
11329             }
11330             
11331             var br = this.el.select('label',true);
11332             
11333             if(br.elements.length) {
11334                 this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
11335                 this.fieldLabel = v;
11336                 return;
11337             }
11338             
11339             Roo.log('Cannot Found any of label > span || label in input');
11340             return;
11341         }
11342         
11343         this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
11344         this.fieldLabel = v;
11345         
11346         
11347     }
11348 });
11349
11350  
11351 /*
11352  * - LGPL
11353  *
11354  * Input
11355  * 
11356  */
11357
11358 /**
11359  * @class Roo.bootstrap.TextArea
11360  * @extends Roo.bootstrap.Input
11361  * Bootstrap TextArea class
11362  * @cfg {Number} cols Specifies the visible width of a text area
11363  * @cfg {Number} rows Specifies the visible number of lines in a text area
11364  * @cfg {string} wrap (soft|hard)Specifies how the text in a text area is to be wrapped when submitted in a form
11365  * @cfg {string} resize (none|both|horizontal|vertical|inherit|initial)
11366  * @cfg {string} html text
11367  * 
11368  * @constructor
11369  * Create a new TextArea
11370  * @param {Object} config The config object
11371  */
11372
11373 Roo.bootstrap.TextArea = function(config){
11374     Roo.bootstrap.TextArea.superclass.constructor.call(this, config);
11375    
11376 };
11377
11378 Roo.extend(Roo.bootstrap.TextArea, Roo.bootstrap.Input,  {
11379      
11380     cols : false,
11381     rows : 5,
11382     readOnly : false,
11383     warp : 'soft',
11384     resize : false,
11385     value: false,
11386     html: false,
11387     
11388     getAutoCreate : function(){
11389         
11390         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
11391         
11392         var id = Roo.id();
11393         
11394         var cfg = {};
11395         
11396         if(this.inputType != 'hidden'){
11397             cfg.cls = 'form-group' //input-group
11398         }
11399         
11400         var input =  {
11401             tag: 'textarea',
11402             id : id,
11403             warp : this.warp,
11404             rows : this.rows,
11405             value : this.value || '',
11406             html: this.html || '',
11407             cls : 'form-control',
11408             placeholder : this.placeholder || '' 
11409             
11410         };
11411         
11412         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
11413             input.maxLength = this.maxLength;
11414         }
11415         
11416         if(this.resize){
11417             input.style = (typeof(input.style) == 'undefined') ? 'resize:' + this.resize : input.style + 'resize:' + this.resize;
11418         }
11419         
11420         if(this.cols){
11421             input.cols = this.cols;
11422         }
11423         
11424         if (this.readOnly) {
11425             input.readonly = true;
11426         }
11427         
11428         if (this.name) {
11429             input.name = this.name;
11430         }
11431         
11432         if (this.size) {
11433             input.cls = (typeof(input.cls) == 'undefined') ? 'input-' + this.size : input.cls + ' input-' + this.size;
11434         }
11435         
11436         var settings=this;
11437         ['xs','sm','md','lg'].map(function(size){
11438             if (settings[size]) {
11439                 cfg.cls += ' col-' + size + '-' + settings[size];
11440             }
11441         });
11442         
11443         var inputblock = input;
11444         
11445         if(this.hasFeedback && !this.allowBlank){
11446             
11447             var feedback = {
11448                 tag: 'span',
11449                 cls: 'glyphicon form-control-feedback'
11450             };
11451
11452             inputblock = {
11453                 cls : 'has-feedback',
11454                 cn :  [
11455                     input,
11456                     feedback
11457                 ] 
11458             };  
11459         }
11460         
11461         
11462         if (this.before || this.after) {
11463             
11464             inputblock = {
11465                 cls : 'input-group',
11466                 cn :  [] 
11467             };
11468             if (this.before) {
11469                 inputblock.cn.push({
11470                     tag :'span',
11471                     cls : 'input-group-addon',
11472                     html : this.before
11473                 });
11474             }
11475             
11476             inputblock.cn.push(input);
11477             
11478             if(this.hasFeedback && !this.allowBlank){
11479                 inputblock.cls += ' has-feedback';
11480                 inputblock.cn.push(feedback);
11481             }
11482             
11483             if (this.after) {
11484                 inputblock.cn.push({
11485                     tag :'span',
11486                     cls : 'input-group-addon',
11487                     html : this.after
11488                 });
11489             }
11490             
11491         }
11492         
11493         if (align ==='left' && this.fieldLabel.length) {
11494             cfg.cn = [
11495                 {
11496                     tag: 'label',
11497                     'for' :  id,
11498                     cls : 'control-label',
11499                     html : this.fieldLabel
11500                 },
11501                 {
11502                     cls : "",
11503                     cn: [
11504                         inputblock
11505                     ]
11506                 }
11507
11508             ];
11509             
11510             if(this.labelWidth > 12){
11511                 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
11512             }
11513
11514             if(this.labelWidth < 13 && this.labelmd == 0){
11515                 this.labelmd = this.labelWidth;
11516             }
11517
11518             if(this.labellg > 0){
11519                 cfg.cn[0].cls += ' col-lg-' + this.labellg;
11520                 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
11521             }
11522
11523             if(this.labelmd > 0){
11524                 cfg.cn[0].cls += ' col-md-' + this.labelmd;
11525                 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
11526             }
11527
11528             if(this.labelsm > 0){
11529                 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
11530                 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
11531             }
11532
11533             if(this.labelxs > 0){
11534                 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
11535                 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
11536             }
11537             
11538         } else if ( this.fieldLabel.length) {
11539             cfg.cn = [
11540
11541                {
11542                    tag: 'label',
11543                    //cls : 'input-group-addon',
11544                    html : this.fieldLabel
11545
11546                },
11547
11548                inputblock
11549
11550            ];
11551
11552         } else {
11553
11554             cfg.cn = [
11555
11556                 inputblock
11557
11558             ];
11559                 
11560         }
11561         
11562         if (this.disabled) {
11563             input.disabled=true;
11564         }
11565         
11566         return cfg;
11567         
11568     },
11569     /**
11570      * return the real textarea element.
11571      */
11572     inputEl: function ()
11573     {
11574         return this.el.select('textarea.form-control',true).first();
11575     },
11576     
11577     /**
11578      * Clear any invalid styles/messages for this field
11579      */
11580     clearInvalid : function()
11581     {
11582         
11583         if(!this.el || this.preventMark){ // not rendered
11584             return;
11585         }
11586         
11587         var label = this.el.select('label', true).first();
11588         var icon = this.el.select('i.fa-star', true).first();
11589         
11590         if(label && icon){
11591             icon.remove();
11592         }
11593         this.el.removeClass( this.validClass);
11594         this.inputEl().removeClass('is-invalid');
11595          
11596         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
11597             
11598             var feedback = this.el.select('.form-control-feedback', true).first();
11599             
11600             if(feedback){
11601                 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
11602             }
11603             
11604         }
11605         
11606         this.fireEvent('valid', this);
11607     },
11608     
11609      /**
11610      * Mark this field as valid
11611      */
11612     markValid : function()
11613     {
11614         if(!this.el  || this.preventMark){ // not rendered
11615             return;
11616         }
11617         
11618         this.el.removeClass([this.invalidClass, this.validClass]);
11619         this.inputEl().removeClass(['is-valid', 'is-invalid']);
11620         
11621         var feedback = this.el.select('.form-control-feedback', true).first();
11622             
11623         if(feedback){
11624             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
11625         }
11626
11627         if(this.disabled || this.allowBlank){
11628             return;
11629         }
11630         
11631         var label = this.el.select('label', true).first();
11632         var icon = this.el.select('i.fa-star', true).first();
11633         
11634         if(label && icon){
11635             icon.remove();
11636         }
11637         if (Roo.bootstrap.version == 3) {
11638             this.el.addClass(this.validClass);
11639         } else {
11640             this.inputEl().addClass('is-valid');
11641         }
11642         
11643         
11644         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
11645             
11646             var feedback = this.el.select('.form-control-feedback', true).first();
11647             
11648             if(feedback){
11649                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
11650                 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
11651             }
11652             
11653         }
11654         
11655         this.fireEvent('valid', this);
11656     },
11657     
11658      /**
11659      * Mark this field as invalid
11660      * @param {String} msg The validation message
11661      */
11662     markInvalid : function(msg)
11663     {
11664         if(!this.el  || this.preventMark){ // not rendered
11665             return;
11666         }
11667         
11668         this.el.removeClass([this.invalidClass, this.validClass]);
11669         this.inputEl().removeClass(['is-valid', 'is-invalid']);
11670         
11671         var feedback = this.el.select('.form-control-feedback', true).first();
11672             
11673         if(feedback){
11674             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
11675         }
11676
11677         if(this.disabled || this.allowBlank){
11678             return;
11679         }
11680         
11681         var label = this.el.select('label', true).first();
11682         var icon = this.el.select('i.fa-star', true).first();
11683         
11684         if(!this.getValue().length && label && !icon){
11685             this.el.createChild({
11686                 tag : 'i',
11687                 cls : 'text-danger fa fa-lg fa-star',
11688                 tooltip : 'This field is required',
11689                 style : 'margin-right:5px;'
11690             }, label, true);
11691         }
11692         
11693         if (Roo.bootstrap.version == 3) {
11694             this.el.addClass(this.invalidClass);
11695         } else {
11696             this.inputEl().addClass('is-invalid');
11697         }
11698         
11699         // fixme ... this may be depricated need to test..
11700         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
11701             
11702             var feedback = this.el.select('.form-control-feedback', true).first();
11703             
11704             if(feedback){
11705                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
11706                 
11707                 if(this.getValue().length || this.forceFeedback){
11708                     this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
11709                 }
11710                 
11711             }
11712             
11713         }
11714         
11715         this.fireEvent('invalid', this, msg);
11716     }
11717 });
11718
11719  
11720 /*
11721  * - LGPL
11722  *
11723  * trigger field - base class for combo..
11724  * 
11725  */
11726  
11727 /**
11728  * @class Roo.bootstrap.TriggerField
11729  * @extends Roo.bootstrap.Input
11730  * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
11731  * The trigger has no default action, so you must assign a function to implement the trigger click handler by
11732  * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
11733  * for which you can provide a custom implementation.  For example:
11734  * <pre><code>
11735 var trigger = new Roo.bootstrap.TriggerField();
11736 trigger.onTriggerClick = myTriggerFn;
11737 trigger.applyTo('my-field');
11738 </code></pre>
11739  *
11740  * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
11741  * {@link Roo.bootstrap.DateField} and {@link Roo.bootstrap.ComboBox} are perfect examples of this.
11742  * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
11743  * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
11744  * @cfg {String} caret (search|calendar) BS3 only - carat fa name
11745
11746  * @constructor
11747  * Create a new TriggerField.
11748  * @param {Object} config Configuration options (valid {@Roo.bootstrap.Input} config options will also be applied
11749  * to the base TextField)
11750  */
11751 Roo.bootstrap.TriggerField = function(config){
11752     this.mimicing = false;
11753     Roo.bootstrap.TriggerField.superclass.constructor.call(this, config);
11754 };
11755
11756 Roo.extend(Roo.bootstrap.TriggerField, Roo.bootstrap.Input,  {
11757     /**
11758      * @cfg {String} triggerClass A CSS class to apply to the trigger
11759      */
11760      /**
11761      * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
11762      */
11763     hideTrigger:false,
11764
11765     /**
11766      * @cfg {Boolean} removable (true|false) special filter default false
11767      */
11768     removable : false,
11769     
11770     /** @cfg {Boolean} grow @hide */
11771     /** @cfg {Number} growMin @hide */
11772     /** @cfg {Number} growMax @hide */
11773
11774     /**
11775      * @hide 
11776      * @method
11777      */
11778     autoSize: Roo.emptyFn,
11779     // private
11780     monitorTab : true,
11781     // private
11782     deferHeight : true,
11783
11784     
11785     actionMode : 'wrap',
11786     
11787     caret : false,
11788     
11789     
11790     getAutoCreate : function(){
11791        
11792         var align = this.labelAlign || this.parentLabelAlign();
11793         
11794         var id = Roo.id();
11795         
11796         var cfg = {
11797             cls: 'form-group' //input-group
11798         };
11799         
11800         
11801         var input =  {
11802             tag: 'input',
11803             id : id,
11804             type : this.inputType,
11805             cls : 'form-control',
11806             autocomplete: 'new-password',
11807             placeholder : this.placeholder || '' 
11808             
11809         };
11810         if (this.name) {
11811             input.name = this.name;
11812         }
11813         if (this.size) {
11814             input.cls += ' input-' + this.size;
11815         }
11816         
11817         if (this.disabled) {
11818             input.disabled=true;
11819         }
11820         
11821         var inputblock = input;
11822         
11823         if(this.hasFeedback && !this.allowBlank){
11824             
11825             var feedback = {
11826                 tag: 'span',
11827                 cls: 'glyphicon form-control-feedback'
11828             };
11829             
11830             if(this.removable && !this.editable  ){
11831                 inputblock = {
11832                     cls : 'has-feedback',
11833                     cn :  [
11834                         inputblock,
11835                         {
11836                             tag: 'button',
11837                             html : 'x',
11838                             cls : 'roo-combo-removable-btn close'
11839                         },
11840                         feedback
11841                     ] 
11842                 };
11843             } else {
11844                 inputblock = {
11845                     cls : 'has-feedback',
11846                     cn :  [
11847                         inputblock,
11848                         feedback
11849                     ] 
11850                 };
11851             }
11852
11853         } else {
11854             if(this.removable && !this.editable ){
11855                 inputblock = {
11856                     cls : 'roo-removable',
11857                     cn :  [
11858                         inputblock,
11859                         {
11860                             tag: 'button',
11861                             html : 'x',
11862                             cls : 'roo-combo-removable-btn close'
11863                         }
11864                     ] 
11865                 };
11866             }
11867         }
11868         
11869         if (this.before || this.after) {
11870             
11871             inputblock = {
11872                 cls : 'input-group',
11873                 cn :  [] 
11874             };
11875             if (this.before) {
11876                 inputblock.cn.push({
11877                     tag :'span',
11878                     cls : 'input-group-addon input-group-prepend input-group-text',
11879                     html : this.before
11880                 });
11881             }
11882             
11883             inputblock.cn.push(input);
11884             
11885             if(this.hasFeedback && !this.allowBlank){
11886                 inputblock.cls += ' has-feedback';
11887                 inputblock.cn.push(feedback);
11888             }
11889             
11890             if (this.after) {
11891                 inputblock.cn.push({
11892                     tag :'span',
11893                     cls : 'input-group-addon input-group-append input-group-text',
11894                     html : this.after
11895                 });
11896             }
11897             
11898         };
11899         
11900       
11901         
11902         var ibwrap = inputblock;
11903         
11904         if(this.multiple){
11905             ibwrap = {
11906                 tag: 'ul',
11907                 cls: 'roo-select2-choices',
11908                 cn:[
11909                     {
11910                         tag: 'li',
11911                         cls: 'roo-select2-search-field',
11912                         cn: [
11913
11914                             inputblock
11915                         ]
11916                     }
11917                 ]
11918             };
11919                 
11920         }
11921         
11922         var combobox = {
11923             cls: 'roo-select2-container input-group',
11924             cn: [
11925                  {
11926                     tag: 'input',
11927                     type : 'hidden',
11928                     cls: 'form-hidden-field'
11929                 },
11930                 ibwrap
11931             ]
11932         };
11933         
11934         if(!this.multiple && this.showToggleBtn){
11935             
11936             var caret = {
11937                         tag: 'span',
11938                         cls: 'caret'
11939              };
11940             if (this.caret != false) {
11941                 caret = {
11942                      tag: 'i',
11943                      cls: 'fa fa-' + this.caret
11944                 };
11945                 
11946             }
11947             
11948             combobox.cn.push({
11949                 tag :'span',
11950                 cls : 'input-group-addon input-group-append input-group-text btn dropdown-toggle',
11951                 cn : [
11952                     Roo.bootstrap.version == 3 ? caret : '',
11953                     {
11954                         tag: 'span',
11955                         cls: 'combobox-clear',
11956                         cn  : [
11957                             {
11958                                 tag : 'i',
11959                                 cls: 'icon-remove'
11960                             }
11961                         ]
11962                     }
11963                 ]
11964
11965             })
11966         }
11967         
11968         if(this.multiple){
11969             combobox.cls += ' roo-select2-container-multi';
11970         }
11971          var indicator = {
11972             tag : 'i',
11973             cls : 'roo-required-indicator ' + (this.indicatorpos == 'right'  ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
11974             tooltip : 'This field is required'
11975         };
11976         if (Roo.bootstrap.version == 4) {
11977             indicator = {
11978                 tag : 'i',
11979                 style : 'display:none'
11980             };
11981         }
11982         
11983         
11984         if (align ==='left' && this.fieldLabel.length) {
11985             
11986             cfg.cls += ' roo-form-group-label-left'  + (Roo.bootstrap.version == 4 ? ' row' : '');
11987
11988             cfg.cn = [
11989                 indicator,
11990                 {
11991                     tag: 'label',
11992                     'for' :  id,
11993                     cls : 'control-label',
11994                     html : this.fieldLabel
11995
11996                 },
11997                 {
11998                     cls : "", 
11999                     cn: [
12000                         combobox
12001                     ]
12002                 }
12003
12004             ];
12005             
12006             var labelCfg = cfg.cn[1];
12007             var contentCfg = cfg.cn[2];
12008             
12009             if(this.indicatorpos == 'right'){
12010                 cfg.cn = [
12011                     {
12012                         tag: 'label',
12013                         'for' :  id,
12014                         cls : 'control-label',
12015                         cn : [
12016                             {
12017                                 tag : 'span',
12018                                 html : this.fieldLabel
12019                             },
12020                             indicator
12021                         ]
12022                     },
12023                     {
12024                         cls : "", 
12025                         cn: [
12026                             combobox
12027                         ]
12028                     }
12029
12030                 ];
12031                 
12032                 labelCfg = cfg.cn[0];
12033                 contentCfg = cfg.cn[1];
12034             }
12035             
12036             if(this.labelWidth > 12){
12037                 labelCfg.style = "width: " + this.labelWidth + 'px';
12038             }
12039             
12040             if(this.labelWidth < 13 && this.labelmd == 0){
12041                 this.labelmd = this.labelWidth;
12042             }
12043             
12044             if(this.labellg > 0){
12045                 labelCfg.cls += ' col-lg-' + this.labellg;
12046                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
12047             }
12048             
12049             if(this.labelmd > 0){
12050                 labelCfg.cls += ' col-md-' + this.labelmd;
12051                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
12052             }
12053             
12054             if(this.labelsm > 0){
12055                 labelCfg.cls += ' col-sm-' + this.labelsm;
12056                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
12057             }
12058             
12059             if(this.labelxs > 0){
12060                 labelCfg.cls += ' col-xs-' + this.labelxs;
12061                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
12062             }
12063             
12064         } else if ( this.fieldLabel.length) {
12065 //                Roo.log(" label");
12066             cfg.cn = [
12067                 indicator,
12068                {
12069                    tag: 'label',
12070                    //cls : 'input-group-addon',
12071                    html : this.fieldLabel
12072
12073                },
12074
12075                combobox
12076
12077             ];
12078             
12079             if(this.indicatorpos == 'right'){
12080                 
12081                 cfg.cn = [
12082                     {
12083                        tag: 'label',
12084                        cn : [
12085                            {
12086                                tag : 'span',
12087                                html : this.fieldLabel
12088                            },
12089                            indicator
12090                        ]
12091
12092                     },
12093                     combobox
12094
12095                 ];
12096
12097             }
12098
12099         } else {
12100             
12101 //                Roo.log(" no label && no align");
12102                 cfg = combobox
12103                      
12104                 
12105         }
12106         
12107         var settings=this;
12108         ['xs','sm','md','lg'].map(function(size){
12109             if (settings[size]) {
12110                 cfg.cls += ' col-' + size + '-' + settings[size];
12111             }
12112         });
12113         
12114         return cfg;
12115         
12116     },
12117     
12118     
12119     
12120     // private
12121     onResize : function(w, h){
12122 //        Roo.bootstrap.TriggerField.superclass.onResize.apply(this, arguments);
12123 //        if(typeof w == 'number'){
12124 //            var x = w - this.trigger.getWidth();
12125 //            this.inputEl().setWidth(this.adjustWidth('input', x));
12126 //            this.trigger.setStyle('left', x+'px');
12127 //        }
12128     },
12129
12130     // private
12131     adjustSize : Roo.BoxComponent.prototype.adjustSize,
12132
12133     // private
12134     getResizeEl : function(){
12135         return this.inputEl();
12136     },
12137
12138     // private
12139     getPositionEl : function(){
12140         return this.inputEl();
12141     },
12142
12143     // private
12144     alignErrorIcon : function(){
12145         this.errorIcon.alignTo(this.inputEl(), 'tl-tr', [2, 0]);
12146     },
12147
12148     // private
12149     initEvents : function(){
12150         
12151         this.createList();
12152         
12153         Roo.bootstrap.TriggerField.superclass.initEvents.call(this);
12154         //this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
12155         if(!this.multiple && this.showToggleBtn){
12156             this.trigger = this.el.select('span.dropdown-toggle',true).first();
12157             if(this.hideTrigger){
12158                 this.trigger.setDisplayed(false);
12159             }
12160             this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
12161         }
12162         
12163         if(this.multiple){
12164             this.inputEl().on("click", this.onTriggerClick, this, {preventDefault:true});
12165         }
12166         
12167         if(this.removable && !this.editable && !this.tickable){
12168             var close = this.closeTriggerEl();
12169             
12170             if(close){
12171                 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
12172                 close.on('click', this.removeBtnClick, this, close);
12173             }
12174         }
12175         
12176         //this.trigger.addClassOnOver('x-form-trigger-over');
12177         //this.trigger.addClassOnClick('x-form-trigger-click');
12178         
12179         //if(!this.width){
12180         //    this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
12181         //}
12182     },
12183     
12184     closeTriggerEl : function()
12185     {
12186         var close = this.el.select('.roo-combo-removable-btn', true).first();
12187         return close ? close : false;
12188     },
12189     
12190     removeBtnClick : function(e, h, el)
12191     {
12192         e.preventDefault();
12193         
12194         if(this.fireEvent("remove", this) !== false){
12195             this.reset();
12196             this.fireEvent("afterremove", this)
12197         }
12198     },
12199     
12200     createList : function()
12201     {
12202         this.list = Roo.get(document.body).createChild({
12203             tag: Roo.bootstrap.version == 4 ? 'div' : 'ul',
12204             cls: 'typeahead typeahead-long dropdown-menu',
12205             style: 'display:none'
12206         });
12207         
12208         this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';;
12209         
12210     },
12211
12212     // private
12213     initTrigger : function(){
12214        
12215     },
12216
12217     // private
12218     onDestroy : function(){
12219         if(this.trigger){
12220             this.trigger.removeAllListeners();
12221           //  this.trigger.remove();
12222         }
12223         //if(this.wrap){
12224         //    this.wrap.remove();
12225         //}
12226         Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
12227     },
12228
12229     // private
12230     onFocus : function(){
12231         Roo.bootstrap.TriggerField.superclass.onFocus.call(this);
12232         /*
12233         if(!this.mimicing){
12234             this.wrap.addClass('x-trigger-wrap-focus');
12235             this.mimicing = true;
12236             Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
12237             if(this.monitorTab){
12238                 this.el.on("keydown", this.checkTab, this);
12239             }
12240         }
12241         */
12242     },
12243
12244     // private
12245     checkTab : function(e){
12246         if(e.getKey() == e.TAB){
12247             this.triggerBlur();
12248         }
12249     },
12250
12251     // private
12252     onBlur : function(){
12253         // do nothing
12254     },
12255
12256     // private
12257     mimicBlur : function(e, t){
12258         /*
12259         if(!this.wrap.contains(t) && this.validateBlur()){
12260             this.triggerBlur();
12261         }
12262         */
12263     },
12264
12265     // private
12266     triggerBlur : function(){
12267         this.mimicing = false;
12268         Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
12269         if(this.monitorTab){
12270             this.el.un("keydown", this.checkTab, this);
12271         }
12272         //this.wrap.removeClass('x-trigger-wrap-focus');
12273         Roo.bootstrap.TriggerField.superclass.onBlur.call(this);
12274     },
12275
12276     // private
12277     // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
12278     validateBlur : function(e, t){
12279         return true;
12280     },
12281
12282     // private
12283     onDisable : function(){
12284         this.inputEl().dom.disabled = true;
12285         //Roo.bootstrap.TriggerField.superclass.onDisable.call(this);
12286         //if(this.wrap){
12287         //    this.wrap.addClass('x-item-disabled');
12288         //}
12289     },
12290
12291     // private
12292     onEnable : function(){
12293         this.inputEl().dom.disabled = false;
12294         //Roo.bootstrap.TriggerField.superclass.onEnable.call(this);
12295         //if(this.wrap){
12296         //    this.el.removeClass('x-item-disabled');
12297         //}
12298     },
12299
12300     // private
12301     onShow : function(){
12302         var ae = this.getActionEl();
12303         
12304         if(ae){
12305             ae.dom.style.display = '';
12306             ae.dom.style.visibility = 'visible';
12307         }
12308     },
12309
12310     // private
12311     
12312     onHide : function(){
12313         var ae = this.getActionEl();
12314         ae.dom.style.display = 'none';
12315     },
12316
12317     /**
12318      * The function that should handle the trigger's click event.  This method does nothing by default until overridden
12319      * by an implementing function.
12320      * @method
12321      * @param {EventObject} e
12322      */
12323     onTriggerClick : Roo.emptyFn
12324 });
12325  
12326 /*
12327 * Licence: LGPL
12328 */
12329
12330 /**
12331  * @class Roo.bootstrap.CardUploader
12332  * @extends Roo.bootstrap.Button
12333  * Bootstrap Card Uploader class - it's a button which when you add files to it, adds cards below with preview and the name...
12334  * @cfg {Number} errorTimeout default 3000
12335  * @cfg {Array}  images  an array of ?? Img objects ??? when loading existing files..
12336  * @cfg {Array}  html The button text.
12337
12338  *
12339  * @constructor
12340  * Create a new CardUploader
12341  * @param {Object} config The config object
12342  */
12343
12344 Roo.bootstrap.CardUploader = function(config){
12345     
12346  
12347     
12348     Roo.bootstrap.CardUploader.superclass.constructor.call(this, config);
12349     
12350     
12351     this.fileCollection   = new Roo.util.MixedCollection(false,function(r) {
12352         return r.data.id
12353         });
12354     
12355     
12356 };
12357
12358 Roo.extend(Roo.bootstrap.CardUploader, Roo.bootstrap.Input,  {
12359     
12360      
12361     errorTimeout : 3000,
12362      
12363     images : false,
12364    
12365     fileCollection : false,
12366     allowBlank : true,
12367     
12368     getAutoCreate : function()
12369     {
12370         
12371         var cfg =  {
12372             cls :'form-group' ,
12373             cn : [
12374                
12375                 {
12376                     tag: 'label',
12377                    //cls : 'input-group-addon',
12378                     html : this.fieldLabel
12379
12380                 },
12381
12382                 {
12383                     tag: 'input',
12384                     type : 'hidden',
12385                     value : this.value,
12386                     cls : 'd-none  form-control'
12387                 },
12388                 
12389                 {
12390                     tag: 'input',
12391                     multiple : 'multiple',
12392                     type : 'file',
12393                     cls : 'd-none  roo-card-upload-selector'
12394                 },
12395                 
12396                 {
12397                     cls : 'roo-card-uploader-button-container w-100 mb-2'
12398                 },
12399                 {
12400                     cls : 'card-columns roo-card-uploader-container'
12401                 }
12402
12403             ]
12404         };
12405            
12406          
12407         return cfg;
12408     },
12409     
12410     getChildContainer : function() /// what children are added to.
12411     {
12412         return this.containerEl;
12413     },
12414    
12415     getButtonContainer : function() /// what children are added to.
12416     {
12417         return this.el.select(".roo-card-uploader-button-container").first();
12418     },
12419    
12420     initEvents : function()
12421     {
12422         
12423         Roo.bootstrap.Input.prototype.initEvents.call(this);
12424         
12425         var t = this;
12426         this.addxtype({
12427             xns: Roo.bootstrap,
12428
12429             xtype : 'Button',
12430             container_method : 'getButtonContainer' ,            
12431             html :  this.html, // fix changable?
12432             cls : 'w-100 ',
12433             listeners : {
12434                 'click' : function(btn, e) {
12435                     t.onClick(e);
12436                 }
12437             }
12438         });
12439         
12440         
12441         
12442         
12443         this.urlAPI = (window.createObjectURL && window) || 
12444                                 (window.URL && URL.revokeObjectURL && URL) || 
12445                                 (window.webkitURL && webkitURL);
12446                         
12447          
12448          
12449          
12450         this.selectorEl = this.el.select('.roo-card-upload-selector', true).first();
12451         
12452         this.selectorEl.on('change', this.onFileSelected, this);
12453         if (this.images) {
12454             var t = this;
12455             this.images.forEach(function(img) {
12456                 t.addCard(img)
12457             });
12458             this.images = false;
12459         }
12460         this.containerEl = this.el.select('.roo-card-uploader-container', true).first();
12461          
12462        
12463     },
12464     
12465    
12466     onClick : function(e)
12467     {
12468         e.preventDefault();
12469          
12470         this.selectorEl.dom.click();
12471          
12472     },
12473     
12474     onFileSelected : function(e)
12475     {
12476         e.preventDefault();
12477         
12478         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
12479             return;
12480         }
12481         
12482         Roo.each(this.selectorEl.dom.files, function(file){    
12483             this.addFile(file);
12484         }, this);
12485          
12486     },
12487     
12488       
12489     
12490       
12491     
12492     addFile : function(file)
12493     {
12494            
12495         if(typeof(file) === 'string'){
12496             throw "Add file by name?"; // should not happen
12497             return;
12498         }
12499         
12500         if(!file || !this.urlAPI){
12501             return;
12502         }
12503         
12504         // file;
12505         // file.type;
12506         
12507         var _this = this;
12508         
12509         
12510         var url = _this.urlAPI.createObjectURL( file);
12511            
12512         this.addCard({
12513             id : Roo.bootstrap.CardUploader.ID--,
12514             is_uploaded : false,
12515             src : url,
12516             title : file.name,
12517             mimetype : file.type,
12518             preview : false,
12519             is_deleted : 0
12520         })
12521         
12522     },
12523     
12524     addCard : function (data)
12525     {
12526         // hidden input element?
12527         // if the file is not an image...
12528         //then we need to use something other that and header_image
12529         var t = this;
12530         //   remove.....
12531         var footer = [
12532             {
12533                 xns : Roo.bootstrap,
12534                 xtype : 'CardFooter',
12535                 items: [
12536                     {
12537                         xns : Roo.bootstrap,
12538                         xtype : 'Element',
12539                         cls : 'd-flex',
12540                         items : [
12541                             
12542                             {
12543                                 xns : Roo.bootstrap,
12544                                 xtype : 'Button',
12545                                 html : String.format("<small>{0}</small>", data.title),
12546                                 cls : 'col-11 text-left',
12547                                 size: 'sm',
12548                                 weight: 'link',
12549                                 fa : 'download',
12550                                 listeners : {
12551                                     click : function() {
12552                                         this.downloadCard(data.id)
12553                                     }
12554                                 }
12555                             },
12556                           
12557                             {
12558                                 xns : Roo.bootstrap,
12559                                 xtype : 'Button',
12560                                 
12561                                 size : 'sm',
12562                                 weight: 'danger',
12563                                 cls : 'col-1',
12564                                 fa : 'times',
12565                                 listeners : {
12566                                     click : function() {
12567                                         t.removeCard(data.id)
12568                                     }
12569                                 }
12570                             }
12571                         ]
12572                     }
12573                     
12574                 ] 
12575             }
12576             
12577         ];
12578
12579         var cn = this.addxtype(
12580             {
12581                  
12582                 xns : Roo.bootstrap,
12583                 xtype : 'Card',
12584                 closeable : true,
12585                 header : !data.mimetype.match(/image/) && !data.preview ? "Document": false,
12586                 header_image : data.mimetype.match(/image/) ? data.src  : data.preview,
12587                 header_image_fit_square: true, // fixme  - we probably need to use the 'Img' element to do stuff like this.
12588                 data : data,
12589                 html : false,
12590                  
12591                 items : footer,
12592                 initEvents : function() {
12593                     Roo.bootstrap.Card.prototype.initEvents.call(this);
12594                     this.imgEl = this.el.select('.card-img-top').first();
12595                     if (this.imgEl) {
12596                         this.imgEl.on('click', function() { t.previewCard( data.id); }, this);
12597                         this.imgEl.set({ 'pointer' : 'cursor' });
12598                                   
12599                     }
12600                     
12601                   
12602                 }
12603                 
12604             }
12605         );
12606         // dont' really need ot update items.
12607         // this.items.push(cn);
12608         this.fileCollection.add(cn);
12609         this.updateInput();
12610         
12611     },
12612     removeCard : function(id)
12613     {
12614         
12615         var card  = this.fileCollection.get(id);
12616         card.data.is_deleted = 1;
12617         card.data.src = ''; /// delete the source - so it reduces size of not uploaded images etc.
12618         this.fileCollection.remove(card);
12619         //this.items = this.items.filter(function(e) { return e != card });
12620         // dont' really need ot update items.
12621         card.el.dom.parentNode.removeChild(card.el.dom);
12622         
12623     },
12624     reset: function()
12625     {
12626         this.fileCollection.each(function(card) {
12627             card.el.dom.parentNode.removeChild(card.el.dom);    
12628         });
12629         this.fileCollection.clear();
12630         this.updateInput();
12631     },
12632     
12633     updateInput : function()
12634     {
12635         var data = [];
12636         this.fileCollection.each(function(e) {
12637             data.push(e.data);
12638         });
12639         
12640         this.inputEl().dom.value = JSON.stringify(data);
12641     }
12642     
12643     
12644 });
12645
12646
12647 Roo.bootstrap.CardUploader.ID = -1;/*
12648  * Based on:
12649  * Ext JS Library 1.1.1
12650  * Copyright(c) 2006-2007, Ext JS, LLC.
12651  *
12652  * Originally Released Under LGPL - original licence link has changed is not relivant.
12653  *
12654  * Fork - LGPL
12655  * <script type="text/javascript">
12656  */
12657
12658
12659 /**
12660  * @class Roo.data.SortTypes
12661  * @singleton
12662  * Defines the default sorting (casting?) comparison functions used when sorting data.
12663  */
12664 Roo.data.SortTypes = {
12665     /**
12666      * Default sort that does nothing
12667      * @param {Mixed} s The value being converted
12668      * @return {Mixed} The comparison value
12669      */
12670     none : function(s){
12671         return s;
12672     },
12673     
12674     /**
12675      * The regular expression used to strip tags
12676      * @type {RegExp}
12677      * @property
12678      */
12679     stripTagsRE : /<\/?[^>]+>/gi,
12680     
12681     /**
12682      * Strips all HTML tags to sort on text only
12683      * @param {Mixed} s The value being converted
12684      * @return {String} The comparison value
12685      */
12686     asText : function(s){
12687         return String(s).replace(this.stripTagsRE, "");
12688     },
12689     
12690     /**
12691      * Strips all HTML tags to sort on text only - Case insensitive
12692      * @param {Mixed} s The value being converted
12693      * @return {String} The comparison value
12694      */
12695     asUCText : function(s){
12696         return String(s).toUpperCase().replace(this.stripTagsRE, "");
12697     },
12698     
12699     /**
12700      * Case insensitive string
12701      * @param {Mixed} s The value being converted
12702      * @return {String} The comparison value
12703      */
12704     asUCString : function(s) {
12705         return String(s).toUpperCase();
12706     },
12707     
12708     /**
12709      * Date sorting
12710      * @param {Mixed} s The value being converted
12711      * @return {Number} The comparison value
12712      */
12713     asDate : function(s) {
12714         if(!s){
12715             return 0;
12716         }
12717         if(s instanceof Date){
12718             return s.getTime();
12719         }
12720         return Date.parse(String(s));
12721     },
12722     
12723     /**
12724      * Float sorting
12725      * @param {Mixed} s The value being converted
12726      * @return {Float} The comparison value
12727      */
12728     asFloat : function(s) {
12729         var val = parseFloat(String(s).replace(/,/g, ""));
12730         if(isNaN(val)) {
12731             val = 0;
12732         }
12733         return val;
12734     },
12735     
12736     /**
12737      * Integer sorting
12738      * @param {Mixed} s The value being converted
12739      * @return {Number} The comparison value
12740      */
12741     asInt : function(s) {
12742         var val = parseInt(String(s).replace(/,/g, ""));
12743         if(isNaN(val)) {
12744             val = 0;
12745         }
12746         return val;
12747     }
12748 };/*
12749  * Based on:
12750  * Ext JS Library 1.1.1
12751  * Copyright(c) 2006-2007, Ext JS, LLC.
12752  *
12753  * Originally Released Under LGPL - original licence link has changed is not relivant.
12754  *
12755  * Fork - LGPL
12756  * <script type="text/javascript">
12757  */
12758
12759 /**
12760 * @class Roo.data.Record
12761  * Instances of this class encapsulate both record <em>definition</em> information, and record
12762  * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
12763  * to access Records cached in an {@link Roo.data.Store} object.<br>
12764  * <p>
12765  * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
12766  * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
12767  * objects.<br>
12768  * <p>
12769  * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
12770  * @constructor
12771  * This constructor should not be used to create Record objects. Instead, use the constructor generated by
12772  * {@link #create}. The parameters are the same.
12773  * @param {Array} data An associative Array of data values keyed by the field name.
12774  * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
12775  * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
12776  * not specified an integer id is generated.
12777  */
12778 Roo.data.Record = function(data, id){
12779     this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
12780     this.data = data;
12781 };
12782
12783 /**
12784  * Generate a constructor for a specific record layout.
12785  * @param {Array} o An Array of field definition objects which specify field names, and optionally,
12786  * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
12787  * Each field definition object may contain the following properties: <ul>
12788  * <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,
12789  * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
12790  * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
12791  * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
12792  * is being used, then this is a string containing the javascript expression to reference the data relative to 
12793  * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
12794  * to the data item relative to the record element. If the mapping expression is the same as the field name,
12795  * this may be omitted.</p></li>
12796  * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
12797  * <ul><li>auto (Default, implies no conversion)</li>
12798  * <li>string</li>
12799  * <li>int</li>
12800  * <li>float</li>
12801  * <li>boolean</li>
12802  * <li>date</li></ul></p></li>
12803  * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
12804  * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
12805  * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
12806  * by the Reader into an object that will be stored in the Record. It is passed the
12807  * following parameters:<ul>
12808  * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
12809  * </ul></p></li>
12810  * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
12811  * </ul>
12812  * <br>usage:<br><pre><code>
12813 var TopicRecord = Roo.data.Record.create(
12814     {name: 'title', mapping: 'topic_title'},
12815     {name: 'author', mapping: 'username'},
12816     {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
12817     {name: 'lastPost', mapping: 'post_time', type: 'date'},
12818     {name: 'lastPoster', mapping: 'user2'},
12819     {name: 'excerpt', mapping: 'post_text'}
12820 );
12821
12822 var myNewRecord = new TopicRecord({
12823     title: 'Do my job please',
12824     author: 'noobie',
12825     totalPosts: 1,
12826     lastPost: new Date(),
12827     lastPoster: 'Animal',
12828     excerpt: 'No way dude!'
12829 });
12830 myStore.add(myNewRecord);
12831 </code></pre>
12832  * @method create
12833  * @static
12834  */
12835 Roo.data.Record.create = function(o){
12836     var f = function(){
12837         f.superclass.constructor.apply(this, arguments);
12838     };
12839     Roo.extend(f, Roo.data.Record);
12840     var p = f.prototype;
12841     p.fields = new Roo.util.MixedCollection(false, function(field){
12842         return field.name;
12843     });
12844     for(var i = 0, len = o.length; i < len; i++){
12845         p.fields.add(new Roo.data.Field(o[i]));
12846     }
12847     f.getField = function(name){
12848         return p.fields.get(name);  
12849     };
12850     return f;
12851 };
12852
12853 Roo.data.Record.AUTO_ID = 1000;
12854 Roo.data.Record.EDIT = 'edit';
12855 Roo.data.Record.REJECT = 'reject';
12856 Roo.data.Record.COMMIT = 'commit';
12857
12858 Roo.data.Record.prototype = {
12859     /**
12860      * Readonly flag - true if this record has been modified.
12861      * @type Boolean
12862      */
12863     dirty : false,
12864     editing : false,
12865     error: null,
12866     modified: null,
12867
12868     // private
12869     join : function(store){
12870         this.store = store;
12871     },
12872
12873     /**
12874      * Set the named field to the specified value.
12875      * @param {String} name The name of the field to set.
12876      * @param {Object} value The value to set the field to.
12877      */
12878     set : function(name, value){
12879         if(this.data[name] == value){
12880             return;
12881         }
12882         this.dirty = true;
12883         if(!this.modified){
12884             this.modified = {};
12885         }
12886         if(typeof this.modified[name] == 'undefined'){
12887             this.modified[name] = this.data[name];
12888         }
12889         this.data[name] = value;
12890         if(!this.editing && this.store){
12891             this.store.afterEdit(this);
12892         }       
12893     },
12894
12895     /**
12896      * Get the value of the named field.
12897      * @param {String} name The name of the field to get the value of.
12898      * @return {Object} The value of the field.
12899      */
12900     get : function(name){
12901         return this.data[name]; 
12902     },
12903
12904     // private
12905     beginEdit : function(){
12906         this.editing = true;
12907         this.modified = {}; 
12908     },
12909
12910     // private
12911     cancelEdit : function(){
12912         this.editing = false;
12913         delete this.modified;
12914     },
12915
12916     // private
12917     endEdit : function(){
12918         this.editing = false;
12919         if(this.dirty && this.store){
12920             this.store.afterEdit(this);
12921         }
12922     },
12923
12924     /**
12925      * Usually called by the {@link Roo.data.Store} which owns the Record.
12926      * Rejects all changes made to the Record since either creation, or the last commit operation.
12927      * Modified fields are reverted to their original values.
12928      * <p>
12929      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
12930      * of reject operations.
12931      */
12932     reject : function(){
12933         var m = this.modified;
12934         for(var n in m){
12935             if(typeof m[n] != "function"){
12936                 this.data[n] = m[n];
12937             }
12938         }
12939         this.dirty = false;
12940         delete this.modified;
12941         this.editing = false;
12942         if(this.store){
12943             this.store.afterReject(this);
12944         }
12945     },
12946
12947     /**
12948      * Usually called by the {@link Roo.data.Store} which owns the Record.
12949      * Commits all changes made to the Record since either creation, or the last commit operation.
12950      * <p>
12951      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
12952      * of commit operations.
12953      */
12954     commit : function(){
12955         this.dirty = false;
12956         delete this.modified;
12957         this.editing = false;
12958         if(this.store){
12959             this.store.afterCommit(this);
12960         }
12961     },
12962
12963     // private
12964     hasError : function(){
12965         return this.error != null;
12966     },
12967
12968     // private
12969     clearError : function(){
12970         this.error = null;
12971     },
12972
12973     /**
12974      * Creates a copy of this record.
12975      * @param {String} id (optional) A new record id if you don't want to use this record's id
12976      * @return {Record}
12977      */
12978     copy : function(newId) {
12979         return new this.constructor(Roo.apply({}, this.data), newId || this.id);
12980     }
12981 };/*
12982  * Based on:
12983  * Ext JS Library 1.1.1
12984  * Copyright(c) 2006-2007, Ext JS, LLC.
12985  *
12986  * Originally Released Under LGPL - original licence link has changed is not relivant.
12987  *
12988  * Fork - LGPL
12989  * <script type="text/javascript">
12990  */
12991
12992
12993
12994 /**
12995  * @class Roo.data.Store
12996  * @extends Roo.util.Observable
12997  * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
12998  * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
12999  * <p>
13000  * 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
13001  * has no knowledge of the format of the data returned by the Proxy.<br>
13002  * <p>
13003  * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
13004  * instances from the data object. These records are cached and made available through accessor functions.
13005  * @constructor
13006  * Creates a new Store.
13007  * @param {Object} config A config object containing the objects needed for the Store to access data,
13008  * and read the data into Records.
13009  */
13010 Roo.data.Store = function(config){
13011     this.data = new Roo.util.MixedCollection(false);
13012     this.data.getKey = function(o){
13013         return o.id;
13014     };
13015     this.baseParams = {};
13016     // private
13017     this.paramNames = {
13018         "start" : "start",
13019         "limit" : "limit",
13020         "sort" : "sort",
13021         "dir" : "dir",
13022         "multisort" : "_multisort"
13023     };
13024
13025     if(config && config.data){
13026         this.inlineData = config.data;
13027         delete config.data;
13028     }
13029
13030     Roo.apply(this, config);
13031     
13032     if(this.reader){ // reader passed
13033         this.reader = Roo.factory(this.reader, Roo.data);
13034         this.reader.xmodule = this.xmodule || false;
13035         if(!this.recordType){
13036             this.recordType = this.reader.recordType;
13037         }
13038         if(this.reader.onMetaChange){
13039             this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
13040         }
13041     }
13042
13043     if(this.recordType){
13044         this.fields = this.recordType.prototype.fields;
13045     }
13046     this.modified = [];
13047
13048     this.addEvents({
13049         /**
13050          * @event datachanged
13051          * Fires when the data cache has changed, and a widget which is using this Store
13052          * as a Record cache should refresh its view.
13053          * @param {Store} this
13054          */
13055         datachanged : true,
13056         /**
13057          * @event metachange
13058          * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
13059          * @param {Store} this
13060          * @param {Object} meta The JSON metadata
13061          */
13062         metachange : true,
13063         /**
13064          * @event add
13065          * Fires when Records have been added to the Store
13066          * @param {Store} this
13067          * @param {Roo.data.Record[]} records The array of Records added
13068          * @param {Number} index The index at which the record(s) were added
13069          */
13070         add : true,
13071         /**
13072          * @event remove
13073          * Fires when a Record has been removed from the Store
13074          * @param {Store} this
13075          * @param {Roo.data.Record} record The Record that was removed
13076          * @param {Number} index The index at which the record was removed
13077          */
13078         remove : true,
13079         /**
13080          * @event update
13081          * Fires when a Record has been updated
13082          * @param {Store} this
13083          * @param {Roo.data.Record} record The Record that was updated
13084          * @param {String} operation The update operation being performed.  Value may be one of:
13085          * <pre><code>
13086  Roo.data.Record.EDIT
13087  Roo.data.Record.REJECT
13088  Roo.data.Record.COMMIT
13089          * </code></pre>
13090          */
13091         update : true,
13092         /**
13093          * @event clear
13094          * Fires when the data cache has been cleared.
13095          * @param {Store} this
13096          */
13097         clear : true,
13098         /**
13099          * @event beforeload
13100          * Fires before a request is made for a new data object.  If the beforeload handler returns false
13101          * the load action will be canceled.
13102          * @param {Store} this
13103          * @param {Object} options The loading options that were specified (see {@link #load} for details)
13104          */
13105         beforeload : true,
13106         /**
13107          * @event beforeloadadd
13108          * Fires after a new set of Records has been loaded.
13109          * @param {Store} this
13110          * @param {Roo.data.Record[]} records The Records that were loaded
13111          * @param {Object} options The loading options that were specified (see {@link #load} for details)
13112          */
13113         beforeloadadd : true,
13114         /**
13115          * @event load
13116          * Fires after a new set of Records has been loaded, before they are added to the store.
13117          * @param {Store} this
13118          * @param {Roo.data.Record[]} records The Records that were loaded
13119          * @param {Object} options The loading options that were specified (see {@link #load} for details)
13120          * @params {Object} return from reader
13121          */
13122         load : true,
13123         /**
13124          * @event loadexception
13125          * Fires if an exception occurs in the Proxy during loading.
13126          * Called with the signature of the Proxy's "loadexception" event.
13127          * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
13128          * 
13129          * @param {Proxy} 
13130          * @param {Object} return from JsonData.reader() - success, totalRecords, records
13131          * @param {Object} load options 
13132          * @param {Object} jsonData from your request (normally this contains the Exception)
13133          */
13134         loadexception : true
13135     });
13136     
13137     if(this.proxy){
13138         this.proxy = Roo.factory(this.proxy, Roo.data);
13139         this.proxy.xmodule = this.xmodule || false;
13140         this.relayEvents(this.proxy,  ["loadexception"]);
13141     }
13142     this.sortToggle = {};
13143     this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
13144
13145     Roo.data.Store.superclass.constructor.call(this);
13146
13147     if(this.inlineData){
13148         this.loadData(this.inlineData);
13149         delete this.inlineData;
13150     }
13151 };
13152
13153 Roo.extend(Roo.data.Store, Roo.util.Observable, {
13154      /**
13155     * @cfg {boolean} isLocal   flag if data is locally available (and can be always looked up
13156     * without a remote query - used by combo/forms at present.
13157     */
13158     
13159     /**
13160     * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
13161     */
13162     /**
13163     * @cfg {Array} data Inline data to be loaded when the store is initialized.
13164     */
13165     /**
13166     * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
13167     * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
13168     */
13169     /**
13170     * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
13171     * on any HTTP request
13172     */
13173     /**
13174     * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
13175     */
13176     /**
13177     * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
13178     */
13179     multiSort: false,
13180     /**
13181     * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
13182     * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
13183     */
13184     remoteSort : false,
13185
13186     /**
13187     * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
13188      * loaded or when a record is removed. (defaults to false).
13189     */
13190     pruneModifiedRecords : false,
13191
13192     // private
13193     lastOptions : null,
13194
13195     /**
13196      * Add Records to the Store and fires the add event.
13197      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
13198      */
13199     add : function(records){
13200         records = [].concat(records);
13201         for(var i = 0, len = records.length; i < len; i++){
13202             records[i].join(this);
13203         }
13204         var index = this.data.length;
13205         this.data.addAll(records);
13206         this.fireEvent("add", this, records, index);
13207     },
13208
13209     /**
13210      * Remove a Record from the Store and fires the remove event.
13211      * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
13212      */
13213     remove : function(record){
13214         var index = this.data.indexOf(record);
13215         this.data.removeAt(index);
13216  
13217         if(this.pruneModifiedRecords){
13218             this.modified.remove(record);
13219         }
13220         this.fireEvent("remove", this, record, index);
13221     },
13222
13223     /**
13224      * Remove all Records from the Store and fires the clear event.
13225      */
13226     removeAll : function(){
13227         this.data.clear();
13228         if(this.pruneModifiedRecords){
13229             this.modified = [];
13230         }
13231         this.fireEvent("clear", this);
13232     },
13233
13234     /**
13235      * Inserts Records to the Store at the given index and fires the add event.
13236      * @param {Number} index The start index at which to insert the passed Records.
13237      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
13238      */
13239     insert : function(index, records){
13240         records = [].concat(records);
13241         for(var i = 0, len = records.length; i < len; i++){
13242             this.data.insert(index, records[i]);
13243             records[i].join(this);
13244         }
13245         this.fireEvent("add", this, records, index);
13246     },
13247
13248     /**
13249      * Get the index within the cache of the passed Record.
13250      * @param {Roo.data.Record} record The Roo.data.Record object to to find.
13251      * @return {Number} The index of the passed Record. Returns -1 if not found.
13252      */
13253     indexOf : function(record){
13254         return this.data.indexOf(record);
13255     },
13256
13257     /**
13258      * Get the index within the cache of the Record with the passed id.
13259      * @param {String} id The id of the Record to find.
13260      * @return {Number} The index of the Record. Returns -1 if not found.
13261      */
13262     indexOfId : function(id){
13263         return this.data.indexOfKey(id);
13264     },
13265
13266     /**
13267      * Get the Record with the specified id.
13268      * @param {String} id The id of the Record to find.
13269      * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
13270      */
13271     getById : function(id){
13272         return this.data.key(id);
13273     },
13274
13275     /**
13276      * Get the Record at the specified index.
13277      * @param {Number} index The index of the Record to find.
13278      * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
13279      */
13280     getAt : function(index){
13281         return this.data.itemAt(index);
13282     },
13283
13284     /**
13285      * Returns a range of Records between specified indices.
13286      * @param {Number} startIndex (optional) The starting index (defaults to 0)
13287      * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
13288      * @return {Roo.data.Record[]} An array of Records
13289      */
13290     getRange : function(start, end){
13291         return this.data.getRange(start, end);
13292     },
13293
13294     // private
13295     storeOptions : function(o){
13296         o = Roo.apply({}, o);
13297         delete o.callback;
13298         delete o.scope;
13299         this.lastOptions = o;
13300     },
13301
13302     /**
13303      * Loads the Record cache from the configured Proxy using the configured Reader.
13304      * <p>
13305      * If using remote paging, then the first load call must specify the <em>start</em>
13306      * and <em>limit</em> properties in the options.params property to establish the initial
13307      * position within the dataset, and the number of Records to cache on each read from the Proxy.
13308      * <p>
13309      * <strong>It is important to note that for remote data sources, loading is asynchronous,
13310      * and this call will return before the new data has been loaded. Perform any post-processing
13311      * in a callback function, or in a "load" event handler.</strong>
13312      * <p>
13313      * @param {Object} options An object containing properties which control loading options:<ul>
13314      * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
13315      * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
13316      * passed the following arguments:<ul>
13317      * <li>r : Roo.data.Record[]</li>
13318      * <li>options: Options object from the load call</li>
13319      * <li>success: Boolean success indicator</li></ul></li>
13320      * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
13321      * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
13322      * </ul>
13323      */
13324     load : function(options){
13325         options = options || {};
13326         if(this.fireEvent("beforeload", this, options) !== false){
13327             this.storeOptions(options);
13328             var p = Roo.apply(options.params || {}, this.baseParams);
13329             // if meta was not loaded from remote source.. try requesting it.
13330             if (!this.reader.metaFromRemote) {
13331                 p._requestMeta = 1;
13332             }
13333             if(this.sortInfo && this.remoteSort){
13334                 var pn = this.paramNames;
13335                 p[pn["sort"]] = this.sortInfo.field;
13336                 p[pn["dir"]] = this.sortInfo.direction;
13337             }
13338             if (this.multiSort) {
13339                 var pn = this.paramNames;
13340                 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
13341             }
13342             
13343             this.proxy.load(p, this.reader, this.loadRecords, this, options);
13344         }
13345     },
13346
13347     /**
13348      * Reloads the Record cache from the configured Proxy using the configured Reader and
13349      * the options from the last load operation performed.
13350      * @param {Object} options (optional) An object containing properties which may override the options
13351      * used in the last load operation. See {@link #load} for details (defaults to null, in which case
13352      * the most recently used options are reused).
13353      */
13354     reload : function(options){
13355         this.load(Roo.applyIf(options||{}, this.lastOptions));
13356     },
13357
13358     // private
13359     // Called as a callback by the Reader during a load operation.
13360     loadRecords : function(o, options, success){
13361         if(!o || success === false){
13362             if(success !== false){
13363                 this.fireEvent("load", this, [], options, o);
13364             }
13365             if(options.callback){
13366                 options.callback.call(options.scope || this, [], options, false);
13367             }
13368             return;
13369         }
13370         // if data returned failure - throw an exception.
13371         if (o.success === false) {
13372             // show a message if no listener is registered.
13373             if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
13374                     Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
13375             }
13376             // loadmask wil be hooked into this..
13377             this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
13378             return;
13379         }
13380         var r = o.records, t = o.totalRecords || r.length;
13381         
13382         this.fireEvent("beforeloadadd", this, r, options, o);
13383         
13384         if(!options || options.add !== true){
13385             if(this.pruneModifiedRecords){
13386                 this.modified = [];
13387             }
13388             for(var i = 0, len = r.length; i < len; i++){
13389                 r[i].join(this);
13390             }
13391             if(this.snapshot){
13392                 this.data = this.snapshot;
13393                 delete this.snapshot;
13394             }
13395             this.data.clear();
13396             this.data.addAll(r);
13397             this.totalLength = t;
13398             this.applySort();
13399             this.fireEvent("datachanged", this);
13400         }else{
13401             this.totalLength = Math.max(t, this.data.length+r.length);
13402             this.add(r);
13403         }
13404         
13405         if(this.parent && !Roo.isIOS && !this.useNativeIOS && this.parent.emptyTitle.length) {
13406                 
13407             var e = new Roo.data.Record({});
13408
13409             e.set(this.parent.displayField, this.parent.emptyTitle);
13410             e.set(this.parent.valueField, '');
13411
13412             this.insert(0, e);
13413         }
13414             
13415         this.fireEvent("load", this, r, options, o);
13416         if(options.callback){
13417             options.callback.call(options.scope || this, r, options, true);
13418         }
13419     },
13420
13421
13422     /**
13423      * Loads data from a passed data block. A Reader which understands the format of the data
13424      * must have been configured in the constructor.
13425      * @param {Object} data The data block from which to read the Records.  The format of the data expected
13426      * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
13427      * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
13428      */
13429     loadData : function(o, append){
13430         var r = this.reader.readRecords(o);
13431         this.loadRecords(r, {add: append}, true);
13432     },
13433     
13434      /**
13435      * using 'cn' the nested child reader read the child array into it's child stores.
13436      * @param {Object} rec The record with a 'children array
13437      */
13438     loadDataFromChildren : function(rec)
13439     {
13440         this.loadData(this.reader.toLoadData(rec));
13441     },
13442     
13443
13444     /**
13445      * Gets the number of cached records.
13446      * <p>
13447      * <em>If using paging, this may not be the total size of the dataset. If the data object
13448      * used by the Reader contains the dataset size, then the getTotalCount() function returns
13449      * the data set size</em>
13450      */
13451     getCount : function(){
13452         return this.data.length || 0;
13453     },
13454
13455     /**
13456      * Gets the total number of records in the dataset as returned by the server.
13457      * <p>
13458      * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
13459      * the dataset size</em>
13460      */
13461     getTotalCount : function(){
13462         return this.totalLength || 0;
13463     },
13464
13465     /**
13466      * Returns the sort state of the Store as an object with two properties:
13467      * <pre><code>
13468  field {String} The name of the field by which the Records are sorted
13469  direction {String} The sort order, "ASC" or "DESC"
13470      * </code></pre>
13471      */
13472     getSortState : function(){
13473         return this.sortInfo;
13474     },
13475
13476     // private
13477     applySort : function(){
13478         if(this.sortInfo && !this.remoteSort){
13479             var s = this.sortInfo, f = s.field;
13480             var st = this.fields.get(f).sortType;
13481             var fn = function(r1, r2){
13482                 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
13483                 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
13484             };
13485             this.data.sort(s.direction, fn);
13486             if(this.snapshot && this.snapshot != this.data){
13487                 this.snapshot.sort(s.direction, fn);
13488             }
13489         }
13490     },
13491
13492     /**
13493      * Sets the default sort column and order to be used by the next load operation.
13494      * @param {String} fieldName The name of the field to sort by.
13495      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
13496      */
13497     setDefaultSort : function(field, dir){
13498         this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
13499     },
13500
13501     /**
13502      * Sort the Records.
13503      * If remote sorting is used, the sort is performed on the server, and the cache is
13504      * reloaded. If local sorting is used, the cache is sorted internally.
13505      * @param {String} fieldName The name of the field to sort by.
13506      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
13507      */
13508     sort : function(fieldName, dir){
13509         var f = this.fields.get(fieldName);
13510         if(!dir){
13511             this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
13512             
13513             if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
13514                 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
13515             }else{
13516                 dir = f.sortDir;
13517             }
13518         }
13519         this.sortToggle[f.name] = dir;
13520         this.sortInfo = {field: f.name, direction: dir};
13521         if(!this.remoteSort){
13522             this.applySort();
13523             this.fireEvent("datachanged", this);
13524         }else{
13525             this.load(this.lastOptions);
13526         }
13527     },
13528
13529     /**
13530      * Calls the specified function for each of the Records in the cache.
13531      * @param {Function} fn The function to call. The Record is passed as the first parameter.
13532      * Returning <em>false</em> aborts and exits the iteration.
13533      * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
13534      */
13535     each : function(fn, scope){
13536         this.data.each(fn, scope);
13537     },
13538
13539     /**
13540      * Gets all records modified since the last commit.  Modified records are persisted across load operations
13541      * (e.g., during paging).
13542      * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
13543      */
13544     getModifiedRecords : function(){
13545         return this.modified;
13546     },
13547
13548     // private
13549     createFilterFn : function(property, value, anyMatch){
13550         if(!value.exec){ // not a regex
13551             value = String(value);
13552             if(value.length == 0){
13553                 return false;
13554             }
13555             value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
13556         }
13557         return function(r){
13558             return value.test(r.data[property]);
13559         };
13560     },
13561
13562     /**
13563      * Sums the value of <i>property</i> for each record between start and end and returns the result.
13564      * @param {String} property A field on your records
13565      * @param {Number} start The record index to start at (defaults to 0)
13566      * @param {Number} end The last record index to include (defaults to length - 1)
13567      * @return {Number} The sum
13568      */
13569     sum : function(property, start, end){
13570         var rs = this.data.items, v = 0;
13571         start = start || 0;
13572         end = (end || end === 0) ? end : rs.length-1;
13573
13574         for(var i = start; i <= end; i++){
13575             v += (rs[i].data[property] || 0);
13576         }
13577         return v;
13578     },
13579
13580     /**
13581      * Filter the records by a specified property.
13582      * @param {String} field A field on your records
13583      * @param {String/RegExp} value Either a string that the field
13584      * should start with or a RegExp to test against the field
13585      * @param {Boolean} anyMatch True to match any part not just the beginning
13586      */
13587     filter : function(property, value, anyMatch){
13588         var fn = this.createFilterFn(property, value, anyMatch);
13589         return fn ? this.filterBy(fn) : this.clearFilter();
13590     },
13591
13592     /**
13593      * Filter by a function. The specified function will be called with each
13594      * record in this data source. If the function returns true the record is included,
13595      * otherwise it is filtered.
13596      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
13597      * @param {Object} scope (optional) The scope of the function (defaults to this)
13598      */
13599     filterBy : function(fn, scope){
13600         this.snapshot = this.snapshot || this.data;
13601         this.data = this.queryBy(fn, scope||this);
13602         this.fireEvent("datachanged", this);
13603     },
13604
13605     /**
13606      * Query the records by a specified property.
13607      * @param {String} field A field on your records
13608      * @param {String/RegExp} value Either a string that the field
13609      * should start with or a RegExp to test against the field
13610      * @param {Boolean} anyMatch True to match any part not just the beginning
13611      * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
13612      */
13613     query : function(property, value, anyMatch){
13614         var fn = this.createFilterFn(property, value, anyMatch);
13615         return fn ? this.queryBy(fn) : this.data.clone();
13616     },
13617
13618     /**
13619      * Query by a function. The specified function will be called with each
13620      * record in this data source. If the function returns true the record is included
13621      * in the results.
13622      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
13623      * @param {Object} scope (optional) The scope of the function (defaults to this)
13624       @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
13625      **/
13626     queryBy : function(fn, scope){
13627         var data = this.snapshot || this.data;
13628         return data.filterBy(fn, scope||this);
13629     },
13630
13631     /**
13632      * Collects unique values for a particular dataIndex from this store.
13633      * @param {String} dataIndex The property to collect
13634      * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
13635      * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
13636      * @return {Array} An array of the unique values
13637      **/
13638     collect : function(dataIndex, allowNull, bypassFilter){
13639         var d = (bypassFilter === true && this.snapshot) ?
13640                 this.snapshot.items : this.data.items;
13641         var v, sv, r = [], l = {};
13642         for(var i = 0, len = d.length; i < len; i++){
13643             v = d[i].data[dataIndex];
13644             sv = String(v);
13645             if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
13646                 l[sv] = true;
13647                 r[r.length] = v;
13648             }
13649         }
13650         return r;
13651     },
13652
13653     /**
13654      * Revert to a view of the Record cache with no filtering applied.
13655      * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
13656      */
13657     clearFilter : function(suppressEvent){
13658         if(this.snapshot && this.snapshot != this.data){
13659             this.data = this.snapshot;
13660             delete this.snapshot;
13661             if(suppressEvent !== true){
13662                 this.fireEvent("datachanged", this);
13663             }
13664         }
13665     },
13666
13667     // private
13668     afterEdit : function(record){
13669         if(this.modified.indexOf(record) == -1){
13670             this.modified.push(record);
13671         }
13672         this.fireEvent("update", this, record, Roo.data.Record.EDIT);
13673     },
13674     
13675     // private
13676     afterReject : function(record){
13677         this.modified.remove(record);
13678         this.fireEvent("update", this, record, Roo.data.Record.REJECT);
13679     },
13680
13681     // private
13682     afterCommit : function(record){
13683         this.modified.remove(record);
13684         this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
13685     },
13686
13687     /**
13688      * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
13689      * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
13690      */
13691     commitChanges : function(){
13692         var m = this.modified.slice(0);
13693         this.modified = [];
13694         for(var i = 0, len = m.length; i < len; i++){
13695             m[i].commit();
13696         }
13697     },
13698
13699     /**
13700      * Cancel outstanding changes on all changed records.
13701      */
13702     rejectChanges : function(){
13703         var m = this.modified.slice(0);
13704         this.modified = [];
13705         for(var i = 0, len = m.length; i < len; i++){
13706             m[i].reject();
13707         }
13708     },
13709
13710     onMetaChange : function(meta, rtype, o){
13711         this.recordType = rtype;
13712         this.fields = rtype.prototype.fields;
13713         delete this.snapshot;
13714         this.sortInfo = meta.sortInfo || this.sortInfo;
13715         this.modified = [];
13716         this.fireEvent('metachange', this, this.reader.meta);
13717     },
13718     
13719     moveIndex : function(data, type)
13720     {
13721         var index = this.indexOf(data);
13722         
13723         var newIndex = index + type;
13724         
13725         this.remove(data);
13726         
13727         this.insert(newIndex, data);
13728         
13729     }
13730 });/*
13731  * Based on:
13732  * Ext JS Library 1.1.1
13733  * Copyright(c) 2006-2007, Ext JS, LLC.
13734  *
13735  * Originally Released Under LGPL - original licence link has changed is not relivant.
13736  *
13737  * Fork - LGPL
13738  * <script type="text/javascript">
13739  */
13740
13741 /**
13742  * @class Roo.data.SimpleStore
13743  * @extends Roo.data.Store
13744  * Small helper class to make creating Stores from Array data easier.
13745  * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
13746  * @cfg {Array} fields An array of field definition objects, or field name strings.
13747  * @cfg {Object} an existing reader (eg. copied from another store)
13748  * @cfg {Array} data The multi-dimensional array of data
13749  * @constructor
13750  * @param {Object} config
13751  */
13752 Roo.data.SimpleStore = function(config)
13753 {
13754     Roo.data.SimpleStore.superclass.constructor.call(this, {
13755         isLocal : true,
13756         reader: typeof(config.reader) != 'undefined' ? config.reader : new Roo.data.ArrayReader({
13757                 id: config.id
13758             },
13759             Roo.data.Record.create(config.fields)
13760         ),
13761         proxy : new Roo.data.MemoryProxy(config.data)
13762     });
13763     this.load();
13764 };
13765 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
13766  * Based on:
13767  * Ext JS Library 1.1.1
13768  * Copyright(c) 2006-2007, Ext JS, LLC.
13769  *
13770  * Originally Released Under LGPL - original licence link has changed is not relivant.
13771  *
13772  * Fork - LGPL
13773  * <script type="text/javascript">
13774  */
13775
13776 /**
13777 /**
13778  * @extends Roo.data.Store
13779  * @class Roo.data.JsonStore
13780  * Small helper class to make creating Stores for JSON data easier. <br/>
13781 <pre><code>
13782 var store = new Roo.data.JsonStore({
13783     url: 'get-images.php',
13784     root: 'images',
13785     fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
13786 });
13787 </code></pre>
13788  * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
13789  * JsonReader and HttpProxy (unless inline data is provided).</b>
13790  * @cfg {Array} fields An array of field definition objects, or field name strings.
13791  * @constructor
13792  * @param {Object} config
13793  */
13794 Roo.data.JsonStore = function(c){
13795     Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
13796         proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
13797         reader: new Roo.data.JsonReader(c, c.fields)
13798     }));
13799 };
13800 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
13801  * Based on:
13802  * Ext JS Library 1.1.1
13803  * Copyright(c) 2006-2007, Ext JS, LLC.
13804  *
13805  * Originally Released Under LGPL - original licence link has changed is not relivant.
13806  *
13807  * Fork - LGPL
13808  * <script type="text/javascript">
13809  */
13810
13811  
13812 Roo.data.Field = function(config){
13813     if(typeof config == "string"){
13814         config = {name: config};
13815     }
13816     Roo.apply(this, config);
13817     
13818     if(!this.type){
13819         this.type = "auto";
13820     }
13821     
13822     var st = Roo.data.SortTypes;
13823     // named sortTypes are supported, here we look them up
13824     if(typeof this.sortType == "string"){
13825         this.sortType = st[this.sortType];
13826     }
13827     
13828     // set default sortType for strings and dates
13829     if(!this.sortType){
13830         switch(this.type){
13831             case "string":
13832                 this.sortType = st.asUCString;
13833                 break;
13834             case "date":
13835                 this.sortType = st.asDate;
13836                 break;
13837             default:
13838                 this.sortType = st.none;
13839         }
13840     }
13841
13842     // define once
13843     var stripRe = /[\$,%]/g;
13844
13845     // prebuilt conversion function for this field, instead of
13846     // switching every time we're reading a value
13847     if(!this.convert){
13848         var cv, dateFormat = this.dateFormat;
13849         switch(this.type){
13850             case "":
13851             case "auto":
13852             case undefined:
13853                 cv = function(v){ return v; };
13854                 break;
13855             case "string":
13856                 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
13857                 break;
13858             case "int":
13859                 cv = function(v){
13860                     return v !== undefined && v !== null && v !== '' ?
13861                            parseInt(String(v).replace(stripRe, ""), 10) : '';
13862                     };
13863                 break;
13864             case "float":
13865                 cv = function(v){
13866                     return v !== undefined && v !== null && v !== '' ?
13867                            parseFloat(String(v).replace(stripRe, ""), 10) : ''; 
13868                     };
13869                 break;
13870             case "bool":
13871             case "boolean":
13872                 cv = function(v){ return v === true || v === "true" || v == 1; };
13873                 break;
13874             case "date":
13875                 cv = function(v){
13876                     if(!v){
13877                         return '';
13878                     }
13879                     if(v instanceof Date){
13880                         return v;
13881                     }
13882                     if(dateFormat){
13883                         if(dateFormat == "timestamp"){
13884                             return new Date(v*1000);
13885                         }
13886                         return Date.parseDate(v, dateFormat);
13887                     }
13888                     var parsed = Date.parse(v);
13889                     return parsed ? new Date(parsed) : null;
13890                 };
13891              break;
13892             
13893         }
13894         this.convert = cv;
13895     }
13896 };
13897
13898 Roo.data.Field.prototype = {
13899     dateFormat: null,
13900     defaultValue: "",
13901     mapping: null,
13902     sortType : null,
13903     sortDir : "ASC"
13904 };/*
13905  * Based on:
13906  * Ext JS Library 1.1.1
13907  * Copyright(c) 2006-2007, Ext JS, LLC.
13908  *
13909  * Originally Released Under LGPL - original licence link has changed is not relivant.
13910  *
13911  * Fork - LGPL
13912  * <script type="text/javascript">
13913  */
13914  
13915 // Base class for reading structured data from a data source.  This class is intended to be
13916 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
13917
13918 /**
13919  * @class Roo.data.DataReader
13920  * Base class for reading structured data from a data source.  This class is intended to be
13921  * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
13922  */
13923
13924 Roo.data.DataReader = function(meta, recordType){
13925     
13926     this.meta = meta;
13927     
13928     this.recordType = recordType instanceof Array ? 
13929         Roo.data.Record.create(recordType) : recordType;
13930 };
13931
13932 Roo.data.DataReader.prototype = {
13933     
13934     
13935     readerType : 'Data',
13936      /**
13937      * Create an empty record
13938      * @param {Object} data (optional) - overlay some values
13939      * @return {Roo.data.Record} record created.
13940      */
13941     newRow :  function(d) {
13942         var da =  {};
13943         this.recordType.prototype.fields.each(function(c) {
13944             switch( c.type) {
13945                 case 'int' : da[c.name] = 0; break;
13946                 case 'date' : da[c.name] = new Date(); break;
13947                 case 'float' : da[c.name] = 0.0; break;
13948                 case 'boolean' : da[c.name] = false; break;
13949                 default : da[c.name] = ""; break;
13950             }
13951             
13952         });
13953         return new this.recordType(Roo.apply(da, d));
13954     }
13955     
13956     
13957 };/*
13958  * Based on:
13959  * Ext JS Library 1.1.1
13960  * Copyright(c) 2006-2007, Ext JS, LLC.
13961  *
13962  * Originally Released Under LGPL - original licence link has changed is not relivant.
13963  *
13964  * Fork - LGPL
13965  * <script type="text/javascript">
13966  */
13967
13968 /**
13969  * @class Roo.data.DataProxy
13970  * @extends Roo.data.Observable
13971  * This class is an abstract base class for implementations which provide retrieval of
13972  * unformatted data objects.<br>
13973  * <p>
13974  * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
13975  * (of the appropriate type which knows how to parse the data object) to provide a block of
13976  * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
13977  * <p>
13978  * Custom implementations must implement the load method as described in
13979  * {@link Roo.data.HttpProxy#load}.
13980  */
13981 Roo.data.DataProxy = function(){
13982     this.addEvents({
13983         /**
13984          * @event beforeload
13985          * Fires before a network request is made to retrieve a data object.
13986          * @param {Object} This DataProxy object.
13987          * @param {Object} params The params parameter to the load function.
13988          */
13989         beforeload : true,
13990         /**
13991          * @event load
13992          * Fires before the load method's callback is called.
13993          * @param {Object} This DataProxy object.
13994          * @param {Object} o The data object.
13995          * @param {Object} arg The callback argument object passed to the load function.
13996          */
13997         load : true,
13998         /**
13999          * @event loadexception
14000          * Fires if an Exception occurs during data retrieval.
14001          * @param {Object} This DataProxy object.
14002          * @param {Object} o The data object.
14003          * @param {Object} arg The callback argument object passed to the load function.
14004          * @param {Object} e The Exception.
14005          */
14006         loadexception : true
14007     });
14008     Roo.data.DataProxy.superclass.constructor.call(this);
14009 };
14010
14011 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
14012
14013     /**
14014      * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
14015      */
14016 /*
14017  * Based on:
14018  * Ext JS Library 1.1.1
14019  * Copyright(c) 2006-2007, Ext JS, LLC.
14020  *
14021  * Originally Released Under LGPL - original licence link has changed is not relivant.
14022  *
14023  * Fork - LGPL
14024  * <script type="text/javascript">
14025  */
14026 /**
14027  * @class Roo.data.MemoryProxy
14028  * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
14029  * to the Reader when its load method is called.
14030  * @constructor
14031  * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
14032  */
14033 Roo.data.MemoryProxy = function(data){
14034     if (data.data) {
14035         data = data.data;
14036     }
14037     Roo.data.MemoryProxy.superclass.constructor.call(this);
14038     this.data = data;
14039 };
14040
14041 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
14042     
14043     /**
14044      * Load data from the requested source (in this case an in-memory
14045      * data object passed to the constructor), read the data object into
14046      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
14047      * process that block using the passed callback.
14048      * @param {Object} params This parameter is not used by the MemoryProxy class.
14049      * @param {Roo.data.DataReader} reader The Reader object which converts the data
14050      * object into a block of Roo.data.Records.
14051      * @param {Function} callback The function into which to pass the block of Roo.data.records.
14052      * The function must be passed <ul>
14053      * <li>The Record block object</li>
14054      * <li>The "arg" argument from the load function</li>
14055      * <li>A boolean success indicator</li>
14056      * </ul>
14057      * @param {Object} scope The scope in which to call the callback
14058      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
14059      */
14060     load : function(params, reader, callback, scope, arg){
14061         params = params || {};
14062         var result;
14063         try {
14064             result = reader.readRecords(params.data ? params.data :this.data);
14065         }catch(e){
14066             this.fireEvent("loadexception", this, arg, null, e);
14067             callback.call(scope, null, arg, false);
14068             return;
14069         }
14070         callback.call(scope, result, arg, true);
14071     },
14072     
14073     // private
14074     update : function(params, records){
14075         
14076     }
14077 });/*
14078  * Based on:
14079  * Ext JS Library 1.1.1
14080  * Copyright(c) 2006-2007, Ext JS, LLC.
14081  *
14082  * Originally Released Under LGPL - original licence link has changed is not relivant.
14083  *
14084  * Fork - LGPL
14085  * <script type="text/javascript">
14086  */
14087 /**
14088  * @class Roo.data.HttpProxy
14089  * @extends Roo.data.DataProxy
14090  * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
14091  * configured to reference a certain URL.<br><br>
14092  * <p>
14093  * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
14094  * from which the running page was served.<br><br>
14095  * <p>
14096  * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
14097  * <p>
14098  * Be aware that to enable the browser to parse an XML document, the server must set
14099  * the Content-Type header in the HTTP response to "text/xml".
14100  * @constructor
14101  * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
14102  * an {@link Roo.data.Connection} object.  If a Connection config is passed, the singleton {@link Roo.Ajax} object
14103  * will be used to make the request.
14104  */
14105 Roo.data.HttpProxy = function(conn){
14106     Roo.data.HttpProxy.superclass.constructor.call(this);
14107     // is conn a conn config or a real conn?
14108     this.conn = conn;
14109     this.useAjax = !conn || !conn.events;
14110   
14111 };
14112
14113 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
14114     // thse are take from connection...
14115     
14116     /**
14117      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
14118      */
14119     /**
14120      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
14121      * extra parameters to each request made by this object. (defaults to undefined)
14122      */
14123     /**
14124      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
14125      *  to each request made by this object. (defaults to undefined)
14126      */
14127     /**
14128      * @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)
14129      */
14130     /**
14131      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
14132      */
14133      /**
14134      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
14135      * @type Boolean
14136      */
14137   
14138
14139     /**
14140      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
14141      * @type Boolean
14142      */
14143     /**
14144      * Return the {@link Roo.data.Connection} object being used by this Proxy.
14145      * @return {Connection} The Connection object. This object may be used to subscribe to events on
14146      * a finer-grained basis than the DataProxy events.
14147      */
14148     getConnection : function(){
14149         return this.useAjax ? Roo.Ajax : this.conn;
14150     },
14151
14152     /**
14153      * Load data from the configured {@link Roo.data.Connection}, read the data object into
14154      * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
14155      * process that block using the passed callback.
14156      * @param {Object} params An object containing properties which are to be used as HTTP parameters
14157      * for the request to the remote server.
14158      * @param {Roo.data.DataReader} reader The Reader object which converts the data
14159      * object into a block of Roo.data.Records.
14160      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
14161      * The function must be passed <ul>
14162      * <li>The Record block object</li>
14163      * <li>The "arg" argument from the load function</li>
14164      * <li>A boolean success indicator</li>
14165      * </ul>
14166      * @param {Object} scope The scope in which to call the callback
14167      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
14168      */
14169     load : function(params, reader, callback, scope, arg){
14170         if(this.fireEvent("beforeload", this, params) !== false){
14171             var  o = {
14172                 params : params || {},
14173                 request: {
14174                     callback : callback,
14175                     scope : scope,
14176                     arg : arg
14177                 },
14178                 reader: reader,
14179                 callback : this.loadResponse,
14180                 scope: this
14181             };
14182             if(this.useAjax){
14183                 Roo.applyIf(o, this.conn);
14184                 if(this.activeRequest){
14185                     Roo.Ajax.abort(this.activeRequest);
14186                 }
14187                 this.activeRequest = Roo.Ajax.request(o);
14188             }else{
14189                 this.conn.request(o);
14190             }
14191         }else{
14192             callback.call(scope||this, null, arg, false);
14193         }
14194     },
14195
14196     // private
14197     loadResponse : function(o, success, response){
14198         delete this.activeRequest;
14199         if(!success){
14200             this.fireEvent("loadexception", this, o, response);
14201             o.request.callback.call(o.request.scope, null, o.request.arg, false);
14202             return;
14203         }
14204         var result;
14205         try {
14206             result = o.reader.read(response);
14207         }catch(e){
14208             this.fireEvent("loadexception", this, o, response, e);
14209             o.request.callback.call(o.request.scope, null, o.request.arg, false);
14210             return;
14211         }
14212         
14213         this.fireEvent("load", this, o, o.request.arg);
14214         o.request.callback.call(o.request.scope, result, o.request.arg, true);
14215     },
14216
14217     // private
14218     update : function(dataSet){
14219
14220     },
14221
14222     // private
14223     updateResponse : function(dataSet){
14224
14225     }
14226 });/*
14227  * Based on:
14228  * Ext JS Library 1.1.1
14229  * Copyright(c) 2006-2007, Ext JS, LLC.
14230  *
14231  * Originally Released Under LGPL - original licence link has changed is not relivant.
14232  *
14233  * Fork - LGPL
14234  * <script type="text/javascript">
14235  */
14236
14237 /**
14238  * @class Roo.data.ScriptTagProxy
14239  * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
14240  * other than the originating domain of the running page.<br><br>
14241  * <p>
14242  * <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
14243  * of the running page, you must use this class, rather than DataProxy.</em><br><br>
14244  * <p>
14245  * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
14246  * source code that is used as the source inside a &lt;script> tag.<br><br>
14247  * <p>
14248  * In order for the browser to process the returned data, the server must wrap the data object
14249  * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
14250  * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
14251  * depending on whether the callback name was passed:
14252  * <p>
14253  * <pre><code>
14254 boolean scriptTag = false;
14255 String cb = request.getParameter("callback");
14256 if (cb != null) {
14257     scriptTag = true;
14258     response.setContentType("text/javascript");
14259 } else {
14260     response.setContentType("application/x-json");
14261 }
14262 Writer out = response.getWriter();
14263 if (scriptTag) {
14264     out.write(cb + "(");
14265 }
14266 out.print(dataBlock.toJsonString());
14267 if (scriptTag) {
14268     out.write(");");
14269 }
14270 </pre></code>
14271  *
14272  * @constructor
14273  * @param {Object} config A configuration object.
14274  */
14275 Roo.data.ScriptTagProxy = function(config){
14276     Roo.data.ScriptTagProxy.superclass.constructor.call(this);
14277     Roo.apply(this, config);
14278     this.head = document.getElementsByTagName("head")[0];
14279 };
14280
14281 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
14282
14283 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
14284     /**
14285      * @cfg {String} url The URL from which to request the data object.
14286      */
14287     /**
14288      * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
14289      */
14290     timeout : 30000,
14291     /**
14292      * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
14293      * the server the name of the callback function set up by the load call to process the returned data object.
14294      * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
14295      * javascript output which calls this named function passing the data object as its only parameter.
14296      */
14297     callbackParam : "callback",
14298     /**
14299      *  @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
14300      * name to the request.
14301      */
14302     nocache : true,
14303
14304     /**
14305      * Load data from the configured URL, read the data object into
14306      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
14307      * process that block using the passed callback.
14308      * @param {Object} params An object containing properties which are to be used as HTTP parameters
14309      * for the request to the remote server.
14310      * @param {Roo.data.DataReader} reader The Reader object which converts the data
14311      * object into a block of Roo.data.Records.
14312      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
14313      * The function must be passed <ul>
14314      * <li>The Record block object</li>
14315      * <li>The "arg" argument from the load function</li>
14316      * <li>A boolean success indicator</li>
14317      * </ul>
14318      * @param {Object} scope The scope in which to call the callback
14319      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
14320      */
14321     load : function(params, reader, callback, scope, arg){
14322         if(this.fireEvent("beforeload", this, params) !== false){
14323
14324             var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
14325
14326             var url = this.url;
14327             url += (url.indexOf("?") != -1 ? "&" : "?") + p;
14328             if(this.nocache){
14329                 url += "&_dc=" + (new Date().getTime());
14330             }
14331             var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
14332             var trans = {
14333                 id : transId,
14334                 cb : "stcCallback"+transId,
14335                 scriptId : "stcScript"+transId,
14336                 params : params,
14337                 arg : arg,
14338                 url : url,
14339                 callback : callback,
14340                 scope : scope,
14341                 reader : reader
14342             };
14343             var conn = this;
14344
14345             window[trans.cb] = function(o){
14346                 conn.handleResponse(o, trans);
14347             };
14348
14349             url += String.format("&{0}={1}", this.callbackParam, trans.cb);
14350
14351             if(this.autoAbort !== false){
14352                 this.abort();
14353             }
14354
14355             trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
14356
14357             var script = document.createElement("script");
14358             script.setAttribute("src", url);
14359             script.setAttribute("type", "text/javascript");
14360             script.setAttribute("id", trans.scriptId);
14361             this.head.appendChild(script);
14362
14363             this.trans = trans;
14364         }else{
14365             callback.call(scope||this, null, arg, false);
14366         }
14367     },
14368
14369     // private
14370     isLoading : function(){
14371         return this.trans ? true : false;
14372     },
14373
14374     /**
14375      * Abort the current server request.
14376      */
14377     abort : function(){
14378         if(this.isLoading()){
14379             this.destroyTrans(this.trans);
14380         }
14381     },
14382
14383     // private
14384     destroyTrans : function(trans, isLoaded){
14385         this.head.removeChild(document.getElementById(trans.scriptId));
14386         clearTimeout(trans.timeoutId);
14387         if(isLoaded){
14388             window[trans.cb] = undefined;
14389             try{
14390                 delete window[trans.cb];
14391             }catch(e){}
14392         }else{
14393             // if hasn't been loaded, wait for load to remove it to prevent script error
14394             window[trans.cb] = function(){
14395                 window[trans.cb] = undefined;
14396                 try{
14397                     delete window[trans.cb];
14398                 }catch(e){}
14399             };
14400         }
14401     },
14402
14403     // private
14404     handleResponse : function(o, trans){
14405         this.trans = false;
14406         this.destroyTrans(trans, true);
14407         var result;
14408         try {
14409             result = trans.reader.readRecords(o);
14410         }catch(e){
14411             this.fireEvent("loadexception", this, o, trans.arg, e);
14412             trans.callback.call(trans.scope||window, null, trans.arg, false);
14413             return;
14414         }
14415         this.fireEvent("load", this, o, trans.arg);
14416         trans.callback.call(trans.scope||window, result, trans.arg, true);
14417     },
14418
14419     // private
14420     handleFailure : function(trans){
14421         this.trans = false;
14422         this.destroyTrans(trans, false);
14423         this.fireEvent("loadexception", this, null, trans.arg);
14424         trans.callback.call(trans.scope||window, null, trans.arg, false);
14425     }
14426 });/*
14427  * Based on:
14428  * Ext JS Library 1.1.1
14429  * Copyright(c) 2006-2007, Ext JS, LLC.
14430  *
14431  * Originally Released Under LGPL - original licence link has changed is not relivant.
14432  *
14433  * Fork - LGPL
14434  * <script type="text/javascript">
14435  */
14436
14437 /**
14438  * @class Roo.data.JsonReader
14439  * @extends Roo.data.DataReader
14440  * Data reader class to create an Array of Roo.data.Record objects from a JSON response
14441  * based on mappings in a provided Roo.data.Record constructor.
14442  * 
14443  * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
14444  * in the reply previously. 
14445  * 
14446  * <p>
14447  * Example code:
14448  * <pre><code>
14449 var RecordDef = Roo.data.Record.create([
14450     {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
14451     {name: 'occupation'}                 // This field will use "occupation" as the mapping.
14452 ]);
14453 var myReader = new Roo.data.JsonReader({
14454     totalProperty: "results",    // The property which contains the total dataset size (optional)
14455     root: "rows",                // The property which contains an Array of row objects
14456     id: "id"                     // The property within each row object that provides an ID for the record (optional)
14457 }, RecordDef);
14458 </code></pre>
14459  * <p>
14460  * This would consume a JSON file like this:
14461  * <pre><code>
14462 { 'results': 2, 'rows': [
14463     { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
14464     { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
14465 }
14466 </code></pre>
14467  * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
14468  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
14469  * paged from the remote server.
14470  * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
14471  * @cfg {String} root name of the property which contains the Array of row objects.
14472  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
14473  * @cfg {Array} fields Array of field definition objects
14474  * @constructor
14475  * Create a new JsonReader
14476  * @param {Object} meta Metadata configuration options
14477  * @param {Object} recordType Either an Array of field definition objects,
14478  * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
14479  */
14480 Roo.data.JsonReader = function(meta, recordType){
14481     
14482     meta = meta || {};
14483     // set some defaults:
14484     Roo.applyIf(meta, {
14485         totalProperty: 'total',
14486         successProperty : 'success',
14487         root : 'data',
14488         id : 'id'
14489     });
14490     
14491     Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
14492 };
14493 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
14494     
14495     readerType : 'Json',
14496     
14497     /**
14498      * @prop {Boolean} metaFromRemote  - if the meta data was loaded from the remote source.
14499      * Used by Store query builder to append _requestMeta to params.
14500      * 
14501      */
14502     metaFromRemote : false,
14503     /**
14504      * This method is only used by a DataProxy which has retrieved data from a remote server.
14505      * @param {Object} response The XHR object which contains the JSON data in its responseText.
14506      * @return {Object} data A data block which is used by an Roo.data.Store object as
14507      * a cache of Roo.data.Records.
14508      */
14509     read : function(response){
14510         var json = response.responseText;
14511        
14512         var o = /* eval:var:o */ eval("("+json+")");
14513         if(!o) {
14514             throw {message: "JsonReader.read: Json object not found"};
14515         }
14516         
14517         if(o.metaData){
14518             
14519             delete this.ef;
14520             this.metaFromRemote = true;
14521             this.meta = o.metaData;
14522             this.recordType = Roo.data.Record.create(o.metaData.fields);
14523             this.onMetaChange(this.meta, this.recordType, o);
14524         }
14525         return this.readRecords(o);
14526     },
14527
14528     // private function a store will implement
14529     onMetaChange : function(meta, recordType, o){
14530
14531     },
14532
14533     /**
14534          * @ignore
14535          */
14536     simpleAccess: function(obj, subsc) {
14537         return obj[subsc];
14538     },
14539
14540         /**
14541          * @ignore
14542          */
14543     getJsonAccessor: function(){
14544         var re = /[\[\.]/;
14545         return function(expr) {
14546             try {
14547                 return(re.test(expr))
14548                     ? new Function("obj", "return obj." + expr)
14549                     : function(obj){
14550                         return obj[expr];
14551                     };
14552             } catch(e){}
14553             return Roo.emptyFn;
14554         };
14555     }(),
14556
14557     /**
14558      * Create a data block containing Roo.data.Records from an XML document.
14559      * @param {Object} o An object which contains an Array of row objects in the property specified
14560      * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
14561      * which contains the total size of the dataset.
14562      * @return {Object} data A data block which is used by an Roo.data.Store object as
14563      * a cache of Roo.data.Records.
14564      */
14565     readRecords : function(o){
14566         /**
14567          * After any data loads, the raw JSON data is available for further custom processing.
14568          * @type Object
14569          */
14570         this.o = o;
14571         var s = this.meta, Record = this.recordType,
14572             f = Record ? Record.prototype.fields : null, fi = f ? f.items : [], fl = f ? f.length : 0;
14573
14574 //      Generate extraction functions for the totalProperty, the root, the id, and for each field
14575         if (!this.ef) {
14576             if(s.totalProperty) {
14577                     this.getTotal = this.getJsonAccessor(s.totalProperty);
14578                 }
14579                 if(s.successProperty) {
14580                     this.getSuccess = this.getJsonAccessor(s.successProperty);
14581                 }
14582                 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
14583                 if (s.id) {
14584                         var g = this.getJsonAccessor(s.id);
14585                         this.getId = function(rec) {
14586                                 var r = g(rec);  
14587                                 return (r === undefined || r === "") ? null : r;
14588                         };
14589                 } else {
14590                         this.getId = function(){return null;};
14591                 }
14592             this.ef = [];
14593             for(var jj = 0; jj < fl; jj++){
14594                 f = fi[jj];
14595                 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
14596                 this.ef[jj] = this.getJsonAccessor(map);
14597             }
14598         }
14599
14600         var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
14601         if(s.totalProperty){
14602             var vt = parseInt(this.getTotal(o), 10);
14603             if(!isNaN(vt)){
14604                 totalRecords = vt;
14605             }
14606         }
14607         if(s.successProperty){
14608             var vs = this.getSuccess(o);
14609             if(vs === false || vs === 'false'){
14610                 success = false;
14611             }
14612         }
14613         var records = [];
14614         for(var i = 0; i < c; i++){
14615                 var n = root[i];
14616             var values = {};
14617             var id = this.getId(n);
14618             for(var j = 0; j < fl; j++){
14619                 f = fi[j];
14620             var v = this.ef[j](n);
14621             if (!f.convert) {
14622                 Roo.log('missing convert for ' + f.name);
14623                 Roo.log(f);
14624                 continue;
14625             }
14626             values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
14627             }
14628             var record = new Record(values, id);
14629             record.json = n;
14630             records[i] = record;
14631         }
14632         return {
14633             raw : o,
14634             success : success,
14635             records : records,
14636             totalRecords : totalRecords
14637         };
14638     },
14639     // used when loading children.. @see loadDataFromChildren
14640     toLoadData: function(rec)
14641     {
14642         // expect rec just to be an array.. eg [a,b,c, [...] << cn ]
14643         var data = typeof(rec.data.cn) == 'undefined' ? [] : rec.data.cn;
14644         return { data : data, total : data.length };
14645         
14646     }
14647 });/*
14648  * Based on:
14649  * Ext JS Library 1.1.1
14650  * Copyright(c) 2006-2007, Ext JS, LLC.
14651  *
14652  * Originally Released Under LGPL - original licence link has changed is not relivant.
14653  *
14654  * Fork - LGPL
14655  * <script type="text/javascript">
14656  */
14657
14658 /**
14659  * @class Roo.data.ArrayReader
14660  * @extends Roo.data.DataReader
14661  * Data reader class to create an Array of Roo.data.Record objects from an Array.
14662  * Each element of that Array represents a row of data fields. The
14663  * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
14664  * of the field definition if it exists, or the field's ordinal position in the definition.<br>
14665  * <p>
14666  * Example code:.
14667  * <pre><code>
14668 var RecordDef = Roo.data.Record.create([
14669     {name: 'name', mapping: 1},         // "mapping" only needed if an "id" field is present which
14670     {name: 'occupation', mapping: 2}    // precludes using the ordinal position as the index.
14671 ]);
14672 var myReader = new Roo.data.ArrayReader({
14673     id: 0                     // The subscript within row Array that provides an ID for the Record (optional)
14674 }, RecordDef);
14675 </code></pre>
14676  * <p>
14677  * This would consume an Array like this:
14678  * <pre><code>
14679 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
14680   </code></pre>
14681  
14682  * @constructor
14683  * Create a new JsonReader
14684  * @param {Object} meta Metadata configuration options.
14685  * @param {Object|Array} recordType Either an Array of field definition objects
14686  * 
14687  * @cfg {Array} fields Array of field definition objects
14688  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
14689  * as specified to {@link Roo.data.Record#create},
14690  * or an {@link Roo.data.Record} object
14691  *
14692  * 
14693  * created using {@link Roo.data.Record#create}.
14694  */
14695 Roo.data.ArrayReader = function(meta, recordType)
14696 {    
14697     Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType||meta.fields);
14698 };
14699
14700 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
14701     
14702       /**
14703      * Create a data block containing Roo.data.Records from an XML document.
14704      * @param {Object} o An Array of row objects which represents the dataset.
14705      * @return {Object} A data block which is used by an {@link Roo.data.Store} object as
14706      * a cache of Roo.data.Records.
14707      */
14708     readRecords : function(o)
14709     {
14710         var sid = this.meta ? this.meta.id : null;
14711         var recordType = this.recordType, fields = recordType.prototype.fields;
14712         var records = [];
14713         var root = o;
14714         for(var i = 0; i < root.length; i++){
14715                 var n = root[i];
14716             var values = {};
14717             var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
14718             for(var j = 0, jlen = fields.length; j < jlen; j++){
14719                 var f = fields.items[j];
14720                 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
14721                 var v = n[k] !== undefined ? n[k] : f.defaultValue;
14722                 v = f.convert(v);
14723                 values[f.name] = v;
14724             }
14725             var record = new recordType(values, id);
14726             record.json = n;
14727             records[records.length] = record;
14728         }
14729         return {
14730             records : records,
14731             totalRecords : records.length
14732         };
14733     },
14734     // used when loading children.. @see loadDataFromChildren
14735     toLoadData: function(rec)
14736     {
14737         // expect rec just to be an array.. eg [a,b,c, [...] << cn ]
14738         return typeof(rec.data.cn) == 'undefined' ? [] : rec.data.cn;
14739         
14740     }
14741     
14742     
14743 });/*
14744  * - LGPL
14745  * * 
14746  */
14747
14748 /**
14749  * @class Roo.bootstrap.ComboBox
14750  * @extends Roo.bootstrap.TriggerField
14751  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
14752  * @cfg {Boolean} append (true|false) default false
14753  * @cfg {Boolean} autoFocus (true|false) auto focus the first item, default true
14754  * @cfg {Boolean} tickable ComboBox with tickable selections (true|false), default false
14755  * @cfg {Boolean} triggerList trigger show the list or not (true|false) default true
14756  * @cfg {Boolean} showToggleBtn show toggle button or not (true|false) default true
14757  * @cfg {String} btnPosition set the position of the trigger button (left | right) default right
14758  * @cfg {Boolean} animate default true
14759  * @cfg {Boolean} emptyResultText only for touch device
14760  * @cfg {String} triggerText multiple combobox trigger button text default 'Select'
14761  * @cfg {String} emptyTitle default ''
14762  * @cfg {Number} width fixed with? experimental
14763  * @constructor
14764  * Create a new ComboBox.
14765  * @param {Object} config Configuration options
14766  */
14767 Roo.bootstrap.ComboBox = function(config){
14768     Roo.bootstrap.ComboBox.superclass.constructor.call(this, config);
14769     this.addEvents({
14770         /**
14771          * @event expand
14772          * Fires when the dropdown list is expanded
14773         * @param {Roo.bootstrap.ComboBox} combo This combo box
14774         */
14775         'expand' : true,
14776         /**
14777          * @event collapse
14778          * Fires when the dropdown list is collapsed
14779         * @param {Roo.bootstrap.ComboBox} combo This combo box
14780         */
14781         'collapse' : true,
14782         /**
14783          * @event beforeselect
14784          * Fires before a list item is selected. Return false to cancel the selection.
14785         * @param {Roo.bootstrap.ComboBox} combo This combo box
14786         * @param {Roo.data.Record} record The data record returned from the underlying store
14787         * @param {Number} index The index of the selected item in the dropdown list
14788         */
14789         'beforeselect' : true,
14790         /**
14791          * @event select
14792          * Fires when a list item is selected
14793         * @param {Roo.bootstrap.ComboBox} combo This combo box
14794         * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
14795         * @param {Number} index The index of the selected item in the dropdown list
14796         */
14797         'select' : true,
14798         /**
14799          * @event beforequery
14800          * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
14801          * The event object passed has these properties:
14802         * @param {Roo.bootstrap.ComboBox} combo This combo box
14803         * @param {String} query The query
14804         * @param {Boolean} forceAll true to force "all" query
14805         * @param {Boolean} cancel true to cancel the query
14806         * @param {Object} e The query event object
14807         */
14808         'beforequery': true,
14809          /**
14810          * @event add
14811          * Fires when the 'add' icon is pressed (add a listener to enable add button)
14812         * @param {Roo.bootstrap.ComboBox} combo This combo box
14813         */
14814         'add' : true,
14815         /**
14816          * @event edit
14817          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
14818         * @param {Roo.bootstrap.ComboBox} combo This combo box
14819         * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
14820         */
14821         'edit' : true,
14822         /**
14823          * @event remove
14824          * Fires when the remove value from the combobox array
14825         * @param {Roo.bootstrap.ComboBox} combo This combo box
14826         */
14827         'remove' : true,
14828         /**
14829          * @event afterremove
14830          * Fires when the remove value from the combobox array
14831         * @param {Roo.bootstrap.ComboBox} combo This combo box
14832         */
14833         'afterremove' : true,
14834         /**
14835          * @event specialfilter
14836          * Fires when specialfilter
14837             * @param {Roo.bootstrap.ComboBox} combo This combo box
14838             */
14839         'specialfilter' : true,
14840         /**
14841          * @event tick
14842          * Fires when tick the element
14843             * @param {Roo.bootstrap.ComboBox} combo This combo box
14844             */
14845         'tick' : true,
14846         /**
14847          * @event touchviewdisplay
14848          * Fires when touch view require special display (default is using displayField)
14849             * @param {Roo.bootstrap.ComboBox} combo This combo box
14850             * @param {Object} cfg set html .
14851             */
14852         'touchviewdisplay' : true
14853         
14854     });
14855     
14856     this.item = [];
14857     this.tickItems = [];
14858     
14859     this.selectedIndex = -1;
14860     if(this.mode == 'local'){
14861         if(config.queryDelay === undefined){
14862             this.queryDelay = 10;
14863         }
14864         if(config.minChars === undefined){
14865             this.minChars = 0;
14866         }
14867     }
14868 };
14869
14870 Roo.extend(Roo.bootstrap.ComboBox, Roo.bootstrap.TriggerField, {
14871      
14872     /**
14873      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
14874      * rendering into an Roo.Editor, defaults to false)
14875      */
14876     /**
14877      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
14878      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
14879      */
14880     /**
14881      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
14882      */
14883     /**
14884      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
14885      * the dropdown list (defaults to undefined, with no header element)
14886      */
14887
14888      /**
14889      * @cfg {String/Roo.Template} tpl The template to use to render the output default is  '<a class="dropdown-item" href="#">{' + this.displayField + '}</a>' 
14890      */
14891      
14892      /**
14893      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
14894      */
14895     listWidth: undefined,
14896     /**
14897      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
14898      * mode = 'remote' or 'text' if mode = 'local')
14899      */
14900     displayField: undefined,
14901     
14902     /**
14903      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
14904      * mode = 'remote' or 'value' if mode = 'local'). 
14905      * Note: use of a valueField requires the user make a selection
14906      * in order for a value to be mapped.
14907      */
14908     valueField: undefined,
14909     /**
14910      * @cfg {String} modalTitle The title of the dialog that pops up on mobile views.
14911      */
14912     modalTitle : '',
14913     
14914     /**
14915      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
14916      * field's data value (defaults to the underlying DOM element's name)
14917      */
14918     hiddenName: undefined,
14919     /**
14920      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
14921      */
14922     listClass: '',
14923     /**
14924      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
14925      */
14926     selectedClass: 'active',
14927     
14928     /**
14929      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
14930      */
14931     shadow:'sides',
14932     /**
14933      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
14934      * anchor positions (defaults to 'tl-bl')
14935      */
14936     listAlign: 'tl-bl?',
14937     /**
14938      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
14939      */
14940     maxHeight: 300,
14941     /**
14942      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
14943      * query specified by the allQuery config option (defaults to 'query')
14944      */
14945     triggerAction: 'query',
14946     /**
14947      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
14948      * (defaults to 4, does not apply if editable = false)
14949      */
14950     minChars : 4,
14951     /**
14952      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
14953      * delay (typeAheadDelay) if it matches a known value (defaults to false)
14954      */
14955     typeAhead: false,
14956     /**
14957      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
14958      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
14959      */
14960     queryDelay: 500,
14961     /**
14962      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
14963      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
14964      */
14965     pageSize: 0,
14966     /**
14967      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
14968      * when editable = true (defaults to false)
14969      */
14970     selectOnFocus:false,
14971     /**
14972      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
14973      */
14974     queryParam: 'query',
14975     /**
14976      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
14977      * when mode = 'remote' (defaults to 'Loading...')
14978      */
14979     loadingText: 'Loading...',
14980     /**
14981      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
14982      */
14983     resizable: false,
14984     /**
14985      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
14986      */
14987     handleHeight : 8,
14988     /**
14989      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
14990      * traditional select (defaults to true)
14991      */
14992     editable: true,
14993     /**
14994      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
14995      */
14996     allQuery: '',
14997     /**
14998      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
14999      */
15000     mode: 'remote',
15001     /**
15002      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
15003      * listWidth has a higher value)
15004      */
15005     minListWidth : 70,
15006     /**
15007      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
15008      * allow the user to set arbitrary text into the field (defaults to false)
15009      */
15010     forceSelection:false,
15011     /**
15012      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
15013      * if typeAhead = true (defaults to 250)
15014      */
15015     typeAheadDelay : 250,
15016     /**
15017      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
15018      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
15019      */
15020     valueNotFoundText : undefined,
15021     /**
15022      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
15023      */
15024     blockFocus : false,
15025     
15026     /**
15027      * @cfg {Boolean} disableClear Disable showing of clear button.
15028      */
15029     disableClear : false,
15030     /**
15031      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
15032      */
15033     alwaysQuery : false,
15034     
15035     /**
15036      * @cfg {Boolean} multiple  (true|false) ComboBobArray, default false
15037      */
15038     multiple : false,
15039     
15040     /**
15041      * @cfg {String} invalidClass DEPRICATED - uses BS4 is-valid now
15042      */
15043     invalidClass : "has-warning",
15044     
15045     /**
15046      * @cfg {String} validClass DEPRICATED - uses BS4 is-valid now
15047      */
15048     validClass : "has-success",
15049     
15050     /**
15051      * @cfg {Boolean} specialFilter (true|false) special filter default false
15052      */
15053     specialFilter : false,
15054     
15055     /**
15056      * @cfg {Boolean} mobileTouchView (true|false) show mobile touch view when using a mobile default true
15057      */
15058     mobileTouchView : true,
15059     
15060     /**
15061      * @cfg {Boolean} useNativeIOS (true|false) render it as classic select for ios, not support dynamic load data (default false)
15062      */
15063     useNativeIOS : false,
15064     
15065     /**
15066      * @cfg {Boolean} mobile_restrict_height (true|false) restrict height for touch view
15067      */
15068     mobile_restrict_height : false,
15069     
15070     ios_options : false,
15071     
15072     //private
15073     addicon : false,
15074     editicon: false,
15075     
15076     page: 0,
15077     hasQuery: false,
15078     append: false,
15079     loadNext: false,
15080     autoFocus : true,
15081     tickable : false,
15082     btnPosition : 'right',
15083     triggerList : true,
15084     showToggleBtn : true,
15085     animate : true,
15086     emptyResultText: 'Empty',
15087     triggerText : 'Select',
15088     emptyTitle : '',
15089     width : false,
15090     
15091     // element that contains real text value.. (when hidden is used..)
15092     
15093     getAutoCreate : function()
15094     {   
15095         var cfg = false;
15096         //render
15097         /*
15098          * Render classic select for iso
15099          */
15100         
15101         if(Roo.isIOS && this.useNativeIOS){
15102             cfg = this.getAutoCreateNativeIOS();
15103             return cfg;
15104         }
15105         
15106         /*
15107          * Touch Devices
15108          */
15109         
15110         if(Roo.isTouch && this.mobileTouchView){
15111             cfg = this.getAutoCreateTouchView();
15112             return cfg;;
15113         }
15114         
15115         /*
15116          *  Normal ComboBox
15117          */
15118         if(!this.tickable){
15119             cfg = Roo.bootstrap.ComboBox.superclass.getAutoCreate.call(this);
15120             return cfg;
15121         }
15122         
15123         /*
15124          *  ComboBox with tickable selections
15125          */
15126              
15127         var align = this.labelAlign || this.parentLabelAlign();
15128         
15129         cfg = {
15130             cls : 'form-group roo-combobox-tickable' //input-group
15131         };
15132         
15133         var btn_text_select = '';
15134         var btn_text_done = '';
15135         var btn_text_cancel = '';
15136         
15137         if (this.btn_text_show) {
15138             btn_text_select = 'Select';
15139             btn_text_done = 'Done';
15140             btn_text_cancel = 'Cancel'; 
15141         }
15142         
15143         var buttons = {
15144             tag : 'div',
15145             cls : 'tickable-buttons',
15146             cn : [
15147                 {
15148                     tag : 'button',
15149                     type : 'button',
15150                     cls : 'btn btn-link btn-edit pull-' + this.btnPosition,
15151                     //html : this.triggerText
15152                     html: btn_text_select
15153                 },
15154                 {
15155                     tag : 'button',
15156                     type : 'button',
15157                     name : 'ok',
15158                     cls : 'btn btn-link btn-ok pull-' + this.btnPosition,
15159                     //html : 'Done'
15160                     html: btn_text_done
15161                 },
15162                 {
15163                     tag : 'button',
15164                     type : 'button',
15165                     name : 'cancel',
15166                     cls : 'btn btn-link btn-cancel pull-' + this.btnPosition,
15167                     //html : 'Cancel'
15168                     html: btn_text_cancel
15169                 }
15170             ]
15171         };
15172         
15173         if(this.editable){
15174             buttons.cn.unshift({
15175                 tag: 'input',
15176                 cls: 'roo-select2-search-field-input'
15177             });
15178         }
15179         
15180         var _this = this;
15181         
15182         Roo.each(buttons.cn, function(c){
15183             if (_this.size) {
15184                 c.cls += ' btn-' + _this.size;
15185             }
15186
15187             if (_this.disabled) {
15188                 c.disabled = true;
15189             }
15190         });
15191         
15192         var box = {
15193             tag: 'div',
15194             style : 'display: contents',
15195             cn: [
15196                 {
15197                     tag: 'input',
15198                     type : 'hidden',
15199                     cls: 'form-hidden-field'
15200                 },
15201                 {
15202                     tag: 'ul',
15203                     cls: 'roo-select2-choices',
15204                     cn:[
15205                         {
15206                             tag: 'li',
15207                             cls: 'roo-select2-search-field',
15208                             cn: [
15209                                 buttons
15210                             ]
15211                         }
15212                     ]
15213                 }
15214             ]
15215         };
15216         
15217         var combobox = {
15218             cls: 'roo-select2-container input-group roo-select2-container-multi',
15219             cn: [
15220                 
15221                 box
15222 //                {
15223 //                    tag: 'ul',
15224 //                    cls: 'typeahead typeahead-long dropdown-menu',
15225 //                    style: 'display:none; max-height:' + this.maxHeight + 'px;'
15226 //                }
15227             ]
15228         };
15229         
15230         if(this.hasFeedback && !this.allowBlank){
15231             
15232             var feedback = {
15233                 tag: 'span',
15234                 cls: 'glyphicon form-control-feedback'
15235             };
15236
15237             combobox.cn.push(feedback);
15238         }
15239         
15240         
15241         
15242         var indicator = {
15243             tag : 'i',
15244             cls : 'roo-required-indicator ' + (this.indicatorpos == 'right'  ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
15245             tooltip : 'This field is required'
15246         };
15247         if (Roo.bootstrap.version == 4) {
15248             indicator = {
15249                 tag : 'i',
15250                 style : 'display:none'
15251             };
15252         }
15253         if (align ==='left' && this.fieldLabel.length) {
15254             
15255             cfg.cls += ' roo-form-group-label-left'  + (Roo.bootstrap.version == 4 ? ' row' : '');
15256             
15257             cfg.cn = [
15258                 indicator,
15259                 {
15260                     tag: 'label',
15261                     'for' :  id,
15262                     cls : 'control-label col-form-label',
15263                     html : this.fieldLabel
15264
15265                 },
15266                 {
15267                     cls : "", 
15268                     cn: [
15269                         combobox
15270                     ]
15271                 }
15272
15273             ];
15274             
15275             var labelCfg = cfg.cn[1];
15276             var contentCfg = cfg.cn[2];
15277             
15278
15279             if(this.indicatorpos == 'right'){
15280                 
15281                 cfg.cn = [
15282                     {
15283                         tag: 'label',
15284                         'for' :  id,
15285                         cls : 'control-label col-form-label',
15286                         cn : [
15287                             {
15288                                 tag : 'span',
15289                                 html : this.fieldLabel
15290                             },
15291                             indicator
15292                         ]
15293                     },
15294                     {
15295                         cls : "",
15296                         cn: [
15297                             combobox
15298                         ]
15299                     }
15300
15301                 ];
15302                 
15303                 
15304                 
15305                 labelCfg = cfg.cn[0];
15306                 contentCfg = cfg.cn[1];
15307             
15308             }
15309             
15310             if(this.labelWidth > 12){
15311                 labelCfg.style = "width: " + this.labelWidth + 'px';
15312             }
15313             if(this.width * 1 > 0){
15314                 contentCfg.style = "width: " + this.width + 'px';
15315             }
15316             if(this.labelWidth < 13 && this.labelmd == 0){
15317                 this.labelmd = this.labelWidth;
15318             }
15319             
15320             if(this.labellg > 0){
15321                 labelCfg.cls += ' col-lg-' + this.labellg;
15322                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
15323             }
15324             
15325             if(this.labelmd > 0){
15326                 labelCfg.cls += ' col-md-' + this.labelmd;
15327                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
15328             }
15329             
15330             if(this.labelsm > 0){
15331                 labelCfg.cls += ' col-sm-' + this.labelsm;
15332                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
15333             }
15334             
15335             if(this.labelxs > 0){
15336                 labelCfg.cls += ' col-xs-' + this.labelxs;
15337                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
15338             }
15339                 
15340                 
15341         } else if ( this.fieldLabel.length) {
15342 //                Roo.log(" label");
15343                  cfg.cn = [
15344                    indicator,
15345                     {
15346                         tag: 'label',
15347                         //cls : 'input-group-addon',
15348                         html : this.fieldLabel
15349                     },
15350                     combobox
15351                 ];
15352                 
15353                 if(this.indicatorpos == 'right'){
15354                     cfg.cn = [
15355                         {
15356                             tag: 'label',
15357                             //cls : 'input-group-addon',
15358                             html : this.fieldLabel
15359                         },
15360                         indicator,
15361                         combobox
15362                     ];
15363                     
15364                 }
15365
15366         } else {
15367             
15368 //                Roo.log(" no label && no align");
15369                 cfg = combobox
15370                      
15371                 
15372         }
15373          
15374         var settings=this;
15375         ['xs','sm','md','lg'].map(function(size){
15376             if (settings[size]) {
15377                 cfg.cls += ' col-' + size + '-' + settings[size];
15378             }
15379         });
15380         
15381         return cfg;
15382         
15383     },
15384     
15385     _initEventsCalled : false,
15386     
15387     // private
15388     initEvents: function()
15389     {   
15390         if (this._initEventsCalled) { // as we call render... prevent looping...
15391             return;
15392         }
15393         this._initEventsCalled = true;
15394         
15395         if (!this.store) {
15396             throw "can not find store for combo";
15397         }
15398         
15399         this.indicator = this.indicatorEl();
15400         
15401         this.store = Roo.factory(this.store, Roo.data);
15402         this.store.parent = this;
15403         
15404         // if we are building from html. then this element is so complex, that we can not really
15405         // use the rendered HTML.
15406         // so we have to trash and replace the previous code.
15407         if (Roo.XComponent.build_from_html) {
15408             // remove this element....
15409             var e = this.el.dom, k=0;
15410             while (e ) { e = e.previousSibling;  ++k;}
15411
15412             this.el.remove();
15413             
15414             this.el=false;
15415             this.rendered = false;
15416             
15417             this.render(this.parent().getChildContainer(true), k);
15418         }
15419         
15420         if(Roo.isIOS && this.useNativeIOS){
15421             this.initIOSView();
15422             return;
15423         }
15424         
15425         /*
15426          * Touch Devices
15427          */
15428         
15429         if(Roo.isTouch && this.mobileTouchView){
15430             this.initTouchView();
15431             return;
15432         }
15433         
15434         if(this.tickable){
15435             this.initTickableEvents();
15436             return;
15437         }
15438         
15439         Roo.bootstrap.ComboBox.superclass.initEvents.call(this);
15440         
15441         if(this.hiddenName){
15442             
15443             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
15444             
15445             this.hiddenField.dom.value =
15446                 this.hiddenValue !== undefined ? this.hiddenValue :
15447                 this.value !== undefined ? this.value : '';
15448
15449             // prevent input submission
15450             this.el.dom.removeAttribute('name');
15451             this.hiddenField.dom.setAttribute('name', this.hiddenName);
15452              
15453              
15454         }
15455         //if(Roo.isGecko){
15456         //    this.el.dom.setAttribute('autocomplete', 'off');
15457         //}
15458         
15459         var cls = 'x-combo-list';
15460         
15461         //this.list = new Roo.Layer({
15462         //    shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
15463         //});
15464         
15465         var _this = this;
15466         
15467         (function(){
15468             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
15469             _this.list.setWidth(lw);
15470         }).defer(100);
15471         
15472         this.list.on('mouseover', this.onViewOver, this);
15473         this.list.on('mousemove', this.onViewMove, this);
15474         this.list.on('scroll', this.onViewScroll, this);
15475         
15476         /*
15477         this.list.swallowEvent('mousewheel');
15478         this.assetHeight = 0;
15479
15480         if(this.title){
15481             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
15482             this.assetHeight += this.header.getHeight();
15483         }
15484
15485         this.innerList = this.list.createChild({cls:cls+'-inner'});
15486         this.innerList.on('mouseover', this.onViewOver, this);
15487         this.innerList.on('mousemove', this.onViewMove, this);
15488         this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
15489         
15490         if(this.allowBlank && !this.pageSize && !this.disableClear){
15491             this.footer = this.list.createChild({cls:cls+'-ft'});
15492             this.pageTb = new Roo.Toolbar(this.footer);
15493            
15494         }
15495         if(this.pageSize){
15496             this.footer = this.list.createChild({cls:cls+'-ft'});
15497             this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
15498                     {pageSize: this.pageSize});
15499             
15500         }
15501         
15502         if (this.pageTb && this.allowBlank && !this.disableClear) {
15503             var _this = this;
15504             this.pageTb.add(new Roo.Toolbar.Fill(), {
15505                 cls: 'x-btn-icon x-btn-clear',
15506                 text: '&#160;',
15507                 handler: function()
15508                 {
15509                     _this.collapse();
15510                     _this.clearValue();
15511                     _this.onSelect(false, -1);
15512                 }
15513             });
15514         }
15515         if (this.footer) {
15516             this.assetHeight += this.footer.getHeight();
15517         }
15518         */
15519             
15520         if(!this.tpl){
15521             this.tpl = Roo.bootstrap.version == 4 ?
15522                 '<a class="dropdown-item" href="#">{' + this.displayField + '}</a>' :  // 4 does not need <li> and it get's really confisued.
15523                 '<li><a class="dropdown-item" href="#">{' + this.displayField + '}</a></li>';
15524         }
15525
15526         this.view = new Roo.View(this.list, this.tpl, {
15527             singleSelect:true, store: this.store, selectedClass: this.selectedClass
15528         });
15529         //this.view.wrapEl.setDisplayed(false);
15530         this.view.on('click', this.onViewClick, this);
15531         
15532         
15533         this.store.on('beforeload', this.onBeforeLoad, this);
15534         this.store.on('load', this.onLoad, this);
15535         this.store.on('loadexception', this.onLoadException, this);
15536         /*
15537         if(this.resizable){
15538             this.resizer = new Roo.Resizable(this.list,  {
15539                pinned:true, handles:'se'
15540             });
15541             this.resizer.on('resize', function(r, w, h){
15542                 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
15543                 this.listWidth = w;
15544                 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
15545                 this.restrictHeight();
15546             }, this);
15547             this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
15548         }
15549         */
15550         if(!this.editable){
15551             this.editable = true;
15552             this.setEditable(false);
15553         }
15554         
15555         /*
15556         
15557         if (typeof(this.events.add.listeners) != 'undefined') {
15558             
15559             this.addicon = this.wrap.createChild(
15560                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });  
15561        
15562             this.addicon.on('click', function(e) {
15563                 this.fireEvent('add', this);
15564             }, this);
15565         }
15566         if (typeof(this.events.edit.listeners) != 'undefined') {
15567             
15568             this.editicon = this.wrap.createChild(
15569                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });  
15570             if (this.addicon) {
15571                 this.editicon.setStyle('margin-left', '40px');
15572             }
15573             this.editicon.on('click', function(e) {
15574                 
15575                 // we fire even  if inothing is selected..
15576                 this.fireEvent('edit', this, this.lastData );
15577                 
15578             }, this);
15579         }
15580         */
15581         
15582         this.keyNav = new Roo.KeyNav(this.inputEl(), {
15583             "up" : function(e){
15584                 this.inKeyMode = true;
15585                 this.selectPrev();
15586             },
15587
15588             "down" : function(e){
15589                 if(!this.isExpanded()){
15590                     this.onTriggerClick();
15591                 }else{
15592                     this.inKeyMode = true;
15593                     this.selectNext();
15594                 }
15595             },
15596
15597             "enter" : function(e){
15598 //                this.onViewClick();
15599                 //return true;
15600                 this.collapse();
15601                 
15602                 if(this.fireEvent("specialkey", this, e)){
15603                     this.onViewClick(false);
15604                 }
15605                 
15606                 return true;
15607             },
15608
15609             "esc" : function(e){
15610                 this.collapse();
15611             },
15612
15613             "tab" : function(e){
15614                 this.collapse();
15615                 
15616                 if(this.fireEvent("specialkey", this, e)){
15617                     this.onViewClick(false);
15618                 }
15619                 
15620                 return true;
15621             },
15622
15623             scope : this,
15624
15625             doRelay : function(foo, bar, hname){
15626                 if(hname == 'down' || this.scope.isExpanded()){
15627                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
15628                 }
15629                 return true;
15630             },
15631
15632             forceKeyDown: true
15633         });
15634         
15635         
15636         this.queryDelay = Math.max(this.queryDelay || 10,
15637                 this.mode == 'local' ? 10 : 250);
15638         
15639         
15640         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
15641         
15642         if(this.typeAhead){
15643             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
15644         }
15645         if(this.editable !== false){
15646             this.inputEl().on("keyup", this.onKeyUp, this);
15647         }
15648         if(this.forceSelection){
15649             this.inputEl().on('blur', this.doForce, this);
15650         }
15651         
15652         if(this.multiple){
15653             this.choices = this.el.select('ul.roo-select2-choices', true).first();
15654             this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
15655         }
15656     },
15657     
15658     initTickableEvents: function()
15659     {   
15660         this.createList();
15661         
15662         if(this.hiddenName){
15663             
15664             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
15665             
15666             this.hiddenField.dom.value =
15667                 this.hiddenValue !== undefined ? this.hiddenValue :
15668                 this.value !== undefined ? this.value : '';
15669
15670             // prevent input submission
15671             this.el.dom.removeAttribute('name');
15672             this.hiddenField.dom.setAttribute('name', this.hiddenName);
15673              
15674              
15675         }
15676         
15677 //        this.list = this.el.select('ul.dropdown-menu',true).first();
15678         
15679         this.choices = this.el.select('ul.roo-select2-choices', true).first();
15680         this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
15681         if(this.triggerList){
15682             this.searchField.on("click", this.onSearchFieldClick, this, {preventDefault:true});
15683         }
15684          
15685         this.trigger = this.el.select('.tickable-buttons > .btn-edit', true).first();
15686         this.trigger.on("click", this.onTickableTriggerClick, this, {preventDefault:true});
15687         
15688         this.okBtn = this.el.select('.tickable-buttons > .btn-ok', true).first();
15689         this.cancelBtn = this.el.select('.tickable-buttons > .btn-cancel', true).first();
15690         
15691         this.okBtn.on('click', this.onTickableFooterButtonClick, this, this.okBtn);
15692         this.cancelBtn.on('click', this.onTickableFooterButtonClick, this, this.cancelBtn);
15693         
15694         this.trigger.setVisibilityMode(Roo.Element.DISPLAY);
15695         this.okBtn.setVisibilityMode(Roo.Element.DISPLAY);
15696         this.cancelBtn.setVisibilityMode(Roo.Element.DISPLAY);
15697         
15698         this.okBtn.hide();
15699         this.cancelBtn.hide();
15700         
15701         var _this = this;
15702         
15703         (function(){
15704             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
15705             _this.list.setWidth(lw);
15706         }).defer(100);
15707         
15708         this.list.on('mouseover', this.onViewOver, this);
15709         this.list.on('mousemove', this.onViewMove, this);
15710         
15711         this.list.on('scroll', this.onViewScroll, this);
15712         
15713         if(!this.tpl){
15714             this.tpl = '<li class="roo-select2-result"><div class="checkbox"><input id="{roo-id}"' + 
15715                 'type="checkbox" {roo-data-checked}><label for="{roo-id}"><b>{' + this.displayField + '}</b></label></div></li>';
15716         }
15717
15718         this.view = new Roo.View(this.list, this.tpl, {
15719             singleSelect:true,
15720             tickable:true,
15721             parent:this,
15722             store: this.store,
15723             selectedClass: this.selectedClass
15724         });
15725         
15726         //this.view.wrapEl.setDisplayed(false);
15727         this.view.on('click', this.onViewClick, this);
15728         
15729         
15730         
15731         this.store.on('beforeload', this.onBeforeLoad, this);
15732         this.store.on('load', this.onLoad, this);
15733         this.store.on('loadexception', this.onLoadException, this);
15734         
15735         if(this.editable){
15736             this.keyNav = new Roo.KeyNav(this.tickableInputEl(), {
15737                 "up" : function(e){
15738                     this.inKeyMode = true;
15739                     this.selectPrev();
15740                 },
15741
15742                 "down" : function(e){
15743                     this.inKeyMode = true;
15744                     this.selectNext();
15745                 },
15746
15747                 "enter" : function(e){
15748                     if(this.fireEvent("specialkey", this, e)){
15749                         this.onViewClick(false);
15750                     }
15751                     
15752                     return true;
15753                 },
15754
15755                 "esc" : function(e){
15756                     this.onTickableFooterButtonClick(e, false, false);
15757                 },
15758
15759                 "tab" : function(e){
15760                     this.fireEvent("specialkey", this, e);
15761                     
15762                     this.onTickableFooterButtonClick(e, false, false);
15763                     
15764                     return true;
15765                 },
15766
15767                 scope : this,
15768
15769                 doRelay : function(e, fn, key){
15770                     if(this.scope.isExpanded()){
15771                        return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
15772                     }
15773                     return true;
15774                 },
15775
15776                 forceKeyDown: true
15777             });
15778         }
15779         
15780         this.queryDelay = Math.max(this.queryDelay || 10,
15781                 this.mode == 'local' ? 10 : 250);
15782         
15783         
15784         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
15785         
15786         if(this.typeAhead){
15787             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
15788         }
15789         
15790         if(this.editable !== false){
15791             this.tickableInputEl().on("keyup", this.onKeyUp, this);
15792         }
15793         
15794         this.indicator = this.indicatorEl();
15795         
15796         if(this.indicator){
15797             this.indicator.setVisibilityMode(Roo.Element.DISPLAY);
15798             this.indicator.hide();
15799         }
15800         
15801     },
15802
15803     onDestroy : function(){
15804         if(this.view){
15805             this.view.setStore(null);
15806             this.view.el.removeAllListeners();
15807             this.view.el.remove();
15808             this.view.purgeListeners();
15809         }
15810         if(this.list){
15811             this.list.dom.innerHTML  = '';
15812         }
15813         
15814         if(this.store){
15815             this.store.un('beforeload', this.onBeforeLoad, this);
15816             this.store.un('load', this.onLoad, this);
15817             this.store.un('loadexception', this.onLoadException, this);
15818         }
15819         Roo.bootstrap.ComboBox.superclass.onDestroy.call(this);
15820     },
15821
15822     // private
15823     fireKey : function(e){
15824         if(e.isNavKeyPress() && !this.list.isVisible()){
15825             this.fireEvent("specialkey", this, e);
15826         }
15827     },
15828
15829     // private
15830     onResize: function(w, h)
15831     {
15832         
15833         
15834 //        Roo.bootstrap.ComboBox.superclass.onResize.apply(this, arguments);
15835 //        
15836 //        if(typeof w != 'number'){
15837 //            // we do not handle it!?!?
15838 //            return;
15839 //        }
15840 //        var tw = this.trigger.getWidth();
15841 //       // tw += this.addicon ? this.addicon.getWidth() : 0;
15842 //       // tw += this.editicon ? this.editicon.getWidth() : 0;
15843 //        var x = w - tw;
15844 //        this.inputEl().setWidth( this.adjustWidth('input', x));
15845 //            
15846 //        //this.trigger.setStyle('left', x+'px');
15847 //        
15848 //        if(this.list && this.listWidth === undefined){
15849 //            var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
15850 //            this.list.setWidth(lw);
15851 //            this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
15852 //        }
15853         
15854     
15855         
15856     },
15857
15858     /**
15859      * Allow or prevent the user from directly editing the field text.  If false is passed,
15860      * the user will only be able to select from the items defined in the dropdown list.  This method
15861      * is the runtime equivalent of setting the 'editable' config option at config time.
15862      * @param {Boolean} value True to allow the user to directly edit the field text
15863      */
15864     setEditable : function(value){
15865         if(value == this.editable){
15866             return;
15867         }
15868         this.editable = value;
15869         if(!value){
15870             this.inputEl().dom.setAttribute('readOnly', true);
15871             this.inputEl().on('mousedown', this.onTriggerClick,  this);
15872             this.inputEl().addClass('x-combo-noedit');
15873         }else{
15874             this.inputEl().dom.setAttribute('readOnly', false);
15875             this.inputEl().un('mousedown', this.onTriggerClick,  this);
15876             this.inputEl().removeClass('x-combo-noedit');
15877         }
15878     },
15879
15880     // private
15881     
15882     onBeforeLoad : function(combo,opts){
15883         if(!this.hasFocus){
15884             return;
15885         }
15886          if (!opts.add) {
15887             this.list.dom.innerHTML = '<li class="loading-indicator">'+(this.loadingText||'loading')+'</li>' ;
15888          }
15889         this.restrictHeight();
15890         this.selectedIndex = -1;
15891     },
15892
15893     // private
15894     onLoad : function(){
15895         
15896         this.hasQuery = false;
15897         
15898         if(!this.hasFocus){
15899             return;
15900         }
15901         
15902         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
15903             this.loading.hide();
15904         }
15905         
15906         if(this.store.getCount() > 0){
15907             
15908             this.expand();
15909             this.restrictHeight();
15910             if(this.lastQuery == this.allQuery){
15911                 if(this.editable && !this.tickable){
15912                     this.inputEl().dom.select();
15913                 }
15914                 
15915                 if(
15916                     !this.selectByValue(this.value, true) &&
15917                     this.autoFocus && 
15918                     (
15919                         !this.store.lastOptions ||
15920                         typeof(this.store.lastOptions.add) == 'undefined' || 
15921                         this.store.lastOptions.add != true
15922                     )
15923                 ){
15924                     this.select(0, true);
15925                 }
15926             }else{
15927                 if(this.autoFocus){
15928                     this.selectNext();
15929                 }
15930                 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
15931                     this.taTask.delay(this.typeAheadDelay);
15932                 }
15933             }
15934         }else{
15935             this.onEmptyResults();
15936         }
15937         
15938         //this.el.focus();
15939     },
15940     // private
15941     onLoadException : function()
15942     {
15943         this.hasQuery = false;
15944         
15945         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
15946             this.loading.hide();
15947         }
15948         
15949         if(this.tickable && this.editable){
15950             return;
15951         }
15952         
15953         this.collapse();
15954         // only causes errors at present
15955         //Roo.log(this.store.reader.jsonData);
15956         //if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
15957             // fixme
15958             //Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
15959         //}
15960         
15961         
15962     },
15963     // private
15964     onTypeAhead : function(){
15965         if(this.store.getCount() > 0){
15966             var r = this.store.getAt(0);
15967             var newValue = r.data[this.displayField];
15968             var len = newValue.length;
15969             var selStart = this.getRawValue().length;
15970             
15971             if(selStart != len){
15972                 this.setRawValue(newValue);
15973                 this.selectText(selStart, newValue.length);
15974             }
15975         }
15976     },
15977
15978     // private
15979     onSelect : function(record, index){
15980         
15981         if(this.fireEvent('beforeselect', this, record, index) !== false){
15982         
15983             this.setFromData(index > -1 ? record.data : false);
15984             
15985             this.collapse();
15986             this.fireEvent('select', this, record, index);
15987         }
15988     },
15989
15990     /**
15991      * Returns the currently selected field value or empty string if no value is set.
15992      * @return {String} value The selected value
15993      */
15994     getValue : function()
15995     {
15996         if(Roo.isIOS && this.useNativeIOS){
15997             return this.ios_options[this.inputEl().dom.selectedIndex].data[this.valueField];
15998         }
15999         
16000         if(this.multiple){
16001             return (this.hiddenField) ? this.hiddenField.dom.value : this.value;
16002         }
16003         
16004         if(this.valueField){
16005             return typeof this.value != 'undefined' ? this.value : '';
16006         }else{
16007             return Roo.bootstrap.ComboBox.superclass.getValue.call(this);
16008         }
16009     },
16010     
16011     getRawValue : function()
16012     {
16013         if(Roo.isIOS && this.useNativeIOS){
16014             return this.ios_options[this.inputEl().dom.selectedIndex].data[this.displayField];
16015         }
16016         
16017         var v = this.inputEl().getValue();
16018         
16019         return v;
16020     },
16021
16022     /**
16023      * Clears any text/value currently set in the field
16024      */
16025     clearValue : function(){
16026         
16027         if(this.hiddenField){
16028             this.hiddenField.dom.value = '';
16029         }
16030         this.value = '';
16031         this.setRawValue('');
16032         this.lastSelectionText = '';
16033         this.lastData = false;
16034         
16035         var close = this.closeTriggerEl();
16036         
16037         if(close){
16038             close.hide();
16039         }
16040         
16041         this.validate();
16042         
16043     },
16044
16045     /**
16046      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
16047      * will be displayed in the field.  If the value does not match the data value of an existing item,
16048      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
16049      * Otherwise the field will be blank (although the value will still be set).
16050      * @param {String} value The value to match
16051      */
16052     setValue : function(v)
16053     {
16054         if(Roo.isIOS && this.useNativeIOS){
16055             this.setIOSValue(v);
16056             return;
16057         }
16058         
16059         if(this.multiple){
16060             this.syncValue();
16061             return;
16062         }
16063         
16064         var text = v;
16065         if(this.valueField){
16066             var r = this.findRecord(this.valueField, v);
16067             if(r){
16068                 text = r.data[this.displayField];
16069             }else if(this.valueNotFoundText !== undefined){
16070                 text = this.valueNotFoundText;
16071             }
16072         }
16073         this.lastSelectionText = text;
16074         if(this.hiddenField){
16075             this.hiddenField.dom.value = v;
16076         }
16077         Roo.bootstrap.ComboBox.superclass.setValue.call(this, text);
16078         this.value = v;
16079         
16080         var close = this.closeTriggerEl();
16081         
16082         if(close){
16083             (v && (v.length || v * 1 > 0)) ? close.show() : close.hide();
16084         }
16085         
16086         this.validate();
16087     },
16088     /**
16089      * @property {Object} the last set data for the element
16090      */
16091     
16092     lastData : false,
16093     /**
16094      * Sets the value of the field based on a object which is related to the record format for the store.
16095      * @param {Object} value the value to set as. or false on reset?
16096      */
16097     setFromData : function(o){
16098         
16099         if(this.multiple){
16100             this.addItem(o);
16101             return;
16102         }
16103             
16104         var dv = ''; // display value
16105         var vv = ''; // value value..
16106         this.lastData = o;
16107         if (this.displayField) {
16108             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
16109         } else {
16110             // this is an error condition!!!
16111             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
16112         }
16113         
16114         if(this.valueField){
16115             vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
16116         }
16117         
16118         var close = this.closeTriggerEl();
16119         
16120         if(close){
16121             if(dv.length || vv * 1 > 0){
16122                 close.show() ;
16123                 this.blockFocus=true;
16124             } else {
16125                 close.hide();
16126             }             
16127         }
16128         
16129         if(this.hiddenField){
16130             this.hiddenField.dom.value = vv;
16131             
16132             this.lastSelectionText = dv;
16133             Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
16134             this.value = vv;
16135             return;
16136         }
16137         // no hidden field.. - we store the value in 'value', but still display
16138         // display field!!!!
16139         this.lastSelectionText = dv;
16140         Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
16141         this.value = vv;
16142         
16143         
16144         
16145     },
16146     // private
16147     reset : function(){
16148         // overridden so that last data is reset..
16149         
16150         if(this.multiple){
16151             this.clearItem();
16152             return;
16153         }
16154         
16155         this.setValue(this.originalValue);
16156         //this.clearInvalid();
16157         this.lastData = false;
16158         if (this.view) {
16159             this.view.clearSelections();
16160         }
16161         
16162         this.validate();
16163     },
16164     // private
16165     findRecord : function(prop, value){
16166         var record;
16167         if(this.store.getCount() > 0){
16168             this.store.each(function(r){
16169                 if(r.data[prop] == value){
16170                     record = r;
16171                     return false;
16172                 }
16173                 return true;
16174             });
16175         }
16176         return record;
16177     },
16178     
16179     getName: function()
16180     {
16181         // returns hidden if it's set..
16182         if (!this.rendered) {return ''};
16183         return !this.hiddenName && this.inputEl().dom.name  ? this.inputEl().dom.name : (this.hiddenName || '');
16184         
16185     },
16186     // private
16187     onViewMove : function(e, t){
16188         this.inKeyMode = false;
16189     },
16190
16191     // private
16192     onViewOver : function(e, t){
16193         if(this.inKeyMode){ // prevent key nav and mouse over conflicts
16194             return;
16195         }
16196         var item = this.view.findItemFromChild(t);
16197         
16198         if(item){
16199             var index = this.view.indexOf(item);
16200             this.select(index, false);
16201         }
16202     },
16203
16204     // private
16205     onViewClick : function(view, doFocus, el, e)
16206     {
16207         var index = this.view.getSelectedIndexes()[0];
16208         
16209         var r = this.store.getAt(index);
16210         
16211         if(this.tickable){
16212             
16213             if(typeof(e) != 'undefined' && e.getTarget().nodeName.toLowerCase() != 'input'){
16214                 return;
16215             }
16216             
16217             var rm = false;
16218             var _this = this;
16219             
16220             Roo.each(this.tickItems, function(v,k){
16221                 
16222                 if(typeof(v) != 'undefined' && v[_this.valueField] == r.data[_this.valueField]){
16223                     Roo.log(v);
16224                     _this.tickItems.splice(k, 1);
16225                     
16226                     if(typeof(e) == 'undefined' && view == false){
16227                         Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = false;
16228                     }
16229                     
16230                     rm = true;
16231                     return;
16232                 }
16233             });
16234             
16235             if(rm){
16236                 return;
16237             }
16238             
16239             if(this.fireEvent('tick', this, r, index, Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked) !== false){
16240                 this.tickItems.push(r.data);
16241             }
16242             
16243             if(typeof(e) == 'undefined' && view == false){
16244                 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = true;
16245             }
16246                     
16247             return;
16248         }
16249         
16250         if(r){
16251             this.onSelect(r, index);
16252         }
16253         if(doFocus !== false && !this.blockFocus){
16254             this.inputEl().focus();
16255         }
16256     },
16257
16258     // private
16259     restrictHeight : function(){
16260         //this.innerList.dom.style.height = '';
16261         //var inner = this.innerList.dom;
16262         //var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
16263         //this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
16264         //this.list.beginUpdate();
16265         //this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
16266         this.list.alignTo(this.inputEl(), this.listAlign);
16267         this.list.alignTo(this.inputEl(), this.listAlign);
16268         //this.list.endUpdate();
16269     },
16270
16271     // private
16272     onEmptyResults : function(){
16273         
16274         if(this.tickable && this.editable){
16275             this.hasFocus = false;
16276             this.restrictHeight();
16277             return;
16278         }
16279         
16280         this.collapse();
16281     },
16282
16283     /**
16284      * Returns true if the dropdown list is expanded, else false.
16285      */
16286     isExpanded : function(){
16287         return this.list.isVisible();
16288     },
16289
16290     /**
16291      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
16292      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
16293      * @param {String} value The data value of the item to select
16294      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
16295      * selected item if it is not currently in view (defaults to true)
16296      * @return {Boolean} True if the value matched an item in the list, else false
16297      */
16298     selectByValue : function(v, scrollIntoView){
16299         if(v !== undefined && v !== null){
16300             var r = this.findRecord(this.valueField || this.displayField, v);
16301             if(r){
16302                 this.select(this.store.indexOf(r), scrollIntoView);
16303                 return true;
16304             }
16305         }
16306         return false;
16307     },
16308
16309     /**
16310      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
16311      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
16312      * @param {Number} index The zero-based index of the list item to select
16313      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
16314      * selected item if it is not currently in view (defaults to true)
16315      */
16316     select : function(index, scrollIntoView){
16317         this.selectedIndex = index;
16318         this.view.select(index);
16319         if(scrollIntoView !== false){
16320             var el = this.view.getNode(index);
16321             /*
16322              * el && !this.multiple && !this.tickable // not sure why we disable multiple before..
16323              */
16324             if(el){
16325                 this.list.scrollChildIntoView(el, false);
16326             }
16327         }
16328     },
16329
16330     // private
16331     selectNext : function(){
16332         var ct = this.store.getCount();
16333         if(ct > 0){
16334             if(this.selectedIndex == -1){
16335                 this.select(0);
16336             }else if(this.selectedIndex < ct-1){
16337                 this.select(this.selectedIndex+1);
16338             }
16339         }
16340     },
16341
16342     // private
16343     selectPrev : function(){
16344         var ct = this.store.getCount();
16345         if(ct > 0){
16346             if(this.selectedIndex == -1){
16347                 this.select(0);
16348             }else if(this.selectedIndex != 0){
16349                 this.select(this.selectedIndex-1);
16350             }
16351         }
16352     },
16353
16354     // private
16355     onKeyUp : function(e){
16356         if(this.editable !== false && !e.isSpecialKey()){
16357             this.lastKey = e.getKey();
16358             this.dqTask.delay(this.queryDelay);
16359         }
16360     },
16361
16362     // private
16363     validateBlur : function(){
16364         return !this.list || !this.list.isVisible();   
16365     },
16366
16367     // private
16368     initQuery : function(){
16369         
16370         var v = this.getRawValue();
16371         
16372         if(this.tickable && this.editable){
16373             v = this.tickableInputEl().getValue();
16374         }
16375         
16376         this.doQuery(v);
16377     },
16378
16379     // private
16380     doForce : function(){
16381         if(this.inputEl().dom.value.length > 0){
16382             this.inputEl().dom.value =
16383                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
16384              
16385         }
16386     },
16387
16388     /**
16389      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
16390      * query allowing the query action to be canceled if needed.
16391      * @param {String} query The SQL query to execute
16392      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
16393      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
16394      * saved in the current store (defaults to false)
16395      */
16396     doQuery : function(q, forceAll){
16397         
16398         if(q === undefined || q === null){
16399             q = '';
16400         }
16401         var qe = {
16402             query: q,
16403             forceAll: forceAll,
16404             combo: this,
16405             cancel:false
16406         };
16407         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
16408             return false;
16409         }
16410         q = qe.query;
16411         
16412         forceAll = qe.forceAll;
16413         if(forceAll === true || (q.length >= this.minChars)){
16414             
16415             this.hasQuery = true;
16416             
16417             if(this.lastQuery != q || this.alwaysQuery){
16418                 this.lastQuery = q;
16419                 if(this.mode == 'local'){
16420                     this.selectedIndex = -1;
16421                     if(forceAll){
16422                         this.store.clearFilter();
16423                     }else{
16424                         
16425                         if(this.specialFilter){
16426                             this.fireEvent('specialfilter', this);
16427                             this.onLoad();
16428                             return;
16429                         }
16430                         
16431                         this.store.filter(this.displayField, q);
16432                     }
16433                     
16434                     this.store.fireEvent("datachanged", this.store);
16435                     
16436                     this.onLoad();
16437                     
16438                     
16439                 }else{
16440                     
16441                     this.store.baseParams[this.queryParam] = q;
16442                     
16443                     var options = {params : this.getParams(q)};
16444                     
16445                     if(this.loadNext){
16446                         options.add = true;
16447                         options.params.start = this.page * this.pageSize;
16448                     }
16449                     
16450                     this.store.load(options);
16451                     
16452                     /*
16453                      *  this code will make the page width larger, at the beginning, the list not align correctly, 
16454                      *  we should expand the list on onLoad
16455                      *  so command out it
16456                      */
16457 //                    this.expand();
16458                 }
16459             }else{
16460                 this.selectedIndex = -1;
16461                 this.onLoad();   
16462             }
16463         }
16464         
16465         this.loadNext = false;
16466     },
16467     
16468     // private
16469     getParams : function(q){
16470         var p = {};
16471         //p[this.queryParam] = q;
16472         
16473         if(this.pageSize){
16474             p.start = 0;
16475             p.limit = this.pageSize;
16476         }
16477         return p;
16478     },
16479
16480     /**
16481      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
16482      */
16483     collapse : function(){
16484         if(!this.isExpanded()){
16485             return;
16486         }
16487         
16488         this.list.hide();
16489         
16490         this.hasFocus = false;
16491         
16492         if(this.tickable){
16493             this.okBtn.hide();
16494             this.cancelBtn.hide();
16495             this.trigger.show();
16496             
16497             if(this.editable){
16498                 this.tickableInputEl().dom.value = '';
16499                 this.tickableInputEl().blur();
16500             }
16501             
16502         }
16503         
16504         Roo.get(document).un('mousedown', this.collapseIf, this);
16505         Roo.get(document).un('mousewheel', this.collapseIf, this);
16506         if (!this.editable) {
16507             Roo.get(document).un('keydown', this.listKeyPress, this);
16508         }
16509         this.fireEvent('collapse', this);
16510         
16511         this.validate();
16512     },
16513
16514     // private
16515     collapseIf : function(e){
16516         var in_combo  = e.within(this.el);
16517         var in_list =  e.within(this.list);
16518         var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
16519         
16520         if (in_combo || in_list || is_list) {
16521             //e.stopPropagation();
16522             return;
16523         }
16524         
16525         if(this.tickable){
16526             this.onTickableFooterButtonClick(e, false, false);
16527         }
16528
16529         this.collapse();
16530         
16531     },
16532
16533     /**
16534      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
16535      */
16536     expand : function(){
16537        
16538         if(this.isExpanded() || !this.hasFocus){
16539             return;
16540         }
16541         
16542         var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
16543         this.list.setWidth(lw);
16544         
16545         Roo.log('expand');
16546         
16547         this.list.show();
16548         
16549         this.restrictHeight();
16550         
16551         if(this.tickable){
16552             
16553             this.tickItems = Roo.apply([], this.item);
16554             
16555             this.okBtn.show();
16556             this.cancelBtn.show();
16557             this.trigger.hide();
16558             
16559             if(this.editable){
16560                 this.tickableInputEl().focus();
16561             }
16562             
16563         }
16564         
16565         Roo.get(document).on('mousedown', this.collapseIf, this);
16566         Roo.get(document).on('mousewheel', this.collapseIf, this);
16567         if (!this.editable) {
16568             Roo.get(document).on('keydown', this.listKeyPress, this);
16569         }
16570         
16571         this.fireEvent('expand', this);
16572     },
16573
16574     // private
16575     // Implements the default empty TriggerField.onTriggerClick function
16576     onTriggerClick : function(e)
16577     {
16578         Roo.log('trigger click');
16579         
16580         if(this.disabled || !this.triggerList){
16581             return;
16582         }
16583         
16584         this.page = 0;
16585         this.loadNext = false;
16586         
16587         if(this.isExpanded()){
16588             this.collapse();
16589             if (!this.blockFocus) {
16590                 this.inputEl().focus();
16591             }
16592             
16593         }else {
16594             this.hasFocus = true;
16595             if(this.triggerAction == 'all') {
16596                 this.doQuery(this.allQuery, true);
16597             } else {
16598                 this.doQuery(this.getRawValue());
16599             }
16600             if (!this.blockFocus) {
16601                 this.inputEl().focus();
16602             }
16603         }
16604     },
16605     
16606     onTickableTriggerClick : function(e)
16607     {
16608         if(this.disabled){
16609             return;
16610         }
16611         
16612         this.page = 0;
16613         this.loadNext = false;
16614         this.hasFocus = true;
16615         
16616         if(this.triggerAction == 'all') {
16617             this.doQuery(this.allQuery, true);
16618         } else {
16619             this.doQuery(this.getRawValue());
16620         }
16621     },
16622     
16623     onSearchFieldClick : function(e)
16624     {
16625         if(this.hasFocus && !this.disabled && e.getTarget().nodeName.toLowerCase() != 'button'){
16626             this.onTickableFooterButtonClick(e, false, false);
16627             return;
16628         }
16629         
16630         if(this.hasFocus || this.disabled || e.getTarget().nodeName.toLowerCase() == 'button'){
16631             return;
16632         }
16633         
16634         this.page = 0;
16635         this.loadNext = false;
16636         this.hasFocus = true;
16637         
16638         if(this.triggerAction == 'all') {
16639             this.doQuery(this.allQuery, true);
16640         } else {
16641             this.doQuery(this.getRawValue());
16642         }
16643     },
16644     
16645     listKeyPress : function(e)
16646     {
16647         //Roo.log('listkeypress');
16648         // scroll to first matching element based on key pres..
16649         if (e.isSpecialKey()) {
16650             return false;
16651         }
16652         var k = String.fromCharCode(e.getKey()).toUpperCase();
16653         //Roo.log(k);
16654         var match  = false;
16655         var csel = this.view.getSelectedNodes();
16656         var cselitem = false;
16657         if (csel.length) {
16658             var ix = this.view.indexOf(csel[0]);
16659             cselitem  = this.store.getAt(ix);
16660             if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
16661                 cselitem = false;
16662             }
16663             
16664         }
16665         
16666         this.store.each(function(v) { 
16667             if (cselitem) {
16668                 // start at existing selection.
16669                 if (cselitem.id == v.id) {
16670                     cselitem = false;
16671                 }
16672                 return true;
16673             }
16674                 
16675             if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
16676                 match = this.store.indexOf(v);
16677                 return false;
16678             }
16679             return true;
16680         }, this);
16681         
16682         if (match === false) {
16683             return true; // no more action?
16684         }
16685         // scroll to?
16686         this.view.select(match);
16687         var sn = Roo.get(this.view.getSelectedNodes()[0]);
16688         sn.scrollIntoView(sn.dom.parentNode, false);
16689     },
16690     
16691     onViewScroll : function(e, t){
16692         
16693         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){
16694             return;
16695         }
16696         
16697         this.hasQuery = true;
16698         
16699         this.loading = this.list.select('.loading', true).first();
16700         
16701         if(this.loading === null){
16702             this.list.createChild({
16703                 tag: 'div',
16704                 cls: 'loading roo-select2-more-results roo-select2-active',
16705                 html: 'Loading more results...'
16706             });
16707             
16708             this.loading = this.list.select('.loading', true).first();
16709             
16710             this.loading.setVisibilityMode(Roo.Element.DISPLAY);
16711             
16712             this.loading.hide();
16713         }
16714         
16715         this.loading.show();
16716         
16717         var _combo = this;
16718         
16719         this.page++;
16720         this.loadNext = true;
16721         
16722         (function() { _combo.doQuery(_combo.allQuery, true); }).defer(500);
16723         
16724         return;
16725     },
16726     
16727     addItem : function(o)
16728     {   
16729         var dv = ''; // display value
16730         
16731         if (this.displayField) {
16732             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
16733         } else {
16734             // this is an error condition!!!
16735             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
16736         }
16737         
16738         if(!dv.length){
16739             return;
16740         }
16741         
16742         var choice = this.choices.createChild({
16743             tag: 'li',
16744             cls: 'roo-select2-search-choice',
16745             cn: [
16746                 {
16747                     tag: 'div',
16748                     html: dv
16749                 },
16750                 {
16751                     tag: 'a',
16752                     href: '#',
16753                     cls: 'roo-select2-search-choice-close fa fa-times',
16754                     tabindex: '-1'
16755                 }
16756             ]
16757             
16758         }, this.searchField);
16759         
16760         var close = choice.select('a.roo-select2-search-choice-close', true).first();
16761         
16762         close.on('click', this.onRemoveItem, this, { item : choice, data : o} );
16763         
16764         this.item.push(o);
16765         
16766         this.lastData = o;
16767         
16768         this.syncValue();
16769         
16770         this.inputEl().dom.value = '';
16771         
16772         this.validate();
16773     },
16774     
16775     onRemoveItem : function(e, _self, o)
16776     {
16777         e.preventDefault();
16778         
16779         this.lastItem = Roo.apply([], this.item);
16780         
16781         var index = this.item.indexOf(o.data) * 1;
16782         
16783         if( index < 0){
16784             Roo.log('not this item?!');
16785             return;
16786         }
16787         
16788         this.item.splice(index, 1);
16789         o.item.remove();
16790         
16791         this.syncValue();
16792         
16793         this.fireEvent('remove', this, e);
16794         
16795         this.validate();
16796         
16797     },
16798     
16799     syncValue : function()
16800     {
16801         if(!this.item.length){
16802             this.clearValue();
16803             return;
16804         }
16805             
16806         var value = [];
16807         var _this = this;
16808         Roo.each(this.item, function(i){
16809             if(_this.valueField){
16810                 value.push(i[_this.valueField]);
16811                 return;
16812             }
16813
16814             value.push(i);
16815         });
16816
16817         this.value = value.join(',');
16818
16819         if(this.hiddenField){
16820             this.hiddenField.dom.value = this.value;
16821         }
16822         
16823         this.store.fireEvent("datachanged", this.store);
16824         
16825         this.validate();
16826     },
16827     
16828     clearItem : function()
16829     {
16830         if(!this.multiple){
16831             return;
16832         }
16833         
16834         this.item = [];
16835         
16836         Roo.each(this.choices.select('>li.roo-select2-search-choice', true).elements, function(c){
16837            c.remove();
16838         });
16839         
16840         this.syncValue();
16841         
16842         this.validate();
16843         
16844         if(this.tickable && !Roo.isTouch){
16845             this.view.refresh();
16846         }
16847     },
16848     
16849     inputEl: function ()
16850     {
16851         if(Roo.isIOS && this.useNativeIOS){
16852             return this.el.select('select.roo-ios-select', true).first();
16853         }
16854         
16855         if(Roo.isTouch && this.mobileTouchView){
16856             return this.el.select('input.form-control',true).first();
16857         }
16858         
16859         if(this.tickable){
16860             return this.searchField;
16861         }
16862         
16863         return this.el.select('input.form-control',true).first();
16864     },
16865     
16866     onTickableFooterButtonClick : function(e, btn, el)
16867     {
16868         e.preventDefault();
16869         
16870         this.lastItem = Roo.apply([], this.item);
16871         
16872         if(btn && btn.name == 'cancel'){
16873             this.tickItems = Roo.apply([], this.item);
16874             this.collapse();
16875             return;
16876         }
16877         
16878         this.clearItem();
16879         
16880         var _this = this;
16881         
16882         Roo.each(this.tickItems, function(o){
16883             _this.addItem(o);
16884         });
16885         
16886         this.collapse();
16887         
16888     },
16889     
16890     validate : function()
16891     {
16892         if(this.getVisibilityEl().hasClass('hidden')){
16893             return true;
16894         }
16895         
16896         var v = this.getRawValue();
16897         
16898         if(this.multiple){
16899             v = this.getValue();
16900         }
16901         
16902         if(this.disabled || this.allowBlank || v.length){
16903             this.markValid();
16904             return true;
16905         }
16906         
16907         this.markInvalid();
16908         return false;
16909     },
16910     
16911     tickableInputEl : function()
16912     {
16913         if(!this.tickable || !this.editable){
16914             return this.inputEl();
16915         }
16916         
16917         return this.inputEl().select('.roo-select2-search-field-input', true).first();
16918     },
16919     
16920     
16921     getAutoCreateTouchView : function()
16922     {
16923         var id = Roo.id();
16924         
16925         var cfg = {
16926             cls: 'form-group' //input-group
16927         };
16928         
16929         var input =  {
16930             tag: 'input',
16931             id : id,
16932             type : this.inputType,
16933             cls : 'form-control x-combo-noedit',
16934             autocomplete: 'new-password',
16935             placeholder : this.placeholder || '',
16936             readonly : true
16937         };
16938         
16939         if (this.name) {
16940             input.name = this.name;
16941         }
16942         
16943         if (this.size) {
16944             input.cls += ' input-' + this.size;
16945         }
16946         
16947         if (this.disabled) {
16948             input.disabled = true;
16949         }
16950         
16951         var inputblock = {
16952             cls : 'roo-combobox-wrap',
16953             cn : [
16954                 input
16955             ]
16956         };
16957         
16958         if(this.before){
16959             inputblock.cls += ' input-group';
16960             
16961             inputblock.cn.unshift({
16962                 tag :'span',
16963                 cls : 'input-group-addon input-group-prepend input-group-text',
16964                 html : this.before
16965             });
16966         }
16967         
16968         if(this.removable && !this.multiple){
16969             inputblock.cls += ' roo-removable';
16970             
16971             inputblock.cn.push({
16972                 tag: 'button',
16973                 html : 'x',
16974                 cls : 'roo-combo-removable-btn close'
16975             });
16976         }
16977
16978         if(this.hasFeedback && !this.allowBlank){
16979             
16980             inputblock.cls += ' has-feedback';
16981             
16982             inputblock.cn.push({
16983                 tag: 'span',
16984                 cls: 'glyphicon form-control-feedback'
16985             });
16986             
16987         }
16988         
16989         if (this.after) {
16990             
16991             inputblock.cls += (this.before) ? '' : ' input-group';
16992             
16993             inputblock.cn.push({
16994                 tag :'span',
16995                 cls : 'input-group-addon input-group-append input-group-text',
16996                 html : this.after
16997             });
16998         }
16999
17000         
17001         var ibwrap = inputblock;
17002         
17003         if(this.multiple){
17004             ibwrap = {
17005                 tag: 'ul',
17006                 cls: 'roo-select2-choices',
17007                 cn:[
17008                     {
17009                         tag: 'li',
17010                         cls: 'roo-select2-search-field',
17011                         cn: [
17012
17013                             inputblock
17014                         ]
17015                     }
17016                 ]
17017             };
17018         
17019             
17020         }
17021         
17022         var combobox = {
17023             cls: 'roo-select2-container input-group roo-touchview-combobox ',
17024             cn: [
17025                 {
17026                     tag: 'input',
17027                     type : 'hidden',
17028                     cls: 'form-hidden-field'
17029                 },
17030                 ibwrap
17031             ]
17032         };
17033         
17034         if(!this.multiple && this.showToggleBtn){
17035             
17036             var caret = {
17037                 cls: 'caret'
17038             };
17039             
17040             if (this.caret != false) {
17041                 caret = {
17042                      tag: 'i',
17043                      cls: 'fa fa-' + this.caret
17044                 };
17045                 
17046             }
17047             
17048             combobox.cn.push({
17049                 tag :'span',
17050                 cls : 'input-group-addon input-group-append input-group-text btn dropdown-toggle',
17051                 cn : [
17052                     Roo.bootstrap.version == 3 ? caret : '',
17053                     {
17054                         tag: 'span',
17055                         cls: 'combobox-clear',
17056                         cn  : [
17057                             {
17058                                 tag : 'i',
17059                                 cls: 'icon-remove'
17060                             }
17061                         ]
17062                     }
17063                 ]
17064
17065             })
17066         }
17067         
17068         if(this.multiple){
17069             combobox.cls += ' roo-select2-container-multi';
17070         }
17071         
17072         var align = this.labelAlign || this.parentLabelAlign();
17073         
17074         if (align ==='left' && this.fieldLabel.length) {
17075
17076             cfg.cn = [
17077                 {
17078                    tag : 'i',
17079                    cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
17080                    tooltip : 'This field is required'
17081                 },
17082                 {
17083                     tag: 'label',
17084                     cls : 'control-label col-form-label',
17085                     html : this.fieldLabel
17086
17087                 },
17088                 {
17089                     cls : 'roo-combobox-wrap ', 
17090                     cn: [
17091                         combobox
17092                     ]
17093                 }
17094             ];
17095             
17096             var labelCfg = cfg.cn[1];
17097             var contentCfg = cfg.cn[2];
17098             
17099
17100             if(this.indicatorpos == 'right'){
17101                 cfg.cn = [
17102                     {
17103                         tag: 'label',
17104                         'for' :  id,
17105                         cls : 'control-label col-form-label',
17106                         cn : [
17107                             {
17108                                 tag : 'span',
17109                                 html : this.fieldLabel
17110                             },
17111                             {
17112                                 tag : 'i',
17113                                 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
17114                                 tooltip : 'This field is required'
17115                             }
17116                         ]
17117                     },
17118                     {
17119                         cls : "roo-combobox-wrap ",
17120                         cn: [
17121                             combobox
17122                         ]
17123                     }
17124
17125                 ];
17126                 
17127                 labelCfg = cfg.cn[0];
17128                 contentCfg = cfg.cn[1];
17129             }
17130             
17131            
17132             
17133             if(this.labelWidth > 12){
17134                 labelCfg.style = "width: " + this.labelWidth + 'px';
17135             }
17136            
17137             if(this.labelWidth < 13 && this.labelmd == 0){
17138                 this.labelmd = this.labelWidth;
17139             }
17140             
17141             if(this.labellg > 0){
17142                 labelCfg.cls += ' col-lg-' + this.labellg;
17143                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
17144             }
17145             
17146             if(this.labelmd > 0){
17147                 labelCfg.cls += ' col-md-' + this.labelmd;
17148                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
17149             }
17150             
17151             if(this.labelsm > 0){
17152                 labelCfg.cls += ' col-sm-' + this.labelsm;
17153                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
17154             }
17155             
17156             if(this.labelxs > 0){
17157                 labelCfg.cls += ' col-xs-' + this.labelxs;
17158                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
17159             }
17160                 
17161                 
17162         } else if ( this.fieldLabel.length) {
17163             cfg.cn = [
17164                 {
17165                    tag : 'i',
17166                    cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
17167                    tooltip : 'This field is required'
17168                 },
17169                 {
17170                     tag: 'label',
17171                     cls : 'control-label',
17172                     html : this.fieldLabel
17173
17174                 },
17175                 {
17176                     cls : '', 
17177                     cn: [
17178                         combobox
17179                     ]
17180                 }
17181             ];
17182             
17183             if(this.indicatorpos == 'right'){
17184                 cfg.cn = [
17185                     {
17186                         tag: 'label',
17187                         cls : 'control-label',
17188                         html : this.fieldLabel,
17189                         cn : [
17190                             {
17191                                tag : 'i',
17192                                cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
17193                                tooltip : 'This field is required'
17194                             }
17195                         ]
17196                     },
17197                     {
17198                         cls : '', 
17199                         cn: [
17200                             combobox
17201                         ]
17202                     }
17203                 ];
17204             }
17205         } else {
17206             cfg.cn = combobox;    
17207         }
17208         
17209         
17210         var settings = this;
17211         
17212         ['xs','sm','md','lg'].map(function(size){
17213             if (settings[size]) {
17214                 cfg.cls += ' col-' + size + '-' + settings[size];
17215             }
17216         });
17217         
17218         return cfg;
17219     },
17220     
17221     initTouchView : function()
17222     {
17223         this.renderTouchView();
17224         
17225         this.touchViewEl.on('scroll', function(){
17226             this.el.dom.scrollTop = 0;
17227         }, this);
17228         
17229         this.originalValue = this.getValue();
17230         
17231         this.triggerEl = this.el.select('span.dropdown-toggle',true).first();
17232         
17233         this.inputEl().on("click", this.showTouchView, this);
17234         if (this.triggerEl) {
17235             this.triggerEl.on("click", this.showTouchView, this);
17236         }
17237         
17238         
17239         this.touchViewFooterEl.select('.roo-touch-view-cancel', true).first().on('click', this.hideTouchView, this);
17240         this.touchViewFooterEl.select('.roo-touch-view-ok', true).first().on('click', this.setTouchViewValue, this);
17241         
17242         this.maskEl = new Roo.LoadMask(this.touchViewEl, { store : this.store, msgCls: 'roo-el-mask-msg' });
17243         
17244         this.store.on('beforeload', this.onTouchViewBeforeLoad, this);
17245         this.store.on('load', this.onTouchViewLoad, this);
17246         this.store.on('loadexception', this.onTouchViewLoadException, this);
17247         
17248         if(this.hiddenName){
17249             
17250             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
17251             
17252             this.hiddenField.dom.value =
17253                 this.hiddenValue !== undefined ? this.hiddenValue :
17254                 this.value !== undefined ? this.value : '';
17255         
17256             this.el.dom.removeAttribute('name');
17257             this.hiddenField.dom.setAttribute('name', this.hiddenName);
17258         }
17259         
17260         if(this.multiple){
17261             this.choices = this.el.select('ul.roo-select2-choices', true).first();
17262             this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
17263         }
17264         
17265         if(this.removable && !this.multiple){
17266             var close = this.closeTriggerEl();
17267             if(close){
17268                 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
17269                 close.on('click', this.removeBtnClick, this, close);
17270             }
17271         }
17272         /*
17273          * fix the bug in Safari iOS8
17274          */
17275         this.inputEl().on("focus", function(e){
17276             document.activeElement.blur();
17277         }, this);
17278         
17279         this._touchViewMask = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
17280         
17281         return;
17282         
17283         
17284     },
17285     
17286     renderTouchView : function()
17287     {
17288         this.touchViewEl = Roo.get(document.body).createChild(Roo.bootstrap.ComboBox.touchViewTemplate);
17289         this.touchViewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17290         
17291         this.touchViewHeaderEl = this.touchViewEl.select('.modal-header', true).first();
17292         this.touchViewHeaderEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17293         
17294         this.touchViewBodyEl = this.touchViewEl.select('.modal-body', true).first();
17295         this.touchViewBodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17296         this.touchViewBodyEl.setStyle('overflow', 'auto');
17297         
17298         this.touchViewListGroup = this.touchViewBodyEl.select('.list-group', true).first();
17299         this.touchViewListGroup.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17300         
17301         this.touchViewFooterEl = this.touchViewEl.select('.modal-footer', true).first();
17302         this.touchViewFooterEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17303         
17304     },
17305     
17306     showTouchView : function()
17307     {
17308         if(this.disabled){
17309             return;
17310         }
17311         
17312         this.touchViewHeaderEl.hide();
17313
17314         if(this.modalTitle.length){
17315             this.touchViewHeaderEl.dom.innerHTML = this.modalTitle;
17316             this.touchViewHeaderEl.show();
17317         }
17318
17319         this.touchViewEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
17320         this.touchViewEl.show();
17321
17322         this.touchViewEl.select('.modal-dialog', true).first().setStyle({ margin : '0px', width : '100%'});
17323         
17324         //this.touchViewEl.select('.modal-dialog > .modal-content', true).first().setSize(
17325         //        Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
17326
17327         var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
17328
17329         if(this.modalTitle.length){
17330             bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
17331         }
17332         
17333         this.touchViewBodyEl.setHeight(bodyHeight);
17334
17335         if(this.animate){
17336             var _this = this;
17337             (function(){ _this.touchViewEl.addClass(['in','show']); }).defer(50);
17338         }else{
17339             this.touchViewEl.addClass(['in','show']);
17340         }
17341         
17342         if(this._touchViewMask){
17343             Roo.get(document.body).addClass("x-body-masked");
17344             this._touchViewMask.setSize(Roo.lib.Dom.getViewWidth(true),   Roo.lib.Dom.getViewHeight(true));
17345             this._touchViewMask.setStyle('z-index', 10000);
17346             this._touchViewMask.addClass('show');
17347         }
17348         
17349         this.doTouchViewQuery();
17350         
17351     },
17352     
17353     hideTouchView : function()
17354     {
17355         this.touchViewEl.removeClass(['in','show']);
17356
17357         if(this.animate){
17358             var _this = this;
17359             (function(){ _this.touchViewEl.setStyle('display', 'none'); }).defer(150);
17360         }else{
17361             this.touchViewEl.setStyle('display', 'none');
17362         }
17363         
17364         if(this._touchViewMask){
17365             this._touchViewMask.removeClass('show');
17366             Roo.get(document.body).removeClass("x-body-masked");
17367         }
17368     },
17369     
17370     setTouchViewValue : function()
17371     {
17372         if(this.multiple){
17373             this.clearItem();
17374         
17375             var _this = this;
17376
17377             Roo.each(this.tickItems, function(o){
17378                 this.addItem(o);
17379             }, this);
17380         }
17381         
17382         this.hideTouchView();
17383     },
17384     
17385     doTouchViewQuery : function()
17386     {
17387         var qe = {
17388             query: '',
17389             forceAll: true,
17390             combo: this,
17391             cancel:false
17392         };
17393         
17394         if(this.fireEvent('beforequery', qe) ===false || qe.cancel){
17395             return false;
17396         }
17397         
17398         if(!this.alwaysQuery || this.mode == 'local'){
17399             this.onTouchViewLoad();
17400             return;
17401         }
17402         
17403         this.store.load();
17404     },
17405     
17406     onTouchViewBeforeLoad : function(combo,opts)
17407     {
17408         return;
17409     },
17410
17411     // private
17412     onTouchViewLoad : function()
17413     {
17414         if(this.store.getCount() < 1){
17415             this.onTouchViewEmptyResults();
17416             return;
17417         }
17418         
17419         this.clearTouchView();
17420         
17421         var rawValue = this.getRawValue();
17422         
17423         var template = (this.multiple) ? Roo.bootstrap.ComboBox.listItemCheckbox : Roo.bootstrap.ComboBox.listItemRadio;
17424         
17425         this.tickItems = [];
17426         
17427         this.store.data.each(function(d, rowIndex){
17428             var row = this.touchViewListGroup.createChild(template);
17429             
17430             if(typeof(d.data.cls) != 'undefined' && d.data.cls.length){
17431                 row.addClass(d.data.cls);
17432             }
17433             
17434             if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
17435                 var cfg = {
17436                     data : d.data,
17437                     html : d.data[this.displayField]
17438                 };
17439                 
17440                 if(this.fireEvent('touchviewdisplay', this, cfg) !== false){
17441                     row.select('.roo-combobox-list-group-item-value', true).first().dom.innerHTML = cfg.html;
17442                 }
17443             }
17444             row.removeClass('selected');
17445             if(!this.multiple && this.valueField &&
17446                     typeof(d.data[this.valueField]) != 'undefined' && d.data[this.valueField] == this.getValue())
17447             {
17448                 // radio buttons..
17449                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
17450                 row.addClass('selected');
17451             }
17452             
17453             if(this.multiple && this.valueField &&
17454                     typeof(d.data[this.valueField]) != 'undefined' && this.getValue().indexOf(d.data[this.valueField]) != -1)
17455             {
17456                 
17457                 // checkboxes...
17458                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
17459                 this.tickItems.push(d.data);
17460             }
17461             
17462             row.on('click', this.onTouchViewClick, this, {row : row, rowIndex : rowIndex});
17463             
17464         }, this);
17465         
17466         var firstChecked = this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).first();
17467         
17468         var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
17469
17470         if(this.modalTitle.length){
17471             bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
17472         }
17473
17474         var listHeight = this.touchViewListGroup.getHeight() + this.touchViewBodyEl.getPadding('tb') * 2;
17475         
17476         if(this.mobile_restrict_height && listHeight < bodyHeight){
17477             this.touchViewBodyEl.setHeight(listHeight);
17478         }
17479         
17480         var _this = this;
17481         
17482         if(firstChecked && listHeight > bodyHeight){
17483             (function() { firstChecked.findParent('li').scrollIntoView(_this.touchViewListGroup.dom); }).defer(500);
17484         }
17485         
17486     },
17487     
17488     onTouchViewLoadException : function()
17489     {
17490         this.hideTouchView();
17491     },
17492     
17493     onTouchViewEmptyResults : function()
17494     {
17495         this.clearTouchView();
17496         
17497         this.touchViewListGroup.createChild(Roo.bootstrap.ComboBox.emptyResult);
17498         
17499         this.touchViewListGroup.select('.roo-combobox-touch-view-empty-result', true).first().dom.innerHTML = this.emptyResultText;
17500         
17501     },
17502     
17503     clearTouchView : function()
17504     {
17505         this.touchViewListGroup.dom.innerHTML = '';
17506     },
17507     
17508     onTouchViewClick : function(e, el, o)
17509     {
17510         e.preventDefault();
17511         
17512         var row = o.row;
17513         var rowIndex = o.rowIndex;
17514         
17515         var r = this.store.getAt(rowIndex);
17516         
17517         if(this.fireEvent('beforeselect', this, r, rowIndex) !== false){
17518             
17519             if(!this.multiple){
17520                 Roo.each(this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).elements, function(c){
17521                     c.dom.removeAttribute('checked');
17522                 }, this);
17523
17524                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
17525
17526                 this.setFromData(r.data);
17527
17528                 var close = this.closeTriggerEl();
17529
17530                 if(close){
17531                     close.show();
17532                 }
17533
17534                 this.hideTouchView();
17535
17536                 this.fireEvent('select', this, r, rowIndex);
17537
17538                 return;
17539             }
17540
17541             if(this.valueField && typeof(r.data[this.valueField]) != 'undefined' && this.getValue().indexOf(r.data[this.valueField]) != -1){
17542                 row.select('.roo-combobox-list-group-item-box > input', true).first().dom.removeAttribute('checked');
17543                 this.tickItems.splice(this.tickItems.indexOf(r.data), 1);
17544                 return;
17545             }
17546
17547             row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
17548             this.addItem(r.data);
17549             this.tickItems.push(r.data);
17550         }
17551     },
17552     
17553     getAutoCreateNativeIOS : function()
17554     {
17555         var cfg = {
17556             cls: 'form-group' //input-group,
17557         };
17558         
17559         var combobox =  {
17560             tag: 'select',
17561             cls : 'roo-ios-select'
17562         };
17563         
17564         if (this.name) {
17565             combobox.name = this.name;
17566         }
17567         
17568         if (this.disabled) {
17569             combobox.disabled = true;
17570         }
17571         
17572         var settings = this;
17573         
17574         ['xs','sm','md','lg'].map(function(size){
17575             if (settings[size]) {
17576                 cfg.cls += ' col-' + size + '-' + settings[size];
17577             }
17578         });
17579         
17580         cfg.cn = combobox;
17581         
17582         return cfg;
17583         
17584     },
17585     
17586     initIOSView : function()
17587     {
17588         this.store.on('load', this.onIOSViewLoad, this);
17589         
17590         return;
17591     },
17592     
17593     onIOSViewLoad : function()
17594     {
17595         if(this.store.getCount() < 1){
17596             return;
17597         }
17598         
17599         this.clearIOSView();
17600         
17601         if(this.allowBlank) {
17602             
17603             var default_text = '-- SELECT --';
17604             
17605             if(this.placeholder.length){
17606                 default_text = this.placeholder;
17607             }
17608             
17609             if(this.emptyTitle.length){
17610                 default_text += ' - ' + this.emptyTitle + ' -';
17611             }
17612             
17613             var opt = this.inputEl().createChild({
17614                 tag: 'option',
17615                 value : 0,
17616                 html : default_text
17617             });
17618             
17619             var o = {};
17620             o[this.valueField] = 0;
17621             o[this.displayField] = default_text;
17622             
17623             this.ios_options.push({
17624                 data : o,
17625                 el : opt
17626             });
17627             
17628         }
17629         
17630         this.store.data.each(function(d, rowIndex){
17631             
17632             var html = '';
17633             
17634             if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
17635                 html = d.data[this.displayField];
17636             }
17637             
17638             var value = '';
17639             
17640             if(this.valueField && typeof(d.data[this.valueField]) != 'undefined'){
17641                 value = d.data[this.valueField];
17642             }
17643             
17644             var option = {
17645                 tag: 'option',
17646                 value : value,
17647                 html : html
17648             };
17649             
17650             if(this.value == d.data[this.valueField]){
17651                 option['selected'] = true;
17652             }
17653             
17654             var opt = this.inputEl().createChild(option);
17655             
17656             this.ios_options.push({
17657                 data : d.data,
17658                 el : opt
17659             });
17660             
17661         }, this);
17662         
17663         this.inputEl().on('change', function(){
17664            this.fireEvent('select', this);
17665         }, this);
17666         
17667     },
17668     
17669     clearIOSView: function()
17670     {
17671         this.inputEl().dom.innerHTML = '';
17672         
17673         this.ios_options = [];
17674     },
17675     
17676     setIOSValue: function(v)
17677     {
17678         this.value = v;
17679         
17680         if(!this.ios_options){
17681             return;
17682         }
17683         
17684         Roo.each(this.ios_options, function(opts){
17685            
17686            opts.el.dom.removeAttribute('selected');
17687            
17688            if(opts.data[this.valueField] != v){
17689                return;
17690            }
17691            
17692            opts.el.dom.setAttribute('selected', true);
17693            
17694         }, this);
17695     }
17696
17697     /** 
17698     * @cfg {Boolean} grow 
17699     * @hide 
17700     */
17701     /** 
17702     * @cfg {Number} growMin 
17703     * @hide 
17704     */
17705     /** 
17706     * @cfg {Number} growMax 
17707     * @hide 
17708     */
17709     /**
17710      * @hide
17711      * @method autoSize
17712      */
17713 });
17714
17715 Roo.apply(Roo.bootstrap.ComboBox,  {
17716     
17717     header : {
17718         tag: 'div',
17719         cls: 'modal-header',
17720         cn: [
17721             {
17722                 tag: 'h4',
17723                 cls: 'modal-title'
17724             }
17725         ]
17726     },
17727     
17728     body : {
17729         tag: 'div',
17730         cls: 'modal-body',
17731         cn: [
17732             {
17733                 tag: 'ul',
17734                 cls: 'list-group'
17735             }
17736         ]
17737     },
17738     
17739     listItemRadio : {
17740         tag: 'li',
17741         cls: 'list-group-item',
17742         cn: [
17743             {
17744                 tag: 'span',
17745                 cls: 'roo-combobox-list-group-item-value'
17746             },
17747             {
17748                 tag: 'div',
17749                 cls: 'roo-combobox-list-group-item-box pull-xs-right radio-inline radio radio-info',
17750                 cn: [
17751                     {
17752                         tag: 'input',
17753                         type: 'radio'
17754                     },
17755                     {
17756                         tag: 'label'
17757                     }
17758                 ]
17759             }
17760         ]
17761     },
17762     
17763     listItemCheckbox : {
17764         tag: 'li',
17765         cls: 'list-group-item',
17766         cn: [
17767             {
17768                 tag: 'span',
17769                 cls: 'roo-combobox-list-group-item-value'
17770             },
17771             {
17772                 tag: 'div',
17773                 cls: 'roo-combobox-list-group-item-box pull-xs-right checkbox-inline checkbox checkbox-info',
17774                 cn: [
17775                     {
17776                         tag: 'input',
17777                         type: 'checkbox'
17778                     },
17779                     {
17780                         tag: 'label'
17781                     }
17782                 ]
17783             }
17784         ]
17785     },
17786     
17787     emptyResult : {
17788         tag: 'div',
17789         cls: 'alert alert-danger roo-combobox-touch-view-empty-result'
17790     },
17791     
17792     footer : {
17793         tag: 'div',
17794         cls: 'modal-footer',
17795         cn: [
17796             {
17797                 tag: 'div',
17798                 cls: 'row',
17799                 cn: [
17800                     {
17801                         tag: 'div',
17802                         cls: 'col-xs-6 text-left',
17803                         cn: {
17804                             tag: 'button',
17805                             cls: 'btn btn-danger roo-touch-view-cancel',
17806                             html: 'Cancel'
17807                         }
17808                     },
17809                     {
17810                         tag: 'div',
17811                         cls: 'col-xs-6 text-right',
17812                         cn: {
17813                             tag: 'button',
17814                             cls: 'btn btn-success roo-touch-view-ok',
17815                             html: 'OK'
17816                         }
17817                     }
17818                 ]
17819             }
17820         ]
17821         
17822     }
17823 });
17824
17825 Roo.apply(Roo.bootstrap.ComboBox,  {
17826     
17827     touchViewTemplate : {
17828         tag: 'div',
17829         cls: 'modal fade roo-combobox-touch-view',
17830         cn: [
17831             {
17832                 tag: 'div',
17833                 cls: 'modal-dialog',
17834                 style : 'position:fixed', // we have to fix position....
17835                 cn: [
17836                     {
17837                         tag: 'div',
17838                         cls: 'modal-content',
17839                         cn: [
17840                             Roo.bootstrap.ComboBox.header,
17841                             Roo.bootstrap.ComboBox.body,
17842                             Roo.bootstrap.ComboBox.footer
17843                         ]
17844                     }
17845                 ]
17846             }
17847         ]
17848     }
17849 });/*
17850  * Based on:
17851  * Ext JS Library 1.1.1
17852  * Copyright(c) 2006-2007, Ext JS, LLC.
17853  *
17854  * Originally Released Under LGPL - original licence link has changed is not relivant.
17855  *
17856  * Fork - LGPL
17857  * <script type="text/javascript">
17858  */
17859
17860 /**
17861  * @class Roo.View
17862  * @extends Roo.util.Observable
17863  * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template. 
17864  * This class also supports single and multi selection modes. <br>
17865  * Create a data model bound view:
17866  <pre><code>
17867  var store = new Roo.data.Store(...);
17868
17869  var view = new Roo.View({
17870     el : "my-element",
17871     tpl : '&lt;div id="{0}"&gt;{2} - {1}&lt;/div&gt;', // auto create template
17872  
17873     singleSelect: true,
17874     selectedClass: "ydataview-selected",
17875     store: store
17876  });
17877
17878  // listen for node click?
17879  view.on("click", function(vw, index, node, e){
17880  alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
17881  });
17882
17883  // load XML data
17884  dataModel.load("foobar.xml");
17885  </code></pre>
17886  For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
17887  * <br><br>
17888  * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
17889  * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
17890  * 
17891  * Note: old style constructor is still suported (container, template, config)
17892  * 
17893  * @constructor
17894  * Create a new View
17895  * @param {Object} config The config object
17896  * 
17897  */
17898 Roo.View = function(config, depreciated_tpl, depreciated_config){
17899     
17900     this.parent = false;
17901     
17902     if (typeof(depreciated_tpl) == 'undefined') {
17903         // new way.. - universal constructor.
17904         Roo.apply(this, config);
17905         this.el  = Roo.get(this.el);
17906     } else {
17907         // old format..
17908         this.el  = Roo.get(config);
17909         this.tpl = depreciated_tpl;
17910         Roo.apply(this, depreciated_config);
17911     }
17912     this.wrapEl  = this.el.wrap().wrap();
17913     ///this.el = this.wrapEla.appendChild(document.createElement("div"));
17914     
17915     
17916     if(typeof(this.tpl) == "string"){
17917         this.tpl = new Roo.Template(this.tpl);
17918     } else {
17919         // support xtype ctors..
17920         this.tpl = new Roo.factory(this.tpl, Roo);
17921     }
17922     
17923     
17924     this.tpl.compile();
17925     
17926     /** @private */
17927     this.addEvents({
17928         /**
17929          * @event beforeclick
17930          * Fires before a click is processed. Returns false to cancel the default action.
17931          * @param {Roo.View} this
17932          * @param {Number} index The index of the target node
17933          * @param {HTMLElement} node The target node
17934          * @param {Roo.EventObject} e The raw event object
17935          */
17936             "beforeclick" : true,
17937         /**
17938          * @event click
17939          * Fires when a template node is clicked.
17940          * @param {Roo.View} this
17941          * @param {Number} index The index of the target node
17942          * @param {HTMLElement} node The target node
17943          * @param {Roo.EventObject} e The raw event object
17944          */
17945             "click" : true,
17946         /**
17947          * @event dblclick
17948          * Fires when a template node is double clicked.
17949          * @param {Roo.View} this
17950          * @param {Number} index The index of the target node
17951          * @param {HTMLElement} node The target node
17952          * @param {Roo.EventObject} e The raw event object
17953          */
17954             "dblclick" : true,
17955         /**
17956          * @event contextmenu
17957          * Fires when a template node is right clicked.
17958          * @param {Roo.View} this
17959          * @param {Number} index The index of the target node
17960          * @param {HTMLElement} node The target node
17961          * @param {Roo.EventObject} e The raw event object
17962          */
17963             "contextmenu" : true,
17964         /**
17965          * @event selectionchange
17966          * Fires when the selected nodes change.
17967          * @param {Roo.View} this
17968          * @param {Array} selections Array of the selected nodes
17969          */
17970             "selectionchange" : true,
17971     
17972         /**
17973          * @event beforeselect
17974          * Fires before a selection is made. If any handlers return false, the selection is cancelled.
17975          * @param {Roo.View} this
17976          * @param {HTMLElement} node The node to be selected
17977          * @param {Array} selections Array of currently selected nodes
17978          */
17979             "beforeselect" : true,
17980         /**
17981          * @event preparedata
17982          * Fires on every row to render, to allow you to change the data.
17983          * @param {Roo.View} this
17984          * @param {Object} data to be rendered (change this)
17985          */
17986           "preparedata" : true
17987           
17988           
17989         });
17990
17991
17992
17993     this.el.on({
17994         "click": this.onClick,
17995         "dblclick": this.onDblClick,
17996         "contextmenu": this.onContextMenu,
17997         scope:this
17998     });
17999
18000     this.selections = [];
18001     this.nodes = [];
18002     this.cmp = new Roo.CompositeElementLite([]);
18003     if(this.store){
18004         this.store = Roo.factory(this.store, Roo.data);
18005         this.setStore(this.store, true);
18006     }
18007     
18008     if ( this.footer && this.footer.xtype) {
18009            
18010          var fctr = this.wrapEl.appendChild(document.createElement("div"));
18011         
18012         this.footer.dataSource = this.store;
18013         this.footer.container = fctr;
18014         this.footer = Roo.factory(this.footer, Roo);
18015         fctr.insertFirst(this.el);
18016         
18017         // this is a bit insane - as the paging toolbar seems to detach the el..
18018 //        dom.parentNode.parentNode.parentNode
18019          // they get detached?
18020     }
18021     
18022     
18023     Roo.View.superclass.constructor.call(this);
18024     
18025     
18026 };
18027
18028 Roo.extend(Roo.View, Roo.util.Observable, {
18029     
18030      /**
18031      * @cfg {Roo.data.Store} store Data store to load data from.
18032      */
18033     store : false,
18034     
18035     /**
18036      * @cfg {String|Roo.Element} el The container element.
18037      */
18038     el : '',
18039     
18040     /**
18041      * @cfg {String|Roo.Template} tpl The template used by this View 
18042      */
18043     tpl : false,
18044     /**
18045      * @cfg {String} dataName the named area of the template to use as the data area
18046      *                          Works with domtemplates roo-name="name"
18047      */
18048     dataName: false,
18049     /**
18050      * @cfg {String} selectedClass The css class to add to selected nodes
18051      */
18052     selectedClass : "x-view-selected",
18053      /**
18054      * @cfg {String} emptyText The empty text to show when nothing is loaded.
18055      */
18056     emptyText : "",
18057     
18058     /**
18059      * @cfg {String} text to display on mask (default Loading)
18060      */
18061     mask : false,
18062     /**
18063      * @cfg {Boolean} multiSelect Allow multiple selection
18064      */
18065     multiSelect : false,
18066     /**
18067      * @cfg {Boolean} singleSelect Allow single selection
18068      */
18069     singleSelect:  false,
18070     
18071     /**
18072      * @cfg {Boolean} toggleSelect - selecting 
18073      */
18074     toggleSelect : false,
18075     
18076     /**
18077      * @cfg {Boolean} tickable - selecting 
18078      */
18079     tickable : false,
18080     
18081     /**
18082      * Returns the element this view is bound to.
18083      * @return {Roo.Element}
18084      */
18085     getEl : function(){
18086         return this.wrapEl;
18087     },
18088     
18089     
18090
18091     /**
18092      * Refreshes the view. - called by datachanged on the store. - do not call directly.
18093      */
18094     refresh : function(){
18095         //Roo.log('refresh');
18096         var t = this.tpl;
18097         
18098         // if we are using something like 'domtemplate', then
18099         // the what gets used is:
18100         // t.applySubtemplate(NAME, data, wrapping data..)
18101         // the outer template then get' applied with
18102         //     the store 'extra data'
18103         // and the body get's added to the
18104         //      roo-name="data" node?
18105         //      <span class='roo-tpl-{name}'></span> ?????
18106         
18107         
18108         
18109         this.clearSelections();
18110         this.el.update("");
18111         var html = [];
18112         var records = this.store.getRange();
18113         if(records.length < 1) {
18114             
18115             // is this valid??  = should it render a template??
18116             
18117             this.el.update(this.emptyText);
18118             return;
18119         }
18120         var el = this.el;
18121         if (this.dataName) {
18122             this.el.update(t.apply(this.store.meta)); //????
18123             el = this.el.child('.roo-tpl-' + this.dataName);
18124         }
18125         
18126         for(var i = 0, len = records.length; i < len; i++){
18127             var data = this.prepareData(records[i].data, i, records[i]);
18128             this.fireEvent("preparedata", this, data, i, records[i]);
18129             
18130             var d = Roo.apply({}, data);
18131             
18132             if(this.tickable){
18133                 Roo.apply(d, {'roo-id' : Roo.id()});
18134                 
18135                 var _this = this;
18136             
18137                 Roo.each(this.parent.item, function(item){
18138                     if(item[_this.parent.valueField] != data[_this.parent.valueField]){
18139                         return;
18140                     }
18141                     Roo.apply(d, {'roo-data-checked' : 'checked'});
18142                 });
18143             }
18144             
18145             html[html.length] = Roo.util.Format.trim(
18146                 this.dataName ?
18147                     t.applySubtemplate(this.dataName, d, this.store.meta) :
18148                     t.apply(d)
18149             );
18150         }
18151         
18152         
18153         
18154         el.update(html.join(""));
18155         this.nodes = el.dom.childNodes;
18156         this.updateIndexes(0);
18157     },
18158     
18159
18160     /**
18161      * Function to override to reformat the data that is sent to
18162      * the template for each node.
18163      * DEPRICATED - use the preparedata event handler.
18164      * @param {Array/Object} data The raw data (array of colData for a data model bound view or
18165      * a JSON object for an UpdateManager bound view).
18166      */
18167     prepareData : function(data, index, record)
18168     {
18169         this.fireEvent("preparedata", this, data, index, record);
18170         return data;
18171     },
18172
18173     onUpdate : function(ds, record){
18174         // Roo.log('on update');   
18175         this.clearSelections();
18176         var index = this.store.indexOf(record);
18177         var n = this.nodes[index];
18178         this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
18179         n.parentNode.removeChild(n);
18180         this.updateIndexes(index, index);
18181     },
18182
18183     
18184     
18185 // --------- FIXME     
18186     onAdd : function(ds, records, index)
18187     {
18188         //Roo.log(['on Add', ds, records, index] );        
18189         this.clearSelections();
18190         if(this.nodes.length == 0){
18191             this.refresh();
18192             return;
18193         }
18194         var n = this.nodes[index];
18195         for(var i = 0, len = records.length; i < len; i++){
18196             var d = this.prepareData(records[i].data, i, records[i]);
18197             if(n){
18198                 this.tpl.insertBefore(n, d);
18199             }else{
18200                 
18201                 this.tpl.append(this.el, d);
18202             }
18203         }
18204         this.updateIndexes(index);
18205     },
18206
18207     onRemove : function(ds, record, index){
18208        // Roo.log('onRemove');
18209         this.clearSelections();
18210         var el = this.dataName  ?
18211             this.el.child('.roo-tpl-' + this.dataName) :
18212             this.el; 
18213         
18214         el.dom.removeChild(this.nodes[index]);
18215         this.updateIndexes(index);
18216     },
18217
18218     /**
18219      * Refresh an individual node.
18220      * @param {Number} index
18221      */
18222     refreshNode : function(index){
18223         this.onUpdate(this.store, this.store.getAt(index));
18224     },
18225
18226     updateIndexes : function(startIndex, endIndex){
18227         var ns = this.nodes;
18228         startIndex = startIndex || 0;
18229         endIndex = endIndex || ns.length - 1;
18230         for(var i = startIndex; i <= endIndex; i++){
18231             ns[i].nodeIndex = i;
18232         }
18233     },
18234
18235     /**
18236      * Changes the data store this view uses and refresh the view.
18237      * @param {Store} store
18238      */
18239     setStore : function(store, initial){
18240         if(!initial && this.store){
18241             this.store.un("datachanged", this.refresh);
18242             this.store.un("add", this.onAdd);
18243             this.store.un("remove", this.onRemove);
18244             this.store.un("update", this.onUpdate);
18245             this.store.un("clear", this.refresh);
18246             this.store.un("beforeload", this.onBeforeLoad);
18247             this.store.un("load", this.onLoad);
18248             this.store.un("loadexception", this.onLoad);
18249         }
18250         if(store){
18251           
18252             store.on("datachanged", this.refresh, this);
18253             store.on("add", this.onAdd, this);
18254             store.on("remove", this.onRemove, this);
18255             store.on("update", this.onUpdate, this);
18256             store.on("clear", this.refresh, this);
18257             store.on("beforeload", this.onBeforeLoad, this);
18258             store.on("load", this.onLoad, this);
18259             store.on("loadexception", this.onLoad, this);
18260         }
18261         
18262         if(store){
18263             this.refresh();
18264         }
18265     },
18266     /**
18267      * onbeforeLoad - masks the loading area.
18268      *
18269      */
18270     onBeforeLoad : function(store,opts)
18271     {
18272          //Roo.log('onBeforeLoad');   
18273         if (!opts.add) {
18274             this.el.update("");
18275         }
18276         this.el.mask(this.mask ? this.mask : "Loading" ); 
18277     },
18278     onLoad : function ()
18279     {
18280         this.el.unmask();
18281     },
18282     
18283
18284     /**
18285      * Returns the template node the passed child belongs to or null if it doesn't belong to one.
18286      * @param {HTMLElement} node
18287      * @return {HTMLElement} The template node
18288      */
18289     findItemFromChild : function(node){
18290         var el = this.dataName  ?
18291             this.el.child('.roo-tpl-' + this.dataName,true) :
18292             this.el.dom; 
18293         
18294         if(!node || node.parentNode == el){
18295                     return node;
18296             }
18297             var p = node.parentNode;
18298             while(p && p != el){
18299             if(p.parentNode == el){
18300                 return p;
18301             }
18302             p = p.parentNode;
18303         }
18304             return null;
18305     },
18306
18307     /** @ignore */
18308     onClick : function(e){
18309         var item = this.findItemFromChild(e.getTarget());
18310         if(item){
18311             var index = this.indexOf(item);
18312             if(this.onItemClick(item, index, e) !== false){
18313                 this.fireEvent("click", this, index, item, e);
18314             }
18315         }else{
18316             this.clearSelections();
18317         }
18318     },
18319
18320     /** @ignore */
18321     onContextMenu : function(e){
18322         var item = this.findItemFromChild(e.getTarget());
18323         if(item){
18324             this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
18325         }
18326     },
18327
18328     /** @ignore */
18329     onDblClick : function(e){
18330         var item = this.findItemFromChild(e.getTarget());
18331         if(item){
18332             this.fireEvent("dblclick", this, this.indexOf(item), item, e);
18333         }
18334     },
18335
18336     onItemClick : function(item, index, e)
18337     {
18338         if(this.fireEvent("beforeclick", this, index, item, e) === false){
18339             return false;
18340         }
18341         if (this.toggleSelect) {
18342             var m = this.isSelected(item) ? 'unselect' : 'select';
18343             //Roo.log(m);
18344             var _t = this;
18345             _t[m](item, true, false);
18346             return true;
18347         }
18348         if(this.multiSelect || this.singleSelect){
18349             if(this.multiSelect && e.shiftKey && this.lastSelection){
18350                 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
18351             }else{
18352                 this.select(item, this.multiSelect && e.ctrlKey);
18353                 this.lastSelection = item;
18354             }
18355             
18356             if(!this.tickable){
18357                 e.preventDefault();
18358             }
18359             
18360         }
18361         return true;
18362     },
18363
18364     /**
18365      * Get the number of selected nodes.
18366      * @return {Number}
18367      */
18368     getSelectionCount : function(){
18369         return this.selections.length;
18370     },
18371
18372     /**
18373      * Get the currently selected nodes.
18374      * @return {Array} An array of HTMLElements
18375      */
18376     getSelectedNodes : function(){
18377         return this.selections;
18378     },
18379
18380     /**
18381      * Get the indexes of the selected nodes.
18382      * @return {Array}
18383      */
18384     getSelectedIndexes : function(){
18385         var indexes = [], s = this.selections;
18386         for(var i = 0, len = s.length; i < len; i++){
18387             indexes.push(s[i].nodeIndex);
18388         }
18389         return indexes;
18390     },
18391
18392     /**
18393      * Clear all selections
18394      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
18395      */
18396     clearSelections : function(suppressEvent){
18397         if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
18398             this.cmp.elements = this.selections;
18399             this.cmp.removeClass(this.selectedClass);
18400             this.selections = [];
18401             if(!suppressEvent){
18402                 this.fireEvent("selectionchange", this, this.selections);
18403             }
18404         }
18405     },
18406
18407     /**
18408      * Returns true if the passed node is selected
18409      * @param {HTMLElement/Number} node The node or node index
18410      * @return {Boolean}
18411      */
18412     isSelected : function(node){
18413         var s = this.selections;
18414         if(s.length < 1){
18415             return false;
18416         }
18417         node = this.getNode(node);
18418         return s.indexOf(node) !== -1;
18419     },
18420
18421     /**
18422      * Selects nodes.
18423      * @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
18424      * @param {Boolean} keepExisting (optional) true to keep existing selections
18425      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
18426      */
18427     select : function(nodeInfo, keepExisting, suppressEvent){
18428         if(nodeInfo instanceof Array){
18429             if(!keepExisting){
18430                 this.clearSelections(true);
18431             }
18432             for(var i = 0, len = nodeInfo.length; i < len; i++){
18433                 this.select(nodeInfo[i], true, true);
18434             }
18435             return;
18436         } 
18437         var node = this.getNode(nodeInfo);
18438         if(!node || this.isSelected(node)){
18439             return; // already selected.
18440         }
18441         if(!keepExisting){
18442             this.clearSelections(true);
18443         }
18444         
18445         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
18446             Roo.fly(node).addClass(this.selectedClass);
18447             this.selections.push(node);
18448             if(!suppressEvent){
18449                 this.fireEvent("selectionchange", this, this.selections);
18450             }
18451         }
18452         
18453         
18454     },
18455       /**
18456      * Unselects nodes.
18457      * @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
18458      * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
18459      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
18460      */
18461     unselect : function(nodeInfo, keepExisting, suppressEvent)
18462     {
18463         if(nodeInfo instanceof Array){
18464             Roo.each(this.selections, function(s) {
18465                 this.unselect(s, nodeInfo);
18466             }, this);
18467             return;
18468         }
18469         var node = this.getNode(nodeInfo);
18470         if(!node || !this.isSelected(node)){
18471             //Roo.log("not selected");
18472             return; // not selected.
18473         }
18474         // fireevent???
18475         var ns = [];
18476         Roo.each(this.selections, function(s) {
18477             if (s == node ) {
18478                 Roo.fly(node).removeClass(this.selectedClass);
18479
18480                 return;
18481             }
18482             ns.push(s);
18483         },this);
18484         
18485         this.selections= ns;
18486         this.fireEvent("selectionchange", this, this.selections);
18487     },
18488
18489     /**
18490      * Gets a template node.
18491      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
18492      * @return {HTMLElement} The node or null if it wasn't found
18493      */
18494     getNode : function(nodeInfo){
18495         if(typeof nodeInfo == "string"){
18496             return document.getElementById(nodeInfo);
18497         }else if(typeof nodeInfo == "number"){
18498             return this.nodes[nodeInfo];
18499         }
18500         return nodeInfo;
18501     },
18502
18503     /**
18504      * Gets a range template nodes.
18505      * @param {Number} startIndex
18506      * @param {Number} endIndex
18507      * @return {Array} An array of nodes
18508      */
18509     getNodes : function(start, end){
18510         var ns = this.nodes;
18511         start = start || 0;
18512         end = typeof end == "undefined" ? ns.length - 1 : end;
18513         var nodes = [];
18514         if(start <= end){
18515             for(var i = start; i <= end; i++){
18516                 nodes.push(ns[i]);
18517             }
18518         } else{
18519             for(var i = start; i >= end; i--){
18520                 nodes.push(ns[i]);
18521             }
18522         }
18523         return nodes;
18524     },
18525
18526     /**
18527      * Finds the index of the passed node
18528      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
18529      * @return {Number} The index of the node or -1
18530      */
18531     indexOf : function(node){
18532         node = this.getNode(node);
18533         if(typeof node.nodeIndex == "number"){
18534             return node.nodeIndex;
18535         }
18536         var ns = this.nodes;
18537         for(var i = 0, len = ns.length; i < len; i++){
18538             if(ns[i] == node){
18539                 return i;
18540             }
18541         }
18542         return -1;
18543     }
18544 });
18545 /*
18546  * - LGPL
18547  *
18548  * based on jquery fullcalendar
18549  * 
18550  */
18551
18552 Roo.bootstrap = Roo.bootstrap || {};
18553 /**
18554  * @class Roo.bootstrap.Calendar
18555  * @extends Roo.bootstrap.Component
18556  * Bootstrap Calendar class
18557  * @cfg {Boolean} loadMask (true|false) default false
18558  * @cfg {Object} header generate the user specific header of the calendar, default false
18559
18560  * @constructor
18561  * Create a new Container
18562  * @param {Object} config The config object
18563  */
18564
18565
18566
18567 Roo.bootstrap.Calendar = function(config){
18568     Roo.bootstrap.Calendar.superclass.constructor.call(this, config);
18569      this.addEvents({
18570         /**
18571              * @event select
18572              * Fires when a date is selected
18573              * @param {DatePicker} this
18574              * @param {Date} date The selected date
18575              */
18576         'select': true,
18577         /**
18578              * @event monthchange
18579              * Fires when the displayed month changes 
18580              * @param {DatePicker} this
18581              * @param {Date} date The selected month
18582              */
18583         'monthchange': true,
18584         /**
18585              * @event evententer
18586              * Fires when mouse over an event
18587              * @param {Calendar} this
18588              * @param {event} Event
18589              */
18590         'evententer': true,
18591         /**
18592              * @event eventleave
18593              * Fires when the mouse leaves an
18594              * @param {Calendar} this
18595              * @param {event}
18596              */
18597         'eventleave': true,
18598         /**
18599              * @event eventclick
18600              * Fires when the mouse click an
18601              * @param {Calendar} this
18602              * @param {event}
18603              */
18604         'eventclick': true
18605         
18606     });
18607
18608 };
18609
18610 Roo.extend(Roo.bootstrap.Calendar, Roo.bootstrap.Component,  {
18611     
18612      /**
18613      * @cfg {Number} startDay
18614      * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
18615      */
18616     startDay : 0,
18617     
18618     loadMask : false,
18619     
18620     header : false,
18621       
18622     getAutoCreate : function(){
18623         
18624         
18625         var fc_button = function(name, corner, style, content ) {
18626             return Roo.apply({},{
18627                 tag : 'span',
18628                 cls : 'fc-button fc-button-'+name+' fc-state-default ' + 
18629                          (corner.length ?
18630                             'fc-corner-' + corner.split(' ').join(' fc-corner-') :
18631                             ''
18632                         ),
18633                 html : '<SPAN class="fc-text-'+style+ '">'+content +'</SPAN>',
18634                 unselectable: 'on'
18635             });
18636         };
18637         
18638         var header = {};
18639         
18640         if(!this.header){
18641             header = {
18642                 tag : 'table',
18643                 cls : 'fc-header',
18644                 style : 'width:100%',
18645                 cn : [
18646                     {
18647                         tag: 'tr',
18648                         cn : [
18649                             {
18650                                 tag : 'td',
18651                                 cls : 'fc-header-left',
18652                                 cn : [
18653                                     fc_button('prev', 'left', 'arrow', '&#8249;' ),
18654                                     fc_button('next', 'right', 'arrow', '&#8250;' ),
18655                                     { tag: 'span', cls: 'fc-header-space' },
18656                                     fc_button('today', 'left right', '', 'today' )  // neds state disabled..
18657
18658
18659                                 ]
18660                             },
18661
18662                             {
18663                                 tag : 'td',
18664                                 cls : 'fc-header-center',
18665                                 cn : [
18666                                     {
18667                                         tag: 'span',
18668                                         cls: 'fc-header-title',
18669                                         cn : {
18670                                             tag: 'H2',
18671                                             html : 'month / year'
18672                                         }
18673                                     }
18674
18675                                 ]
18676                             },
18677                             {
18678                                 tag : 'td',
18679                                 cls : 'fc-header-right',
18680                                 cn : [
18681                               /*      fc_button('month', 'left', '', 'month' ),
18682                                     fc_button('week', '', '', 'week' ),
18683                                     fc_button('day', 'right', '', 'day' )
18684                                 */    
18685
18686                                 ]
18687                             }
18688
18689                         ]
18690                     }
18691                 ]
18692             };
18693         }
18694         
18695         header = this.header;
18696         
18697        
18698         var cal_heads = function() {
18699             var ret = [];
18700             // fixme - handle this.
18701             
18702             for (var i =0; i < Date.dayNames.length; i++) {
18703                 var d = Date.dayNames[i];
18704                 ret.push({
18705                     tag: 'th',
18706                     cls : 'fc-day-header fc-' + d.substring(0,3).toLowerCase() + ' fc-widget-header',
18707                     html : d.substring(0,3)
18708                 });
18709                 
18710             }
18711             ret[0].cls += ' fc-first';
18712             ret[6].cls += ' fc-last';
18713             return ret;
18714         };
18715         var cal_cell = function(n) {
18716             return  {
18717                 tag: 'td',
18718                 cls : 'fc-day fc-'+n + ' fc-widget-content', ///fc-other-month fc-past
18719                 cn : [
18720                     {
18721                         cn : [
18722                             {
18723                                 cls: 'fc-day-number',
18724                                 html: 'D'
18725                             },
18726                             {
18727                                 cls: 'fc-day-content',
18728                              
18729                                 cn : [
18730                                      {
18731                                         style: 'position: relative;' // height: 17px;
18732                                     }
18733                                 ]
18734                             }
18735                             
18736                             
18737                         ]
18738                     }
18739                 ]
18740                 
18741             }
18742         };
18743         var cal_rows = function() {
18744             
18745             var ret = [];
18746             for (var r = 0; r < 6; r++) {
18747                 var row= {
18748                     tag : 'tr',
18749                     cls : 'fc-week',
18750                     cn : []
18751                 };
18752                 
18753                 for (var i =0; i < Date.dayNames.length; i++) {
18754                     var d = Date.dayNames[i];
18755                     row.cn.push(cal_cell(d.substring(0,3).toLowerCase()));
18756
18757                 }
18758                 row.cn[0].cls+=' fc-first';
18759                 row.cn[0].cn[0].style = 'min-height:90px';
18760                 row.cn[6].cls+=' fc-last';
18761                 ret.push(row);
18762                 
18763             }
18764             ret[0].cls += ' fc-first';
18765             ret[4].cls += ' fc-prev-last';
18766             ret[5].cls += ' fc-last';
18767             return ret;
18768             
18769         };
18770         
18771         var cal_table = {
18772             tag: 'table',
18773             cls: 'fc-border-separate',
18774             style : 'width:100%',
18775             cellspacing  : 0,
18776             cn : [
18777                 { 
18778                     tag: 'thead',
18779                     cn : [
18780                         { 
18781                             tag: 'tr',
18782                             cls : 'fc-first fc-last',
18783                             cn : cal_heads()
18784                         }
18785                     ]
18786                 },
18787                 { 
18788                     tag: 'tbody',
18789                     cn : cal_rows()
18790                 }
18791                   
18792             ]
18793         };
18794          
18795          var cfg = {
18796             cls : 'fc fc-ltr',
18797             cn : [
18798                 header,
18799                 {
18800                     cls : 'fc-content',
18801                     style : "position: relative;",
18802                     cn : [
18803                         {
18804                             cls : 'fc-view fc-view-month fc-grid',
18805                             style : 'position: relative',
18806                             unselectable : 'on',
18807                             cn : [
18808                                 {
18809                                     cls : 'fc-event-container',
18810                                     style : 'position:absolute;z-index:8;top:0;left:0;'
18811                                 },
18812                                 cal_table
18813                             ]
18814                         }
18815                     ]
18816     
18817                 }
18818            ] 
18819             
18820         };
18821         
18822          
18823         
18824         return cfg;
18825     },
18826     
18827     
18828     initEvents : function()
18829     {
18830         if(!this.store){
18831             throw "can not find store for calendar";
18832         }
18833         
18834         var mark = {
18835             tag: "div",
18836             cls:"x-dlg-mask",
18837             style: "text-align:center",
18838             cn: [
18839                 {
18840                     tag: "div",
18841                     style: "background-color:white;width:50%;margin:250 auto",
18842                     cn: [
18843                         {
18844                             tag: "img",
18845                             src: Roo.rootURL + '/images/ux/lightbox/loading.gif' 
18846                         },
18847                         {
18848                             tag: "span",
18849                             html: "Loading"
18850                         }
18851                         
18852                     ]
18853                 }
18854             ]
18855         };
18856         this.maskEl = Roo.DomHelper.append(this.el.select('.fc-content', true).first(), mark, true);
18857         
18858         var size = this.el.select('.fc-content', true).first().getSize();
18859         this.maskEl.setSize(size.width, size.height);
18860         this.maskEl.enableDisplayMode("block");
18861         if(!this.loadMask){
18862             this.maskEl.hide();
18863         }
18864         
18865         this.store = Roo.factory(this.store, Roo.data);
18866         this.store.on('load', this.onLoad, this);
18867         this.store.on('beforeload', this.onBeforeLoad, this);
18868         
18869         this.resize();
18870         
18871         this.cells = this.el.select('.fc-day',true);
18872         //Roo.log(this.cells);
18873         this.textNodes = this.el.query('.fc-day-number');
18874         this.cells.addClassOnOver('fc-state-hover');
18875         
18876         this.el.select('.fc-button-prev',true).on('click', this.showPrevMonth, this);
18877         this.el.select('.fc-button-next',true).on('click', this.showNextMonth, this);
18878         this.el.select('.fc-button-today',true).on('click', this.showToday, this);
18879         this.el.select('.fc-button',true).addClassOnOver('fc-state-hover');
18880         
18881         this.on('monthchange', this.onMonthChange, this);
18882         
18883         this.update(new Date().clearTime());
18884     },
18885     
18886     resize : function() {
18887         var sz  = this.el.getSize();
18888         
18889         this.el.select('.fc-day-header',true).setWidth(sz.width / 7);
18890         this.el.select('.fc-day-content div',true).setHeight(34);
18891     },
18892     
18893     
18894     // private
18895     showPrevMonth : function(e){
18896         this.update(this.activeDate.add("mo", -1));
18897     },
18898     showToday : function(e){
18899         this.update(new Date().clearTime());
18900     },
18901     // private
18902     showNextMonth : function(e){
18903         this.update(this.activeDate.add("mo", 1));
18904     },
18905
18906     // private
18907     showPrevYear : function(){
18908         this.update(this.activeDate.add("y", -1));
18909     },
18910
18911     // private
18912     showNextYear : function(){
18913         this.update(this.activeDate.add("y", 1));
18914     },
18915
18916     
18917    // private
18918     update : function(date)
18919     {
18920         var vd = this.activeDate;
18921         this.activeDate = date;
18922 //        if(vd && this.el){
18923 //            var t = date.getTime();
18924 //            if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
18925 //                Roo.log('using add remove');
18926 //                
18927 //                this.fireEvent('monthchange', this, date);
18928 //                
18929 //                this.cells.removeClass("fc-state-highlight");
18930 //                this.cells.each(function(c){
18931 //                   if(c.dateValue == t){
18932 //                       c.addClass("fc-state-highlight");
18933 //                       setTimeout(function(){
18934 //                            try{c.dom.firstChild.focus();}catch(e){}
18935 //                       }, 50);
18936 //                       return false;
18937 //                   }
18938 //                   return true;
18939 //                });
18940 //                return;
18941 //            }
18942 //        }
18943         
18944         var days = date.getDaysInMonth();
18945         
18946         var firstOfMonth = date.getFirstDateOfMonth();
18947         var startingPos = firstOfMonth.getDay()-this.startDay;
18948         
18949         if(startingPos < this.startDay){
18950             startingPos += 7;
18951         }
18952         
18953         var pm = date.add(Date.MONTH, -1);
18954         var prevStart = pm.getDaysInMonth()-startingPos;
18955 //        
18956         this.cells = this.el.select('.fc-day',true);
18957         this.textNodes = this.el.query('.fc-day-number');
18958         this.cells.addClassOnOver('fc-state-hover');
18959         
18960         var cells = this.cells.elements;
18961         var textEls = this.textNodes;
18962         
18963         Roo.each(cells, function(cell){
18964             cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
18965         });
18966         
18967         days += startingPos;
18968
18969         // convert everything to numbers so it's fast
18970         var day = 86400000;
18971         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
18972         //Roo.log(d);
18973         //Roo.log(pm);
18974         //Roo.log(prevStart);
18975         
18976         var today = new Date().clearTime().getTime();
18977         var sel = date.clearTime().getTime();
18978         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
18979         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
18980         var ddMatch = this.disabledDatesRE;
18981         var ddText = this.disabledDatesText;
18982         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
18983         var ddaysText = this.disabledDaysText;
18984         var format = this.format;
18985         
18986         var setCellClass = function(cal, cell){
18987             cell.row = 0;
18988             cell.events = [];
18989             cell.more = [];
18990             //Roo.log('set Cell Class');
18991             cell.title = "";
18992             var t = d.getTime();
18993             
18994             //Roo.log(d);
18995             
18996             cell.dateValue = t;
18997             if(t == today){
18998                 cell.className += " fc-today";
18999                 cell.className += " fc-state-highlight";
19000                 cell.title = cal.todayText;
19001             }
19002             if(t == sel){
19003                 // disable highlight in other month..
19004                 //cell.className += " fc-state-highlight";
19005                 
19006             }
19007             // disabling
19008             if(t < min) {
19009                 cell.className = " fc-state-disabled";
19010                 cell.title = cal.minText;
19011                 return;
19012             }
19013             if(t > max) {
19014                 cell.className = " fc-state-disabled";
19015                 cell.title = cal.maxText;
19016                 return;
19017             }
19018             if(ddays){
19019                 if(ddays.indexOf(d.getDay()) != -1){
19020                     cell.title = ddaysText;
19021                     cell.className = " fc-state-disabled";
19022                 }
19023             }
19024             if(ddMatch && format){
19025                 var fvalue = d.dateFormat(format);
19026                 if(ddMatch.test(fvalue)){
19027                     cell.title = ddText.replace("%0", fvalue);
19028                     cell.className = " fc-state-disabled";
19029                 }
19030             }
19031             
19032             if (!cell.initialClassName) {
19033                 cell.initialClassName = cell.dom.className;
19034             }
19035             
19036             cell.dom.className = cell.initialClassName  + ' ' +  cell.className;
19037         };
19038
19039         var i = 0;
19040         
19041         for(; i < startingPos; i++) {
19042             textEls[i].innerHTML = (++prevStart);
19043             d.setDate(d.getDate()+1);
19044             
19045             cells[i].className = "fc-past fc-other-month";
19046             setCellClass(this, cells[i]);
19047         }
19048         
19049         var intDay = 0;
19050         
19051         for(; i < days; i++){
19052             intDay = i - startingPos + 1;
19053             textEls[i].innerHTML = (intDay);
19054             d.setDate(d.getDate()+1);
19055             
19056             cells[i].className = ''; // "x-date-active";
19057             setCellClass(this, cells[i]);
19058         }
19059         var extraDays = 0;
19060         
19061         for(; i < 42; i++) {
19062             textEls[i].innerHTML = (++extraDays);
19063             d.setDate(d.getDate()+1);
19064             
19065             cells[i].className = "fc-future fc-other-month";
19066             setCellClass(this, cells[i]);
19067         }
19068         
19069         this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
19070         
19071         var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
19072         
19073         this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
19074         this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
19075         
19076         if(totalRows != 6){
19077             this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
19078             this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
19079         }
19080         
19081         this.fireEvent('monthchange', this, date);
19082         
19083         
19084         /*
19085         if(!this.internalRender){
19086             var main = this.el.dom.firstChild;
19087             var w = main.offsetWidth;
19088             this.el.setWidth(w + this.el.getBorderWidth("lr"));
19089             Roo.fly(main).setWidth(w);
19090             this.internalRender = true;
19091             // opera does not respect the auto grow header center column
19092             // then, after it gets a width opera refuses to recalculate
19093             // without a second pass
19094             if(Roo.isOpera && !this.secondPass){
19095                 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
19096                 this.secondPass = true;
19097                 this.update.defer(10, this, [date]);
19098             }
19099         }
19100         */
19101         
19102     },
19103     
19104     findCell : function(dt) {
19105         dt = dt.clearTime().getTime();
19106         var ret = false;
19107         this.cells.each(function(c){
19108             //Roo.log("check " +c.dateValue + '?=' + dt);
19109             if(c.dateValue == dt){
19110                 ret = c;
19111                 return false;
19112             }
19113             return true;
19114         });
19115         
19116         return ret;
19117     },
19118     
19119     findCells : function(ev) {
19120         var s = ev.start.clone().clearTime().getTime();
19121        // Roo.log(s);
19122         var e= ev.end.clone().clearTime().getTime();
19123        // Roo.log(e);
19124         var ret = [];
19125         this.cells.each(function(c){
19126              ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
19127             
19128             if(c.dateValue > e){
19129                 return ;
19130             }
19131             if(c.dateValue < s){
19132                 return ;
19133             }
19134             ret.push(c);
19135         });
19136         
19137         return ret;    
19138     },
19139     
19140 //    findBestRow: function(cells)
19141 //    {
19142 //        var ret = 0;
19143 //        
19144 //        for (var i =0 ; i < cells.length;i++) {
19145 //            ret  = Math.max(cells[i].rows || 0,ret);
19146 //        }
19147 //        return ret;
19148 //        
19149 //    },
19150     
19151     
19152     addItem : function(ev)
19153     {
19154         // look for vertical location slot in
19155         var cells = this.findCells(ev);
19156         
19157 //        ev.row = this.findBestRow(cells);
19158         
19159         // work out the location.
19160         
19161         var crow = false;
19162         var rows = [];
19163         for(var i =0; i < cells.length; i++) {
19164             
19165             cells[i].row = cells[0].row;
19166             
19167             if(i == 0){
19168                 cells[i].row = cells[i].row + 1;
19169             }
19170             
19171             if (!crow) {
19172                 crow = {
19173                     start : cells[i],
19174                     end :  cells[i]
19175                 };
19176                 continue;
19177             }
19178             if (crow.start.getY() == cells[i].getY()) {
19179                 // on same row.
19180                 crow.end = cells[i];
19181                 continue;
19182             }
19183             // different row.
19184             rows.push(crow);
19185             crow = {
19186                 start: cells[i],
19187                 end : cells[i]
19188             };
19189             
19190         }
19191         
19192         rows.push(crow);
19193         ev.els = [];
19194         ev.rows = rows;
19195         ev.cells = cells;
19196         
19197         cells[0].events.push(ev);
19198         
19199         this.calevents.push(ev);
19200     },
19201     
19202     clearEvents: function() {
19203         
19204         if(!this.calevents){
19205             return;
19206         }
19207         
19208         Roo.each(this.cells.elements, function(c){
19209             c.row = 0;
19210             c.events = [];
19211             c.more = [];
19212         });
19213         
19214         Roo.each(this.calevents, function(e) {
19215             Roo.each(e.els, function(el) {
19216                 el.un('mouseenter' ,this.onEventEnter, this);
19217                 el.un('mouseleave' ,this.onEventLeave, this);
19218                 el.remove();
19219             },this);
19220         },this);
19221         
19222         Roo.each(Roo.select('.fc-more-event', true).elements, function(e){
19223             e.remove();
19224         });
19225         
19226     },
19227     
19228     renderEvents: function()
19229     {   
19230         var _this = this;
19231         
19232         this.cells.each(function(c) {
19233             
19234             if(c.row < 5){
19235                 return;
19236             }
19237             
19238             var ev = c.events;
19239             
19240             var r = 4;
19241             if(c.row != c.events.length){
19242                 r = 4 - (4 - (c.row - c.events.length));
19243             }
19244             
19245             c.events = ev.slice(0, r);
19246             c.more = ev.slice(r);
19247             
19248             if(c.more.length && c.more.length == 1){
19249                 c.events.push(c.more.pop());
19250             }
19251             
19252             c.row = (c.row - ev.length) + c.events.length + ((c.more.length) ? 1 : 0);
19253             
19254         });
19255             
19256         this.cells.each(function(c) {
19257             
19258             c.select('.fc-day-content div',true).first().setHeight(Math.max(34, c.row * 20));
19259             
19260             
19261             for (var e = 0; e < c.events.length; e++){
19262                 var ev = c.events[e];
19263                 var rows = ev.rows;
19264                 
19265                 for(var i = 0; i < rows.length; i++) {
19266                 
19267                     // how many rows should it span..
19268
19269                     var  cfg = {
19270                         cls : 'roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable',
19271                         style : 'position: absolute', // left: 387px; width: 121px; top: 359px;
19272
19273                         unselectable : "on",
19274                         cn : [
19275                             {
19276                                 cls: 'fc-event-inner',
19277                                 cn : [
19278     //                                {
19279     //                                  tag:'span',
19280     //                                  cls: 'fc-event-time',
19281     //                                  html : cells.length > 1 ? '' : ev.time
19282     //                                },
19283                                     {
19284                                       tag:'span',
19285                                       cls: 'fc-event-title',
19286                                       html : String.format('{0}', ev.title)
19287                                     }
19288
19289
19290                                 ]
19291                             },
19292                             {
19293                                 cls: 'ui-resizable-handle ui-resizable-e',
19294                                 html : '&nbsp;&nbsp;&nbsp'
19295                             }
19296
19297                         ]
19298                     };
19299
19300                     if (i == 0) {
19301                         cfg.cls += ' fc-event-start';
19302                     }
19303                     if ((i+1) == rows.length) {
19304                         cfg.cls += ' fc-event-end';
19305                     }
19306
19307                     var ctr = _this.el.select('.fc-event-container',true).first();
19308                     var cg = ctr.createChild(cfg);
19309
19310                     var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
19311                     var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
19312
19313                     var r = (c.more.length) ? 1 : 0;
19314                     cg.setXY([sbox.x +2, sbox.y + ((c.row - c.events.length - r + e) * 20)]);    
19315                     cg.setWidth(ebox.right - sbox.x -2);
19316
19317                     cg.on('mouseenter' ,_this.onEventEnter, _this, ev);
19318                     cg.on('mouseleave' ,_this.onEventLeave, _this, ev);
19319                     cg.on('click', _this.onEventClick, _this, ev);
19320
19321                     ev.els.push(cg);
19322                     
19323                 }
19324                 
19325             }
19326             
19327             
19328             if(c.more.length){
19329                 var  cfg = {
19330                     cls : 'fc-more-event roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable fc-event-start fc-event-end',
19331                     style : 'position: absolute',
19332                     unselectable : "on",
19333                     cn : [
19334                         {
19335                             cls: 'fc-event-inner',
19336                             cn : [
19337                                 {
19338                                   tag:'span',
19339                                   cls: 'fc-event-title',
19340                                   html : 'More'
19341                                 }
19342
19343
19344                             ]
19345                         },
19346                         {
19347                             cls: 'ui-resizable-handle ui-resizable-e',
19348                             html : '&nbsp;&nbsp;&nbsp'
19349                         }
19350
19351                     ]
19352                 };
19353
19354                 var ctr = _this.el.select('.fc-event-container',true).first();
19355                 var cg = ctr.createChild(cfg);
19356
19357                 var sbox = c.select('.fc-day-content',true).first().getBox();
19358                 var ebox = c.select('.fc-day-content',true).first().getBox();
19359                 //Roo.log(cg);
19360                 cg.setXY([sbox.x +2, sbox.y +((c.row - 1) * 20)]);    
19361                 cg.setWidth(ebox.right - sbox.x -2);
19362
19363                 cg.on('click', _this.onMoreEventClick, _this, c.more);
19364                 
19365             }
19366             
19367         });
19368         
19369         
19370         
19371     },
19372     
19373     onEventEnter: function (e, el,event,d) {
19374         this.fireEvent('evententer', this, el, event);
19375     },
19376     
19377     onEventLeave: function (e, el,event,d) {
19378         this.fireEvent('eventleave', this, el, event);
19379     },
19380     
19381     onEventClick: function (e, el,event,d) {
19382         this.fireEvent('eventclick', this, el, event);
19383     },
19384     
19385     onMonthChange: function () {
19386         this.store.load();
19387     },
19388     
19389     onMoreEventClick: function(e, el, more)
19390     {
19391         var _this = this;
19392         
19393         this.calpopover.placement = 'right';
19394         this.calpopover.setTitle('More');
19395         
19396         this.calpopover.setContent('');
19397         
19398         var ctr = this.calpopover.el.select('.popover-content', true).first();
19399         
19400         Roo.each(more, function(m){
19401             var cfg = {
19402                 cls : 'fc-event-hori fc-event-draggable',
19403                 html : m.title
19404             };
19405             var cg = ctr.createChild(cfg);
19406             
19407             cg.on('click', _this.onEventClick, _this, m);
19408         });
19409         
19410         this.calpopover.show(el);
19411         
19412         
19413     },
19414     
19415     onLoad: function () 
19416     {   
19417         this.calevents = [];
19418         var cal = this;
19419         
19420         if(this.store.getCount() > 0){
19421             this.store.data.each(function(d){
19422                cal.addItem({
19423                     id : d.data.id,
19424                     start: (typeof(d.data.start_dt) === 'string') ? new Date.parseDate(d.data.start_dt, 'Y-m-d H:i:s') : d.data.start_dt,
19425                     end : (typeof(d.data.end_dt) === 'string') ? new Date.parseDate(d.data.end_dt, 'Y-m-d H:i:s') : d.data.end_dt,
19426                     time : d.data.start_time,
19427                     title : d.data.title,
19428                     description : d.data.description,
19429                     venue : d.data.venue
19430                 });
19431             });
19432         }
19433         
19434         this.renderEvents();
19435         
19436         if(this.calevents.length && this.loadMask){
19437             this.maskEl.hide();
19438         }
19439     },
19440     
19441     onBeforeLoad: function()
19442     {
19443         this.clearEvents();
19444         if(this.loadMask){
19445             this.maskEl.show();
19446         }
19447     }
19448 });
19449
19450  
19451  /*
19452  * - LGPL
19453  *
19454  * element
19455  * 
19456  */
19457
19458 /**
19459  * @class Roo.bootstrap.Popover
19460  * @extends Roo.bootstrap.Component
19461  * Bootstrap Popover class
19462  * @cfg {String} html contents of the popover   (or false to use children..)
19463  * @cfg {String} title of popover (or false to hide)
19464  * @cfg {String} placement how it is placed
19465  * @cfg {String} trigger click || hover (or false to trigger manually)
19466  * @cfg {String} over what (parent or false to trigger manually.)
19467  * @cfg {Number} delay - delay before showing
19468  
19469  * @constructor
19470  * Create a new Popover
19471  * @param {Object} config The config object
19472  */
19473
19474 Roo.bootstrap.Popover = function(config){
19475     Roo.bootstrap.Popover.superclass.constructor.call(this, config);
19476     
19477     this.addEvents({
19478         // raw events
19479          /**
19480          * @event show
19481          * After the popover show
19482          * 
19483          * @param {Roo.bootstrap.Popover} this
19484          */
19485         "show" : true,
19486         /**
19487          * @event hide
19488          * After the popover hide
19489          * 
19490          * @param {Roo.bootstrap.Popover} this
19491          */
19492         "hide" : true
19493     });
19494 };
19495
19496 Roo.extend(Roo.bootstrap.Popover, Roo.bootstrap.Component,  {
19497     
19498     title: 'Fill in a title',
19499     html: false,
19500     
19501     placement : 'right',
19502     trigger : 'hover', // hover
19503     
19504     delay : 0,
19505     
19506     over: 'parent',
19507     
19508     can_build_overlaid : false,
19509     
19510     getChildContainer : function()
19511     {
19512         return this.el.select('.popover-content',true).first();
19513     },
19514     
19515     getAutoCreate : function(){
19516          
19517         var cfg = {
19518            cls : 'popover roo-dynamic',
19519            style: 'display:block',
19520            cn : [
19521                 {
19522                     cls : 'arrow'
19523                 },
19524                 {
19525                     cls : 'popover-inner',
19526                     cn : [
19527                         {
19528                             tag: 'h3',
19529                             cls: 'popover-title popover-header',
19530                             html : this.title
19531                         },
19532                         {
19533                             cls : 'popover-content popover-body',
19534                             html : this.html
19535                         }
19536                     ]
19537                     
19538                 }
19539            ]
19540         };
19541         
19542         return cfg;
19543     },
19544     setTitle: function(str)
19545     {
19546         this.title = str;
19547         this.el.select('.popover-title',true).first().dom.innerHTML = str;
19548     },
19549     setContent: function(str)
19550     {
19551         this.html = str;
19552         this.el.select('.popover-content',true).first().dom.innerHTML = str;
19553     },
19554     // as it get's added to the bottom of the page.
19555     onRender : function(ct, position)
19556     {
19557         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
19558         if(!this.el){
19559             var cfg = Roo.apply({},  this.getAutoCreate());
19560             cfg.id = Roo.id();
19561             
19562             if (this.cls) {
19563                 cfg.cls += ' ' + this.cls;
19564             }
19565             if (this.style) {
19566                 cfg.style = this.style;
19567             }
19568             //Roo.log("adding to ");
19569             this.el = Roo.get(document.body).createChild(cfg, position);
19570 //            Roo.log(this.el);
19571         }
19572         this.initEvents();
19573     },
19574     
19575     initEvents : function()
19576     {
19577         this.el.select('.popover-title',true).setVisibilityMode(Roo.Element.DISPLAY);
19578         this.el.enableDisplayMode('block');
19579         this.el.hide();
19580         if (this.over === false) {
19581             return; 
19582         }
19583         if (this.triggers === false) {
19584             return;
19585         }
19586         var on_el = (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
19587         var triggers = this.trigger ? this.trigger.split(' ') : [];
19588         Roo.each(triggers, function(trigger) {
19589         
19590             if (trigger == 'click') {
19591                 on_el.on('click', this.toggle, this);
19592             } else if (trigger != 'manual') {
19593                 var eventIn  = trigger == 'hover' ? 'mouseenter' : 'focusin';
19594                 var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout';
19595       
19596                 on_el.on(eventIn  ,this.enter, this);
19597                 on_el.on(eventOut, this.leave, this);
19598             }
19599         }, this);
19600         
19601     },
19602     
19603     
19604     // private
19605     timeout : null,
19606     hoverState : null,
19607     
19608     toggle : function () {
19609         this.hoverState == 'in' ? this.leave() : this.enter();
19610     },
19611     
19612     enter : function () {
19613         
19614         clearTimeout(this.timeout);
19615     
19616         this.hoverState = 'in';
19617     
19618         if (!this.delay || !this.delay.show) {
19619             this.show();
19620             return;
19621         }
19622         var _t = this;
19623         this.timeout = setTimeout(function () {
19624             if (_t.hoverState == 'in') {
19625                 _t.show();
19626             }
19627         }, this.delay.show)
19628     },
19629     
19630     leave : function() {
19631         clearTimeout(this.timeout);
19632     
19633         this.hoverState = 'out';
19634     
19635         if (!this.delay || !this.delay.hide) {
19636             this.hide();
19637             return;
19638         }
19639         var _t = this;
19640         this.timeout = setTimeout(function () {
19641             if (_t.hoverState == 'out') {
19642                 _t.hide();
19643             }
19644         }, this.delay.hide)
19645     },
19646     
19647     show : function (on_el)
19648     {
19649         if (!on_el) {
19650             on_el= (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
19651         }
19652         
19653         // set content.
19654         this.el.select('.popover-title',true).first().dom.innerHtml = this.title;
19655         if (this.html !== false) {
19656             this.el.select('.popover-content',true).first().dom.innerHtml = this.html;
19657         }
19658         this.el.removeClass([
19659             'fade','top','bottom', 'left', 'right','in',
19660             'bs-popover-top','bs-popover-bottom', 'bs-popover-left', 'bs-popover-right'
19661         ]);
19662         if (!this.title.length) {
19663             this.el.select('.popover-title',true).hide();
19664         }
19665         
19666         var placement = typeof this.placement == 'function' ?
19667             this.placement.call(this, this.el, on_el) :
19668             this.placement;
19669             
19670         var autoToken = /\s?auto?\s?/i;
19671         var autoPlace = autoToken.test(placement);
19672         if (autoPlace) {
19673             placement = placement.replace(autoToken, '') || 'top';
19674         }
19675         
19676         //this.el.detach()
19677         //this.el.setXY([0,0]);
19678         this.el.show();
19679         this.el.dom.style.display='block';
19680         this.el.addClass(placement);
19681         
19682         //this.el.appendTo(on_el);
19683         
19684         var p = this.getPosition();
19685         var box = this.el.getBox();
19686         
19687         if (autoPlace) {
19688             // fixme..
19689         }
19690         var align = Roo.bootstrap.Popover.alignment[placement];
19691         
19692 //        Roo.log(align);
19693         this.el.alignTo(on_el, align[0],align[1]);
19694         //var arrow = this.el.select('.arrow',true).first();
19695         //arrow.set(align[2], 
19696         
19697         this.el.addClass('in');
19698         
19699         
19700         if (this.el.hasClass('fade')) {
19701             // fade it?
19702         }
19703         
19704         this.hoverState = 'in';
19705         
19706         this.fireEvent('show', this);
19707         
19708     },
19709     hide : function()
19710     {
19711         this.el.setXY([0,0]);
19712         this.el.removeClass('in');
19713         this.el.hide();
19714         this.hoverState = null;
19715         
19716         this.fireEvent('hide', this);
19717     }
19718     
19719 });
19720
19721 Roo.bootstrap.Popover.alignment = {
19722     'left' : ['r-l', [-10,0], 'right bs-popover-right'],
19723     'right' : ['l-r', [10,0], 'left bs-popover-left'],
19724     'bottom' : ['t-b', [0,10], 'top bs-popover-top'],
19725     'top' : [ 'b-t', [0,-10], 'bottom bs-popover-bottom']
19726 };
19727
19728  /*
19729  * - LGPL
19730  *
19731  * Progress
19732  * 
19733  */
19734
19735 /**
19736  * @class Roo.bootstrap.Progress
19737  * @extends Roo.bootstrap.Component
19738  * Bootstrap Progress class
19739  * @cfg {Boolean} striped striped of the progress bar
19740  * @cfg {Boolean} active animated of the progress bar
19741  * 
19742  * 
19743  * @constructor
19744  * Create a new Progress
19745  * @param {Object} config The config object
19746  */
19747
19748 Roo.bootstrap.Progress = function(config){
19749     Roo.bootstrap.Progress.superclass.constructor.call(this, config);
19750 };
19751
19752 Roo.extend(Roo.bootstrap.Progress, Roo.bootstrap.Component,  {
19753     
19754     striped : false,
19755     active: false,
19756     
19757     getAutoCreate : function(){
19758         var cfg = {
19759             tag: 'div',
19760             cls: 'progress'
19761         };
19762         
19763         
19764         if(this.striped){
19765             cfg.cls += ' progress-striped';
19766         }
19767       
19768         if(this.active){
19769             cfg.cls += ' active';
19770         }
19771         
19772         
19773         return cfg;
19774     }
19775    
19776 });
19777
19778  
19779
19780  /*
19781  * - LGPL
19782  *
19783  * ProgressBar
19784  * 
19785  */
19786
19787 /**
19788  * @class Roo.bootstrap.ProgressBar
19789  * @extends Roo.bootstrap.Component
19790  * Bootstrap ProgressBar class
19791  * @cfg {Number} aria_valuenow aria-value now
19792  * @cfg {Number} aria_valuemin aria-value min
19793  * @cfg {Number} aria_valuemax aria-value max
19794  * @cfg {String} label label for the progress bar
19795  * @cfg {String} panel (success | info | warning | danger )
19796  * @cfg {String} role role of the progress bar
19797  * @cfg {String} sr_only text
19798  * 
19799  * 
19800  * @constructor
19801  * Create a new ProgressBar
19802  * @param {Object} config The config object
19803  */
19804
19805 Roo.bootstrap.ProgressBar = function(config){
19806     Roo.bootstrap.ProgressBar.superclass.constructor.call(this, config);
19807 };
19808
19809 Roo.extend(Roo.bootstrap.ProgressBar, Roo.bootstrap.Component,  {
19810     
19811     aria_valuenow : 0,
19812     aria_valuemin : 0,
19813     aria_valuemax : 100,
19814     label : false,
19815     panel : false,
19816     role : false,
19817     sr_only: false,
19818     
19819     getAutoCreate : function()
19820     {
19821         
19822         var cfg = {
19823             tag: 'div',
19824             cls: 'progress-bar',
19825             style: 'width:' + Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%'
19826         };
19827         
19828         if(this.sr_only){
19829             cfg.cn = {
19830                 tag: 'span',
19831                 cls: 'sr-only',
19832                 html: this.sr_only
19833             }
19834         }
19835         
19836         if(this.role){
19837             cfg.role = this.role;
19838         }
19839         
19840         if(this.aria_valuenow){
19841             cfg['aria-valuenow'] = this.aria_valuenow;
19842         }
19843         
19844         if(this.aria_valuemin){
19845             cfg['aria-valuemin'] = this.aria_valuemin;
19846         }
19847         
19848         if(this.aria_valuemax){
19849             cfg['aria-valuemax'] = this.aria_valuemax;
19850         }
19851         
19852         if(this.label && !this.sr_only){
19853             cfg.html = this.label;
19854         }
19855         
19856         if(this.panel){
19857             cfg.cls += ' progress-bar-' + this.panel;
19858         }
19859         
19860         return cfg;
19861     },
19862     
19863     update : function(aria_valuenow)
19864     {
19865         this.aria_valuenow = aria_valuenow;
19866         
19867         this.el.setStyle('width', Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%');
19868     }
19869    
19870 });
19871
19872  
19873
19874  /*
19875  * - LGPL
19876  *
19877  * column
19878  * 
19879  */
19880
19881 /**
19882  * @class Roo.bootstrap.TabGroup
19883  * @extends Roo.bootstrap.Column
19884  * Bootstrap Column class
19885  * @cfg {String} navId the navigation id (for use with navbars) - will be auto generated if it does not exist..
19886  * @cfg {Boolean} carousel true to make the group behave like a carousel
19887  * @cfg {Boolean} bullets show bullets for the panels
19888  * @cfg {Boolean} autoslide (true|false) auto slide .. default false
19889  * @cfg {Number} timer auto slide timer .. default 0 millisecond
19890  * @cfg {Boolean} showarrow (true|false) show arrow default true
19891  * 
19892  * @constructor
19893  * Create a new TabGroup
19894  * @param {Object} config The config object
19895  */
19896
19897 Roo.bootstrap.TabGroup = function(config){
19898     Roo.bootstrap.TabGroup.superclass.constructor.call(this, config);
19899     if (!this.navId) {
19900         this.navId = Roo.id();
19901     }
19902     this.tabs = [];
19903     Roo.bootstrap.TabGroup.register(this);
19904     
19905 };
19906
19907 Roo.extend(Roo.bootstrap.TabGroup, Roo.bootstrap.Column,  {
19908     
19909     carousel : false,
19910     transition : false,
19911     bullets : 0,
19912     timer : 0,
19913     autoslide : false,
19914     slideFn : false,
19915     slideOnTouch : false,
19916     showarrow : true,
19917     
19918     getAutoCreate : function()
19919     {
19920         var cfg = Roo.apply({}, Roo.bootstrap.TabGroup.superclass.getAutoCreate.call(this));
19921         
19922         cfg.cls += ' tab-content';
19923         
19924         if (this.carousel) {
19925             cfg.cls += ' carousel slide';
19926             
19927             cfg.cn = [{
19928                cls : 'carousel-inner',
19929                cn : []
19930             }];
19931         
19932             if(this.bullets  && !Roo.isTouch){
19933                 
19934                 var bullets = {
19935                     cls : 'carousel-bullets',
19936                     cn : []
19937                 };
19938                
19939                 if(this.bullets_cls){
19940                     bullets.cls = bullets.cls + ' ' + this.bullets_cls;
19941                 }
19942                 
19943                 bullets.cn.push({
19944                     cls : 'clear'
19945                 });
19946                 
19947                 cfg.cn[0].cn.push(bullets);
19948             }
19949             
19950             if(this.showarrow){
19951                 cfg.cn[0].cn.push({
19952                     tag : 'div',
19953                     class : 'carousel-arrow',
19954                     cn : [
19955                         {
19956                             tag : 'div',
19957                             class : 'carousel-prev',
19958                             cn : [
19959                                 {
19960                                     tag : 'i',
19961                                     class : 'fa fa-chevron-left'
19962                                 }
19963                             ]
19964                         },
19965                         {
19966                             tag : 'div',
19967                             class : 'carousel-next',
19968                             cn : [
19969                                 {
19970                                     tag : 'i',
19971                                     class : 'fa fa-chevron-right'
19972                                 }
19973                             ]
19974                         }
19975                     ]
19976                 });
19977             }
19978             
19979         }
19980         
19981         return cfg;
19982     },
19983     
19984     initEvents:  function()
19985     {
19986 //        if(Roo.isTouch && this.slideOnTouch && !this.showarrow){
19987 //            this.el.on("touchstart", this.onTouchStart, this);
19988 //        }
19989         
19990         if(this.autoslide){
19991             var _this = this;
19992             
19993             this.slideFn = window.setInterval(function() {
19994                 _this.showPanelNext();
19995             }, this.timer);
19996         }
19997         
19998         if(this.showarrow){
19999             this.el.select('.carousel-prev', true).first().on('click', this.showPanelPrev, this);
20000             this.el.select('.carousel-next', true).first().on('click', this.showPanelNext, this);
20001         }
20002         
20003         
20004     },
20005     
20006 //    onTouchStart : function(e, el, o)
20007 //    {
20008 //        if(!this.slideOnTouch || !Roo.isTouch || Roo.get(e.getTarget()).hasClass('roo-button-text')){
20009 //            return;
20010 //        }
20011 //        
20012 //        this.showPanelNext();
20013 //    },
20014     
20015     
20016     getChildContainer : function()
20017     {
20018         return this.carousel ? this.el.select('.carousel-inner', true).first() : this.el;
20019     },
20020     
20021     /**
20022     * register a Navigation item
20023     * @param {Roo.bootstrap.NavItem} the navitem to add
20024     */
20025     register : function(item)
20026     {
20027         this.tabs.push( item);
20028         item.navId = this.navId; // not really needed..
20029         this.addBullet();
20030     
20031     },
20032     
20033     getActivePanel : function()
20034     {
20035         var r = false;
20036         Roo.each(this.tabs, function(t) {
20037             if (t.active) {
20038                 r = t;
20039                 return false;
20040             }
20041             return null;
20042         });
20043         return r;
20044         
20045     },
20046     getPanelByName : function(n)
20047     {
20048         var r = false;
20049         Roo.each(this.tabs, function(t) {
20050             if (t.tabId == n) {
20051                 r = t;
20052                 return false;
20053             }
20054             return null;
20055         });
20056         return r;
20057     },
20058     indexOfPanel : function(p)
20059     {
20060         var r = false;
20061         Roo.each(this.tabs, function(t,i) {
20062             if (t.tabId == p.tabId) {
20063                 r = i;
20064                 return false;
20065             }
20066             return null;
20067         });
20068         return r;
20069     },
20070     /**
20071      * show a specific panel
20072      * @param {Roo.bootstrap.TabPanel|number|string} panel to change to (use the tabId to specify a specific one)
20073      * @return {boolean} false if panel was not shown (invalid entry or beforedeactivate fails.)
20074      */
20075     showPanel : function (pan)
20076     {
20077         if(this.transition || typeof(pan) == 'undefined'){
20078             Roo.log("waiting for the transitionend");
20079             return false;
20080         }
20081         
20082         if (typeof(pan) == 'number') {
20083             pan = this.tabs[pan];
20084         }
20085         
20086         if (typeof(pan) == 'string') {
20087             pan = this.getPanelByName(pan);
20088         }
20089         
20090         var cur = this.getActivePanel();
20091         
20092         if(!pan || !cur){
20093             Roo.log('pan or acitve pan is undefined');
20094             return false;
20095         }
20096         
20097         if (pan.tabId == this.getActivePanel().tabId) {
20098             return true;
20099         }
20100         
20101         if (false === cur.fireEvent('beforedeactivate')) {
20102             return false;
20103         }
20104         
20105         if(this.bullets > 0 && !Roo.isTouch){
20106             this.setActiveBullet(this.indexOfPanel(pan));
20107         }
20108         
20109         if (this.carousel && typeof(Roo.get(document.body).dom.style.transition) != 'undefined') {
20110             
20111             //class="carousel-item carousel-item-next carousel-item-left"
20112             
20113             this.transition = true;
20114             var dir = this.indexOfPanel(pan) > this.indexOfPanel(cur)  ? 'next' : 'prev';
20115             var lr = dir == 'next' ? 'left' : 'right';
20116             pan.el.addClass(dir); // or prev
20117             pan.el.addClass('carousel-item-' + dir); // or prev
20118             pan.el.dom.offsetWidth; // find the offset with - causing a reflow?
20119             cur.el.addClass(lr); // or right
20120             pan.el.addClass(lr);
20121             cur.el.addClass('carousel-item-' +lr); // or right
20122             pan.el.addClass('carousel-item-' +lr);
20123             
20124             
20125             var _this = this;
20126             cur.el.on('transitionend', function() {
20127                 Roo.log("trans end?");
20128                 
20129                 pan.el.removeClass([lr,dir, 'carousel-item-' + lr, 'carousel-item-' + dir]);
20130                 pan.setActive(true);
20131                 
20132                 cur.el.removeClass([lr, 'carousel-item-' + lr]);
20133                 cur.setActive(false);
20134                 
20135                 _this.transition = false;
20136                 
20137             }, this, { single:  true } );
20138             
20139             return true;
20140         }
20141         
20142         cur.setActive(false);
20143         pan.setActive(true);
20144         
20145         return true;
20146         
20147     },
20148     showPanelNext : function()
20149     {
20150         var i = this.indexOfPanel(this.getActivePanel());
20151         
20152         if (i >= this.tabs.length - 1 && !this.autoslide) {
20153             return;
20154         }
20155         
20156         if (i >= this.tabs.length - 1 && this.autoslide) {
20157             i = -1;
20158         }
20159         
20160         this.showPanel(this.tabs[i+1]);
20161     },
20162     
20163     showPanelPrev : function()
20164     {
20165         var i = this.indexOfPanel(this.getActivePanel());
20166         
20167         if (i  < 1 && !this.autoslide) {
20168             return;
20169         }
20170         
20171         if (i < 1 && this.autoslide) {
20172             i = this.tabs.length;
20173         }
20174         
20175         this.showPanel(this.tabs[i-1]);
20176     },
20177     
20178     
20179     addBullet: function()
20180     {
20181         if(!this.bullets || Roo.isTouch){
20182             return;
20183         }
20184         var ctr = this.el.select('.carousel-bullets',true).first();
20185         var i = this.el.select('.carousel-bullets .bullet',true).getCount() ;
20186         var bullet = ctr.createChild({
20187             cls : 'bullet bullet-' + i
20188         },ctr.dom.lastChild);
20189         
20190         
20191         var _this = this;
20192         
20193         bullet.on('click', (function(e, el, o, ii, t){
20194
20195             e.preventDefault();
20196
20197             this.showPanel(ii);
20198
20199             if(this.autoslide && this.slideFn){
20200                 clearInterval(this.slideFn);
20201                 this.slideFn = window.setInterval(function() {
20202                     _this.showPanelNext();
20203                 }, this.timer);
20204             }
20205
20206         }).createDelegate(this, [i, bullet], true));
20207                 
20208         
20209     },
20210      
20211     setActiveBullet : function(i)
20212     {
20213         if(Roo.isTouch){
20214             return;
20215         }
20216         
20217         Roo.each(this.el.select('.bullet', true).elements, function(el){
20218             el.removeClass('selected');
20219         });
20220
20221         var bullet = this.el.select('.bullet-' + i, true).first();
20222         
20223         if(!bullet){
20224             return;
20225         }
20226         
20227         bullet.addClass('selected');
20228     }
20229     
20230     
20231   
20232 });
20233
20234  
20235
20236  
20237  
20238 Roo.apply(Roo.bootstrap.TabGroup, {
20239     
20240     groups: {},
20241      /**
20242     * register a Navigation Group
20243     * @param {Roo.bootstrap.NavGroup} the navgroup to add
20244     */
20245     register : function(navgrp)
20246     {
20247         this.groups[navgrp.navId] = navgrp;
20248         
20249     },
20250     /**
20251     * fetch a Navigation Group based on the navigation ID
20252     * if one does not exist , it will get created.
20253     * @param {string} the navgroup to add
20254     * @returns {Roo.bootstrap.NavGroup} the navgroup 
20255     */
20256     get: function(navId) {
20257         if (typeof(this.groups[navId]) == 'undefined') {
20258             this.register(new Roo.bootstrap.TabGroup({ navId : navId }));
20259         }
20260         return this.groups[navId] ;
20261     }
20262     
20263     
20264     
20265 });
20266
20267  /*
20268  * - LGPL
20269  *
20270  * TabPanel
20271  * 
20272  */
20273
20274 /**
20275  * @class Roo.bootstrap.TabPanel
20276  * @extends Roo.bootstrap.Component
20277  * Bootstrap TabPanel class
20278  * @cfg {Boolean} active panel active
20279  * @cfg {String} html panel content
20280  * @cfg {String} tabId  unique tab ID (will be autogenerated if not set. - used to match TabItem to Panel)
20281  * @cfg {String} navId The Roo.bootstrap.NavGroup which triggers show hide ()
20282  * @cfg {String} href click to link..
20283  * @cfg {Boolean} touchSlide if swiping slides tab to next panel (default off)
20284  * 
20285  * 
20286  * @constructor
20287  * Create a new TabPanel
20288  * @param {Object} config The config object
20289  */
20290
20291 Roo.bootstrap.TabPanel = function(config){
20292     Roo.bootstrap.TabPanel.superclass.constructor.call(this, config);
20293     this.addEvents({
20294         /**
20295              * @event changed
20296              * Fires when the active status changes
20297              * @param {Roo.bootstrap.TabPanel} this
20298              * @param {Boolean} state the new state
20299             
20300          */
20301         'changed': true,
20302         /**
20303              * @event beforedeactivate
20304              * Fires before a tab is de-activated - can be used to do validation on a form.
20305              * @param {Roo.bootstrap.TabPanel} this
20306              * @return {Boolean} false if there is an error
20307             
20308          */
20309         'beforedeactivate': true
20310      });
20311     
20312     this.tabId = this.tabId || Roo.id();
20313   
20314 };
20315
20316 Roo.extend(Roo.bootstrap.TabPanel, Roo.bootstrap.Component,  {
20317     
20318     active: false,
20319     html: false,
20320     tabId: false,
20321     navId : false,
20322     href : '',
20323     touchSlide : false,
20324     getAutoCreate : function(){
20325         
20326         
20327         var cfg = {
20328             tag: 'div',
20329             // item is needed for carousel - not sure if it has any effect otherwise
20330             cls: 'carousel-item tab-pane item' + ((this.href.length) ? ' clickable ' : ''),
20331             html: this.html || ''
20332         };
20333         
20334         if(this.active){
20335             cfg.cls += ' active';
20336         }
20337         
20338         if(this.tabId){
20339             cfg.tabId = this.tabId;
20340         }
20341         
20342         
20343         
20344         return cfg;
20345     },
20346     
20347     initEvents:  function()
20348     {
20349         var p = this.parent();
20350         
20351         this.navId = this.navId || p.navId;
20352         
20353         if (typeof(this.navId) != 'undefined') {
20354             // not really needed.. but just in case.. parent should be a NavGroup.
20355             var tg = Roo.bootstrap.TabGroup.get(this.navId);
20356             
20357             tg.register(this);
20358             
20359             var i = tg.tabs.length - 1;
20360             
20361             if(this.active && tg.bullets > 0 && i < tg.bullets){
20362                 tg.setActiveBullet(i);
20363             }
20364         }
20365         
20366         this.el.on('click', this.onClick, this);
20367         
20368         if(Roo.isTouch && this.touchSlide){
20369             this.el.on("touchstart", this.onTouchStart, this);
20370             this.el.on("touchmove", this.onTouchMove, this);
20371             this.el.on("touchend", this.onTouchEnd, this);
20372         }
20373         
20374     },
20375     
20376     onRender : function(ct, position)
20377     {
20378         Roo.bootstrap.TabPanel.superclass.onRender.call(this, ct, position);
20379     },
20380     
20381     setActive : function(state)
20382     {
20383         Roo.log("panel - set active " + this.tabId + "=" + state);
20384         
20385         this.active = state;
20386         if (!state) {
20387             this.el.removeClass('active');
20388             
20389         } else  if (!this.el.hasClass('active')) {
20390             this.el.addClass('active');
20391         }
20392         
20393         this.fireEvent('changed', this, state);
20394     },
20395     
20396     onClick : function(e)
20397     {
20398         e.preventDefault();
20399         
20400         if(!this.href.length){
20401             return;
20402         }
20403         
20404         window.location.href = this.href;
20405     },
20406     
20407     startX : 0,
20408     startY : 0,
20409     endX : 0,
20410     endY : 0,
20411     swiping : false,
20412     
20413     onTouchStart : function(e)
20414     {
20415         this.swiping = false;
20416         
20417         this.startX = e.browserEvent.touches[0].clientX;
20418         this.startY = e.browserEvent.touches[0].clientY;
20419     },
20420     
20421     onTouchMove : function(e)
20422     {
20423         this.swiping = true;
20424         
20425         this.endX = e.browserEvent.touches[0].clientX;
20426         this.endY = e.browserEvent.touches[0].clientY;
20427     },
20428     
20429     onTouchEnd : function(e)
20430     {
20431         if(!this.swiping){
20432             this.onClick(e);
20433             return;
20434         }
20435         
20436         var tabGroup = this.parent();
20437         
20438         if(this.endX > this.startX){ // swiping right
20439             tabGroup.showPanelPrev();
20440             return;
20441         }
20442         
20443         if(this.startX > this.endX){ // swiping left
20444             tabGroup.showPanelNext();
20445             return;
20446         }
20447     }
20448     
20449     
20450 });
20451  
20452
20453  
20454
20455  /*
20456  * - LGPL
20457  *
20458  * DateField
20459  * 
20460  */
20461
20462 /**
20463  * @class Roo.bootstrap.DateField
20464  * @extends Roo.bootstrap.Input
20465  * Bootstrap DateField class
20466  * @cfg {Number} weekStart default 0
20467  * @cfg {String} viewMode default empty, (months|years)
20468  * @cfg {String} minViewMode default empty, (months|years)
20469  * @cfg {Number} startDate default -Infinity
20470  * @cfg {Number} endDate default Infinity
20471  * @cfg {Boolean} todayHighlight default false
20472  * @cfg {Boolean} todayBtn default false
20473  * @cfg {Boolean} calendarWeeks default false
20474  * @cfg {Object} daysOfWeekDisabled default empty
20475  * @cfg {Boolean} singleMode default false (true | false)
20476  * 
20477  * @cfg {Boolean} keyboardNavigation default true
20478  * @cfg {String} language default en
20479  * 
20480  * @constructor
20481  * Create a new DateField
20482  * @param {Object} config The config object
20483  */
20484
20485 Roo.bootstrap.DateField = function(config){
20486     Roo.bootstrap.DateField.superclass.constructor.call(this, config);
20487      this.addEvents({
20488             /**
20489              * @event show
20490              * Fires when this field show.
20491              * @param {Roo.bootstrap.DateField} this
20492              * @param {Mixed} date The date value
20493              */
20494             show : true,
20495             /**
20496              * @event show
20497              * Fires when this field hide.
20498              * @param {Roo.bootstrap.DateField} this
20499              * @param {Mixed} date The date value
20500              */
20501             hide : true,
20502             /**
20503              * @event select
20504              * Fires when select a date.
20505              * @param {Roo.bootstrap.DateField} this
20506              * @param {Mixed} date The date value
20507              */
20508             select : true,
20509             /**
20510              * @event beforeselect
20511              * Fires when before select a date.
20512              * @param {Roo.bootstrap.DateField} this
20513              * @param {Mixed} date The date value
20514              */
20515             beforeselect : true
20516         });
20517 };
20518
20519 Roo.extend(Roo.bootstrap.DateField, Roo.bootstrap.Input,  {
20520     
20521     /**
20522      * @cfg {String} format
20523      * The default date format string which can be overriden for localization support.  The format must be
20524      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
20525      */
20526     format : "m/d/y",
20527     /**
20528      * @cfg {String} altFormats
20529      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
20530      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
20531      */
20532     altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
20533     
20534     weekStart : 0,
20535     
20536     viewMode : '',
20537     
20538     minViewMode : '',
20539     
20540     todayHighlight : false,
20541     
20542     todayBtn: false,
20543     
20544     language: 'en',
20545     
20546     keyboardNavigation: true,
20547     
20548     calendarWeeks: false,
20549     
20550     startDate: -Infinity,
20551     
20552     endDate: Infinity,
20553     
20554     daysOfWeekDisabled: [],
20555     
20556     _events: [],
20557     
20558     singleMode : false,
20559     
20560     UTCDate: function()
20561     {
20562         return new Date(Date.UTC.apply(Date, arguments));
20563     },
20564     
20565     UTCToday: function()
20566     {
20567         var today = new Date();
20568         return this.UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
20569     },
20570     
20571     getDate: function() {
20572             var d = this.getUTCDate();
20573             return new Date(d.getTime() + (d.getTimezoneOffset()*60000));
20574     },
20575     
20576     getUTCDate: function() {
20577             return this.date;
20578     },
20579     
20580     setDate: function(d) {
20581             this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000)));
20582     },
20583     
20584     setUTCDate: function(d) {
20585             this.date = d;
20586             this.setValue(this.formatDate(this.date));
20587     },
20588         
20589     onRender: function(ct, position)
20590     {
20591         
20592         Roo.bootstrap.DateField.superclass.onRender.call(this, ct, position);
20593         
20594         this.language = this.language || 'en';
20595         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : this.language.split('-')[0];
20596         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : "en";
20597         
20598         this.isRTL = Roo.bootstrap.DateField.dates[this.language].rtl || false;
20599         this.format = this.format || 'm/d/y';
20600         this.isInline = false;
20601         this.isInput = true;
20602         this.component = this.el.select('.add-on', true).first() || false;
20603         this.component = (this.component && this.component.length === 0) ? false : this.component;
20604         this.hasInput = this.component && this.inputEl().length;
20605         
20606         if (typeof(this.minViewMode === 'string')) {
20607             switch (this.minViewMode) {
20608                 case 'months':
20609                     this.minViewMode = 1;
20610                     break;
20611                 case 'years':
20612                     this.minViewMode = 2;
20613                     break;
20614                 default:
20615                     this.minViewMode = 0;
20616                     break;
20617             }
20618         }
20619         
20620         if (typeof(this.viewMode === 'string')) {
20621             switch (this.viewMode) {
20622                 case 'months':
20623                     this.viewMode = 1;
20624                     break;
20625                 case 'years':
20626                     this.viewMode = 2;
20627                     break;
20628                 default:
20629                     this.viewMode = 0;
20630                     break;
20631             }
20632         }
20633                 
20634         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.DateField.template);
20635         
20636 //        this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.DateField.template);
20637         
20638         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
20639         
20640         this.picker().on('mousedown', this.onMousedown, this);
20641         this.picker().on('click', this.onClick, this);
20642         
20643         this.picker().addClass('datepicker-dropdown');
20644         
20645         this.startViewMode = this.viewMode;
20646         
20647         if(this.singleMode){
20648             Roo.each(this.picker().select('thead > tr > th', true).elements, function(v){
20649                 v.setVisibilityMode(Roo.Element.DISPLAY);
20650                 v.hide();
20651             });
20652             
20653             Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
20654                 v.setStyle('width', '189px');
20655             });
20656         }
20657         
20658         Roo.each(this.picker().select('tfoot th.today', true).elements, function(v){
20659             if(!this.calendarWeeks){
20660                 v.remove();
20661                 return;
20662             }
20663             
20664             v.dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
20665             v.attr('colspan', function(i, val){
20666                 return parseInt(val) + 1;
20667             });
20668         });
20669                         
20670         
20671         this.weekEnd = this.weekStart === 0 ? 6 : this.weekStart - 1;
20672         
20673         this.setStartDate(this.startDate);
20674         this.setEndDate(this.endDate);
20675         
20676         this.setDaysOfWeekDisabled(this.daysOfWeekDisabled);
20677         
20678         this.fillDow();
20679         this.fillMonths();
20680         this.update();
20681         this.showMode();
20682         
20683         if(this.isInline) {
20684             this.showPopup();
20685         }
20686     },
20687     
20688     picker : function()
20689     {
20690         return this.pickerEl;
20691 //        return this.el.select('.datepicker', true).first();
20692     },
20693     
20694     fillDow: function()
20695     {
20696         var dowCnt = this.weekStart;
20697         
20698         var dow = {
20699             tag: 'tr',
20700             cn: [
20701                 
20702             ]
20703         };
20704         
20705         if(this.calendarWeeks){
20706             dow.cn.push({
20707                 tag: 'th',
20708                 cls: 'cw',
20709                 html: '&nbsp;'
20710             })
20711         }
20712         
20713         while (dowCnt < this.weekStart + 7) {
20714             dow.cn.push({
20715                 tag: 'th',
20716                 cls: 'dow',
20717                 html: Roo.bootstrap.DateField.dates[this.language].daysMin[(dowCnt++)%7]
20718             });
20719         }
20720         
20721         this.picker().select('>.datepicker-days thead', true).first().createChild(dow);
20722     },
20723     
20724     fillMonths: function()
20725     {    
20726         var i = 0;
20727         var months = this.picker().select('>.datepicker-months td', true).first();
20728         
20729         months.dom.innerHTML = '';
20730         
20731         while (i < 12) {
20732             var month = {
20733                 tag: 'span',
20734                 cls: 'month',
20735                 html: Roo.bootstrap.DateField.dates[this.language].monthsShort[i++]
20736             };
20737             
20738             months.createChild(month);
20739         }
20740         
20741     },
20742     
20743     update: function()
20744     {
20745         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;
20746         
20747         if (this.date < this.startDate) {
20748             this.viewDate = new Date(this.startDate);
20749         } else if (this.date > this.endDate) {
20750             this.viewDate = new Date(this.endDate);
20751         } else {
20752             this.viewDate = new Date(this.date);
20753         }
20754         
20755         this.fill();
20756     },
20757     
20758     fill: function() 
20759     {
20760         var d = new Date(this.viewDate),
20761                 year = d.getUTCFullYear(),
20762                 month = d.getUTCMonth(),
20763                 startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity,
20764                 startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
20765                 endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
20766                 endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
20767                 currentDate = this.date && this.date.valueOf(),
20768                 today = this.UTCToday();
20769         
20770         this.picker().select('>.datepicker-days thead th.switch', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].months[month]+' '+year;
20771         
20772 //        this.picker().select('>tfoot th.today', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
20773         
20774 //        this.picker.select('>tfoot th.today').
20775 //                                              .text(dates[this.language].today)
20776 //                                              .toggle(this.todayBtn !== false);
20777     
20778         this.updateNavArrows();
20779         this.fillMonths();
20780                                                 
20781         var prevMonth = this.UTCDate(year, month-1, 28,0,0,0,0),
20782         
20783         day = prevMonth.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
20784          
20785         prevMonth.setUTCDate(day);
20786         
20787         prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7);
20788         
20789         var nextMonth = new Date(prevMonth);
20790         
20791         nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
20792         
20793         nextMonth = nextMonth.valueOf();
20794         
20795         var fillMonths = false;
20796         
20797         this.picker().select('>.datepicker-days tbody',true).first().dom.innerHTML = '';
20798         
20799         while(prevMonth.valueOf() <= nextMonth) {
20800             var clsName = '';
20801             
20802             if (prevMonth.getUTCDay() === this.weekStart) {
20803                 if(fillMonths){
20804                     this.picker().select('>.datepicker-days tbody',true).first().createChild(fillMonths);
20805                 }
20806                     
20807                 fillMonths = {
20808                     tag: 'tr',
20809                     cn: []
20810                 };
20811                 
20812                 if(this.calendarWeeks){
20813                     // ISO 8601: First week contains first thursday.
20814                     // ISO also states week starts on Monday, but we can be more abstract here.
20815                     var
20816                     // Start of current week: based on weekstart/current date
20817                     ws = new Date(+prevMonth + (this.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5),
20818                     // Thursday of this week
20819                     th = new Date(+ws + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
20820                     // First Thursday of year, year from thursday
20821                     yth = new Date(+(yth = this.UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5),
20822                     // Calendar week: ms between thursdays, div ms per day, div 7 days
20823                     calWeek =  (th - yth) / 864e5 / 7 + 1;
20824                     
20825                     fillMonths.cn.push({
20826                         tag: 'td',
20827                         cls: 'cw',
20828                         html: calWeek
20829                     });
20830                 }
20831             }
20832             
20833             if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
20834                 clsName += ' old';
20835             } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
20836                 clsName += ' new';
20837             }
20838             if (this.todayHighlight &&
20839                 prevMonth.getUTCFullYear() == today.getFullYear() &&
20840                 prevMonth.getUTCMonth() == today.getMonth() &&
20841                 prevMonth.getUTCDate() == today.getDate()) {
20842                 clsName += ' today';
20843             }
20844             
20845             if (currentDate && prevMonth.valueOf() === currentDate) {
20846                 clsName += ' active';
20847             }
20848             
20849             if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate ||
20850                     this.daysOfWeekDisabled.indexOf(prevMonth.getUTCDay()) !== -1) {
20851                     clsName += ' disabled';
20852             }
20853             
20854             fillMonths.cn.push({
20855                 tag: 'td',
20856                 cls: 'day ' + clsName,
20857                 html: prevMonth.getDate()
20858             });
20859             
20860             prevMonth.setDate(prevMonth.getDate()+1);
20861         }
20862           
20863         var currentYear = this.date && this.date.getUTCFullYear();
20864         var currentMonth = this.date && this.date.getUTCMonth();
20865         
20866         this.picker().select('>.datepicker-months th.switch',true).first().dom.innerHTML = year;
20867         
20868         Roo.each(this.picker().select('>.datepicker-months tbody span',true).elements, function(v,k){
20869             v.removeClass('active');
20870             
20871             if(currentYear === year && k === currentMonth){
20872                 v.addClass('active');
20873             }
20874             
20875             if (year < startYear || year > endYear || (year == startYear && k < startMonth) || (year == endYear && k > endMonth)) {
20876                 v.addClass('disabled');
20877             }
20878             
20879         });
20880         
20881         
20882         year = parseInt(year/10, 10) * 10;
20883         
20884         this.picker().select('>.datepicker-years th.switch', true).first().dom.innerHTML = year + '-' + (year + 9);
20885         
20886         this.picker().select('>.datepicker-years tbody td',true).first().dom.innerHTML = '';
20887         
20888         year -= 1;
20889         for (var i = -1; i < 11; i++) {
20890             this.picker().select('>.datepicker-years tbody td',true).first().createChild({
20891                 tag: 'span',
20892                 cls: 'year' + (i === -1 || i === 10 ? ' old' : '') + (currentYear === year ? ' active' : '') + (year < startYear || year > endYear ? ' disabled' : ''),
20893                 html: year
20894             });
20895             
20896             year += 1;
20897         }
20898     },
20899     
20900     showMode: function(dir) 
20901     {
20902         if (dir) {
20903             this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir));
20904         }
20905         
20906         Roo.each(this.picker().select('>div',true).elements, function(v){
20907             v.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
20908             v.hide();
20909         });
20910         this.picker().select('>.datepicker-'+Roo.bootstrap.DateField.modes[this.viewMode].clsName, true).first().show();
20911     },
20912     
20913     place: function()
20914     {
20915         if(this.isInline) {
20916             return;
20917         }
20918         
20919         this.picker().removeClass(['bottom', 'top']);
20920         
20921         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
20922             /*
20923              * place to the top of element!
20924              *
20925              */
20926             
20927             this.picker().addClass('top');
20928             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
20929             
20930             return;
20931         }
20932         
20933         this.picker().addClass('bottom');
20934         
20935         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
20936     },
20937     
20938     parseDate : function(value)
20939     {
20940         if(!value || value instanceof Date){
20941             return value;
20942         }
20943         var v = Date.parseDate(value, this.format);
20944         if (!v && (this.useIso || value.match(/^(\d{4})-0?(\d+)-0?(\d+)/))) {
20945             v = Date.parseDate(value, 'Y-m-d');
20946         }
20947         if(!v && this.altFormats){
20948             if(!this.altFormatsArray){
20949                 this.altFormatsArray = this.altFormats.split("|");
20950             }
20951             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
20952                 v = Date.parseDate(value, this.altFormatsArray[i]);
20953             }
20954         }
20955         return v;
20956     },
20957     
20958     formatDate : function(date, fmt)
20959     {   
20960         return (!date || !(date instanceof Date)) ?
20961         date : date.dateFormat(fmt || this.format);
20962     },
20963     
20964     onFocus : function()
20965     {
20966         Roo.bootstrap.DateField.superclass.onFocus.call(this);
20967         this.showPopup();
20968     },
20969     
20970     onBlur : function()
20971     {
20972         Roo.bootstrap.DateField.superclass.onBlur.call(this);
20973         
20974         var d = this.inputEl().getValue();
20975         
20976         this.setValue(d);
20977                 
20978         this.hidePopup();
20979     },
20980     
20981     showPopup : function()
20982     {
20983         this.picker().show();
20984         this.update();
20985         this.place();
20986         
20987         this.fireEvent('showpopup', this, this.date);
20988     },
20989     
20990     hidePopup : function()
20991     {
20992         if(this.isInline) {
20993             return;
20994         }
20995         this.picker().hide();
20996         this.viewMode = this.startViewMode;
20997         this.showMode();
20998         
20999         this.fireEvent('hidepopup', this, this.date);
21000         
21001     },
21002     
21003     onMousedown: function(e)
21004     {
21005         e.stopPropagation();
21006         e.preventDefault();
21007     },
21008     
21009     keyup: function(e)
21010     {
21011         Roo.bootstrap.DateField.superclass.keyup.call(this);
21012         this.update();
21013     },
21014
21015     setValue: function(v)
21016     {
21017         if(this.fireEvent('beforeselect', this, v) !== false){
21018             var d = new Date(this.parseDate(v) ).clearTime();
21019         
21020             if(isNaN(d.getTime())){
21021                 this.date = this.viewDate = '';
21022                 Roo.bootstrap.DateField.superclass.setValue.call(this, '');
21023                 return;
21024             }
21025
21026             v = this.formatDate(d);
21027
21028             Roo.bootstrap.DateField.superclass.setValue.call(this, v);
21029
21030             this.date = new Date(d.getTime() - d.getTimezoneOffset()*60000);
21031
21032             this.update();
21033
21034             this.fireEvent('select', this, this.date);
21035         }
21036     },
21037     
21038     getValue: function()
21039     {
21040         return this.formatDate(this.date);
21041     },
21042     
21043     fireKey: function(e)
21044     {
21045         if (!this.picker().isVisible()){
21046             if (e.keyCode == 27) { // allow escape to hide and re-show picker
21047                 this.showPopup();
21048             }
21049             return;
21050         }
21051         
21052         var dateChanged = false,
21053         dir, day, month,
21054         newDate, newViewDate;
21055         
21056         switch(e.keyCode){
21057             case 27: // escape
21058                 this.hidePopup();
21059                 e.preventDefault();
21060                 break;
21061             case 37: // left
21062             case 39: // right
21063                 if (!this.keyboardNavigation) {
21064                     break;
21065                 }
21066                 dir = e.keyCode == 37 ? -1 : 1;
21067                 
21068                 if (e.ctrlKey){
21069                     newDate = this.moveYear(this.date, dir);
21070                     newViewDate = this.moveYear(this.viewDate, dir);
21071                 } else if (e.shiftKey){
21072                     newDate = this.moveMonth(this.date, dir);
21073                     newViewDate = this.moveMonth(this.viewDate, dir);
21074                 } else {
21075                     newDate = new Date(this.date);
21076                     newDate.setUTCDate(this.date.getUTCDate() + dir);
21077                     newViewDate = new Date(this.viewDate);
21078                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
21079                 }
21080                 if (this.dateWithinRange(newDate)){
21081                     this.date = newDate;
21082                     this.viewDate = newViewDate;
21083                     this.setValue(this.formatDate(this.date));
21084 //                    this.update();
21085                     e.preventDefault();
21086                     dateChanged = true;
21087                 }
21088                 break;
21089             case 38: // up
21090             case 40: // down
21091                 if (!this.keyboardNavigation) {
21092                     break;
21093                 }
21094                 dir = e.keyCode == 38 ? -1 : 1;
21095                 if (e.ctrlKey){
21096                     newDate = this.moveYear(this.date, dir);
21097                     newViewDate = this.moveYear(this.viewDate, dir);
21098                 } else if (e.shiftKey){
21099                     newDate = this.moveMonth(this.date, dir);
21100                     newViewDate = this.moveMonth(this.viewDate, dir);
21101                 } else {
21102                     newDate = new Date(this.date);
21103                     newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
21104                     newViewDate = new Date(this.viewDate);
21105                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
21106                 }
21107                 if (this.dateWithinRange(newDate)){
21108                     this.date = newDate;
21109                     this.viewDate = newViewDate;
21110                     this.setValue(this.formatDate(this.date));
21111 //                    this.update();
21112                     e.preventDefault();
21113                     dateChanged = true;
21114                 }
21115                 break;
21116             case 13: // enter
21117                 this.setValue(this.formatDate(this.date));
21118                 this.hidePopup();
21119                 e.preventDefault();
21120                 break;
21121             case 9: // tab
21122                 this.setValue(this.formatDate(this.date));
21123                 this.hidePopup();
21124                 break;
21125             case 16: // shift
21126             case 17: // ctrl
21127             case 18: // alt
21128                 break;
21129             default :
21130                 this.hidePopup();
21131                 
21132         }
21133     },
21134     
21135     
21136     onClick: function(e) 
21137     {
21138         e.stopPropagation();
21139         e.preventDefault();
21140         
21141         var target = e.getTarget();
21142         
21143         if(target.nodeName.toLowerCase() === 'i'){
21144             target = Roo.get(target).dom.parentNode;
21145         }
21146         
21147         var nodeName = target.nodeName;
21148         var className = target.className;
21149         var html = target.innerHTML;
21150         //Roo.log(nodeName);
21151         
21152         switch(nodeName.toLowerCase()) {
21153             case 'th':
21154                 switch(className) {
21155                     case 'switch':
21156                         this.showMode(1);
21157                         break;
21158                     case 'prev':
21159                     case 'next':
21160                         var dir = Roo.bootstrap.DateField.modes[this.viewMode].navStep * (className == 'prev' ? -1 : 1);
21161                         switch(this.viewMode){
21162                                 case 0:
21163                                         this.viewDate = this.moveMonth(this.viewDate, dir);
21164                                         break;
21165                                 case 1:
21166                                 case 2:
21167                                         this.viewDate = this.moveYear(this.viewDate, dir);
21168                                         break;
21169                         }
21170                         this.fill();
21171                         break;
21172                     case 'today':
21173                         var date = new Date();
21174                         this.date = this.UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
21175 //                        this.fill()
21176                         this.setValue(this.formatDate(this.date));
21177                         
21178                         this.hidePopup();
21179                         break;
21180                 }
21181                 break;
21182             case 'span':
21183                 if (className.indexOf('disabled') < 0) {
21184                     this.viewDate.setUTCDate(1);
21185                     if (className.indexOf('month') > -1) {
21186                         this.viewDate.setUTCMonth(Roo.bootstrap.DateField.dates[this.language].monthsShort.indexOf(html));
21187                     } else {
21188                         var year = parseInt(html, 10) || 0;
21189                         this.viewDate.setUTCFullYear(year);
21190                         
21191                     }
21192                     
21193                     if(this.singleMode){
21194                         this.setValue(this.formatDate(this.viewDate));
21195                         this.hidePopup();
21196                         return;
21197                     }
21198                     
21199                     this.showMode(-1);
21200                     this.fill();
21201                 }
21202                 break;
21203                 
21204             case 'td':
21205                 //Roo.log(className);
21206                 if (className.indexOf('day') > -1 && className.indexOf('disabled') < 0 ){
21207                     var day = parseInt(html, 10) || 1;
21208                     var year = this.viewDate.getUTCFullYear(),
21209                         month = this.viewDate.getUTCMonth();
21210
21211                     if (className.indexOf('old') > -1) {
21212                         if(month === 0 ){
21213                             month = 11;
21214                             year -= 1;
21215                         }else{
21216                             month -= 1;
21217                         }
21218                     } else if (className.indexOf('new') > -1) {
21219                         if (month == 11) {
21220                             month = 0;
21221                             year += 1;
21222                         } else {
21223                             month += 1;
21224                         }
21225                     }
21226                     //Roo.log([year,month,day]);
21227                     this.date = this.UTCDate(year, month, day,0,0,0,0);
21228                     this.viewDate = this.UTCDate(year, month, Math.min(28, day),0,0,0,0);
21229 //                    this.fill();
21230                     //Roo.log(this.formatDate(this.date));
21231                     this.setValue(this.formatDate(this.date));
21232                     this.hidePopup();
21233                 }
21234                 break;
21235         }
21236     },
21237     
21238     setStartDate: function(startDate)
21239     {
21240         this.startDate = startDate || -Infinity;
21241         if (this.startDate !== -Infinity) {
21242             this.startDate = this.parseDate(this.startDate);
21243         }
21244         this.update();
21245         this.updateNavArrows();
21246     },
21247
21248     setEndDate: function(endDate)
21249     {
21250         this.endDate = endDate || Infinity;
21251         if (this.endDate !== Infinity) {
21252             this.endDate = this.parseDate(this.endDate);
21253         }
21254         this.update();
21255         this.updateNavArrows();
21256     },
21257     
21258     setDaysOfWeekDisabled: function(daysOfWeekDisabled)
21259     {
21260         this.daysOfWeekDisabled = daysOfWeekDisabled || [];
21261         if (typeof(this.daysOfWeekDisabled) !== 'object') {
21262             this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
21263         }
21264         this.daysOfWeekDisabled = this.daysOfWeekDisabled.map(function (d) {
21265             return parseInt(d, 10);
21266         });
21267         this.update();
21268         this.updateNavArrows();
21269     },
21270     
21271     updateNavArrows: function() 
21272     {
21273         if(this.singleMode){
21274             return;
21275         }
21276         
21277         var d = new Date(this.viewDate),
21278         year = d.getUTCFullYear(),
21279         month = d.getUTCMonth();
21280         
21281         Roo.each(this.picker().select('.prev', true).elements, function(v){
21282             v.show();
21283             switch (this.viewMode) {
21284                 case 0:
21285
21286                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
21287                         v.hide();
21288                     }
21289                     break;
21290                 case 1:
21291                 case 2:
21292                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
21293                         v.hide();
21294                     }
21295                     break;
21296             }
21297         });
21298         
21299         Roo.each(this.picker().select('.next', true).elements, function(v){
21300             v.show();
21301             switch (this.viewMode) {
21302                 case 0:
21303
21304                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
21305                         v.hide();
21306                     }
21307                     break;
21308                 case 1:
21309                 case 2:
21310                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
21311                         v.hide();
21312                     }
21313                     break;
21314             }
21315         })
21316     },
21317     
21318     moveMonth: function(date, dir)
21319     {
21320         if (!dir) {
21321             return date;
21322         }
21323         var new_date = new Date(date.valueOf()),
21324         day = new_date.getUTCDate(),
21325         month = new_date.getUTCMonth(),
21326         mag = Math.abs(dir),
21327         new_month, test;
21328         dir = dir > 0 ? 1 : -1;
21329         if (mag == 1){
21330             test = dir == -1
21331             // If going back one month, make sure month is not current month
21332             // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
21333             ? function(){
21334                 return new_date.getUTCMonth() == month;
21335             }
21336             // If going forward one month, make sure month is as expected
21337             // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
21338             : function(){
21339                 return new_date.getUTCMonth() != new_month;
21340             };
21341             new_month = month + dir;
21342             new_date.setUTCMonth(new_month);
21343             // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
21344             if (new_month < 0 || new_month > 11) {
21345                 new_month = (new_month + 12) % 12;
21346             }
21347         } else {
21348             // For magnitudes >1, move one month at a time...
21349             for (var i=0; i<mag; i++) {
21350                 // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
21351                 new_date = this.moveMonth(new_date, dir);
21352             }
21353             // ...then reset the day, keeping it in the new month
21354             new_month = new_date.getUTCMonth();
21355             new_date.setUTCDate(day);
21356             test = function(){
21357                 return new_month != new_date.getUTCMonth();
21358             };
21359         }
21360         // Common date-resetting loop -- if date is beyond end of month, make it
21361         // end of month
21362         while (test()){
21363             new_date.setUTCDate(--day);
21364             new_date.setUTCMonth(new_month);
21365         }
21366         return new_date;
21367     },
21368
21369     moveYear: function(date, dir)
21370     {
21371         return this.moveMonth(date, dir*12);
21372     },
21373
21374     dateWithinRange: function(date)
21375     {
21376         return date >= this.startDate && date <= this.endDate;
21377     },
21378
21379     
21380     remove: function() 
21381     {
21382         this.picker().remove();
21383     },
21384     
21385     validateValue : function(value)
21386     {
21387         if(this.getVisibilityEl().hasClass('hidden')){
21388             return true;
21389         }
21390         
21391         if(value.length < 1)  {
21392             if(this.allowBlank){
21393                 return true;
21394             }
21395             return false;
21396         }
21397         
21398         if(value.length < this.minLength){
21399             return false;
21400         }
21401         if(value.length > this.maxLength){
21402             return false;
21403         }
21404         if(this.vtype){
21405             var vt = Roo.form.VTypes;
21406             if(!vt[this.vtype](value, this)){
21407                 return false;
21408             }
21409         }
21410         if(typeof this.validator == "function"){
21411             var msg = this.validator(value);
21412             if(msg !== true){
21413                 return false;
21414             }
21415         }
21416         
21417         if(this.regex && !this.regex.test(value)){
21418             return false;
21419         }
21420         
21421         if(typeof(this.parseDate(value)) == 'undefined'){
21422             return false;
21423         }
21424         
21425         if (this.endDate !== Infinity && this.parseDate(value).getTime() > this.endDate.getTime()) {
21426             return false;
21427         }      
21428         
21429         if (this.startDate !== -Infinity && this.parseDate(value).getTime() < this.startDate.getTime()) {
21430             return false;
21431         } 
21432         
21433         
21434         return true;
21435     },
21436     
21437     reset : function()
21438     {
21439         this.date = this.viewDate = '';
21440         
21441         Roo.bootstrap.DateField.superclass.setValue.call(this, '');
21442     }
21443    
21444 });
21445
21446 Roo.apply(Roo.bootstrap.DateField,  {
21447     
21448     head : {
21449         tag: 'thead',
21450         cn: [
21451         {
21452             tag: 'tr',
21453             cn: [
21454             {
21455                 tag: 'th',
21456                 cls: 'prev',
21457                 html: '<i class="fa fa-arrow-left"/>'
21458             },
21459             {
21460                 tag: 'th',
21461                 cls: 'switch',
21462                 colspan: '5'
21463             },
21464             {
21465                 tag: 'th',
21466                 cls: 'next',
21467                 html: '<i class="fa fa-arrow-right"/>'
21468             }
21469
21470             ]
21471         }
21472         ]
21473     },
21474     
21475     content : {
21476         tag: 'tbody',
21477         cn: [
21478         {
21479             tag: 'tr',
21480             cn: [
21481             {
21482                 tag: 'td',
21483                 colspan: '7'
21484             }
21485             ]
21486         }
21487         ]
21488     },
21489     
21490     footer : {
21491         tag: 'tfoot',
21492         cn: [
21493         {
21494             tag: 'tr',
21495             cn: [
21496             {
21497                 tag: 'th',
21498                 colspan: '7',
21499                 cls: 'today'
21500             }
21501                     
21502             ]
21503         }
21504         ]
21505     },
21506     
21507     dates:{
21508         en: {
21509             days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
21510             daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
21511             daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
21512             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
21513             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
21514             today: "Today"
21515         }
21516     },
21517     
21518     modes: [
21519     {
21520         clsName: 'days',
21521         navFnc: 'Month',
21522         navStep: 1
21523     },
21524     {
21525         clsName: 'months',
21526         navFnc: 'FullYear',
21527         navStep: 1
21528     },
21529     {
21530         clsName: 'years',
21531         navFnc: 'FullYear',
21532         navStep: 10
21533     }]
21534 });
21535
21536 Roo.apply(Roo.bootstrap.DateField,  {
21537   
21538     template : {
21539         tag: 'div',
21540         cls: 'datepicker dropdown-menu roo-dynamic',
21541         cn: [
21542         {
21543             tag: 'div',
21544             cls: 'datepicker-days',
21545             cn: [
21546             {
21547                 tag: 'table',
21548                 cls: 'table-condensed',
21549                 cn:[
21550                 Roo.bootstrap.DateField.head,
21551                 {
21552                     tag: 'tbody'
21553                 },
21554                 Roo.bootstrap.DateField.footer
21555                 ]
21556             }
21557             ]
21558         },
21559         {
21560             tag: 'div',
21561             cls: 'datepicker-months',
21562             cn: [
21563             {
21564                 tag: 'table',
21565                 cls: 'table-condensed',
21566                 cn:[
21567                 Roo.bootstrap.DateField.head,
21568                 Roo.bootstrap.DateField.content,
21569                 Roo.bootstrap.DateField.footer
21570                 ]
21571             }
21572             ]
21573         },
21574         {
21575             tag: 'div',
21576             cls: 'datepicker-years',
21577             cn: [
21578             {
21579                 tag: 'table',
21580                 cls: 'table-condensed',
21581                 cn:[
21582                 Roo.bootstrap.DateField.head,
21583                 Roo.bootstrap.DateField.content,
21584                 Roo.bootstrap.DateField.footer
21585                 ]
21586             }
21587             ]
21588         }
21589         ]
21590     }
21591 });
21592
21593  
21594
21595  /*
21596  * - LGPL
21597  *
21598  * TimeField
21599  * 
21600  */
21601
21602 /**
21603  * @class Roo.bootstrap.TimeField
21604  * @extends Roo.bootstrap.Input
21605  * Bootstrap DateField class
21606  * 
21607  * 
21608  * @constructor
21609  * Create a new TimeField
21610  * @param {Object} config The config object
21611  */
21612
21613 Roo.bootstrap.TimeField = function(config){
21614     Roo.bootstrap.TimeField.superclass.constructor.call(this, config);
21615     this.addEvents({
21616             /**
21617              * @event show
21618              * Fires when this field show.
21619              * @param {Roo.bootstrap.DateField} thisthis
21620              * @param {Mixed} date The date value
21621              */
21622             show : true,
21623             /**
21624              * @event show
21625              * Fires when this field hide.
21626              * @param {Roo.bootstrap.DateField} this
21627              * @param {Mixed} date The date value
21628              */
21629             hide : true,
21630             /**
21631              * @event select
21632              * Fires when select a date.
21633              * @param {Roo.bootstrap.DateField} this
21634              * @param {Mixed} date The date value
21635              */
21636             select : true
21637         });
21638 };
21639
21640 Roo.extend(Roo.bootstrap.TimeField, Roo.bootstrap.Input,  {
21641     
21642     /**
21643      * @cfg {String} format
21644      * The default time format string which can be overriden for localization support.  The format must be
21645      * valid according to {@link Date#parseDate} (defaults to 'H:i').
21646      */
21647     format : "H:i",
21648        
21649     onRender: function(ct, position)
21650     {
21651         
21652         Roo.bootstrap.TimeField.superclass.onRender.call(this, ct, position);
21653                 
21654         this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.TimeField.template);
21655         
21656         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
21657         
21658         this.pop = this.picker().select('>.datepicker-time',true).first();
21659         this.pop.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
21660         
21661         this.picker().on('mousedown', this.onMousedown, this);
21662         this.picker().on('click', this.onClick, this);
21663         
21664         this.picker().addClass('datepicker-dropdown');
21665     
21666         this.fillTime();
21667         this.update();
21668             
21669         this.pop.select('span.hours-up', true).first().on('click', this.onIncrementHours, this);
21670         this.pop.select('span.hours-down', true).first().on('click', this.onDecrementHours, this);
21671         this.pop.select('span.minutes-up', true).first().on('click', this.onIncrementMinutes, this);
21672         this.pop.select('span.minutes-down', true).first().on('click', this.onDecrementMinutes, this);
21673         this.pop.select('button.period', true).first().on('click', this.onTogglePeriod, this);
21674         this.pop.select('button.ok', true).first().on('click', this.setTime, this);
21675
21676     },
21677     
21678     fireKey: function(e){
21679         if (!this.picker().isVisible()){
21680             if (e.keyCode == 27) { // allow escape to hide and re-show picker
21681                 this.show();
21682             }
21683             return;
21684         }
21685
21686         e.preventDefault();
21687         
21688         switch(e.keyCode){
21689             case 27: // escape
21690                 this.hide();
21691                 break;
21692             case 37: // left
21693             case 39: // right
21694                 this.onTogglePeriod();
21695                 break;
21696             case 38: // up
21697                 this.onIncrementMinutes();
21698                 break;
21699             case 40: // down
21700                 this.onDecrementMinutes();
21701                 break;
21702             case 13: // enter
21703             case 9: // tab
21704                 this.setTime();
21705                 break;
21706         }
21707     },
21708     
21709     onClick: function(e) {
21710         e.stopPropagation();
21711         e.preventDefault();
21712     },
21713     
21714     picker : function()
21715     {
21716         return this.el.select('.datepicker', true).first();
21717     },
21718     
21719     fillTime: function()
21720     {    
21721         var time = this.pop.select('tbody', true).first();
21722         
21723         time.dom.innerHTML = '';
21724         
21725         time.createChild({
21726             tag: 'tr',
21727             cn: [
21728                 {
21729                     tag: 'td',
21730                     cn: [
21731                         {
21732                             tag: 'a',
21733                             href: '#',
21734                             cls: 'btn',
21735                             cn: [
21736                                 {
21737                                     tag: 'span',
21738                                     cls: 'hours-up glyphicon glyphicon-chevron-up'
21739                                 }
21740                             ]
21741                         } 
21742                     ]
21743                 },
21744                 {
21745                     tag: 'td',
21746                     cls: 'separator'
21747                 },
21748                 {
21749                     tag: 'td',
21750                     cn: [
21751                         {
21752                             tag: 'a',
21753                             href: '#',
21754                             cls: 'btn',
21755                             cn: [
21756                                 {
21757                                     tag: 'span',
21758                                     cls: 'minutes-up glyphicon glyphicon-chevron-up'
21759                                 }
21760                             ]
21761                         }
21762                     ]
21763                 },
21764                 {
21765                     tag: 'td',
21766                     cls: 'separator'
21767                 }
21768             ]
21769         });
21770         
21771         time.createChild({
21772             tag: 'tr',
21773             cn: [
21774                 {
21775                     tag: 'td',
21776                     cn: [
21777                         {
21778                             tag: 'span',
21779                             cls: 'timepicker-hour',
21780                             html: '00'
21781                         }  
21782                     ]
21783                 },
21784                 {
21785                     tag: 'td',
21786                     cls: 'separator',
21787                     html: ':'
21788                 },
21789                 {
21790                     tag: 'td',
21791                     cn: [
21792                         {
21793                             tag: 'span',
21794                             cls: 'timepicker-minute',
21795                             html: '00'
21796                         }  
21797                     ]
21798                 },
21799                 {
21800                     tag: 'td',
21801                     cls: 'separator'
21802                 },
21803                 {
21804                     tag: 'td',
21805                     cn: [
21806                         {
21807                             tag: 'button',
21808                             type: 'button',
21809                             cls: 'btn btn-primary period',
21810                             html: 'AM'
21811                             
21812                         }
21813                     ]
21814                 }
21815             ]
21816         });
21817         
21818         time.createChild({
21819             tag: 'tr',
21820             cn: [
21821                 {
21822                     tag: 'td',
21823                     cn: [
21824                         {
21825                             tag: 'a',
21826                             href: '#',
21827                             cls: 'btn',
21828                             cn: [
21829                                 {
21830                                     tag: 'span',
21831                                     cls: 'hours-down glyphicon glyphicon-chevron-down'
21832                                 }
21833                             ]
21834                         }
21835                     ]
21836                 },
21837                 {
21838                     tag: 'td',
21839                     cls: 'separator'
21840                 },
21841                 {
21842                     tag: 'td',
21843                     cn: [
21844                         {
21845                             tag: 'a',
21846                             href: '#',
21847                             cls: 'btn',
21848                             cn: [
21849                                 {
21850                                     tag: 'span',
21851                                     cls: 'minutes-down glyphicon glyphicon-chevron-down'
21852                                 }
21853                             ]
21854                         }
21855                     ]
21856                 },
21857                 {
21858                     tag: 'td',
21859                     cls: 'separator'
21860                 }
21861             ]
21862         });
21863         
21864     },
21865     
21866     update: function()
21867     {
21868         
21869         this.time = (typeof(this.time) === 'undefined') ? new Date() : this.time;
21870         
21871         this.fill();
21872     },
21873     
21874     fill: function() 
21875     {
21876         var hours = this.time.getHours();
21877         var minutes = this.time.getMinutes();
21878         var period = 'AM';
21879         
21880         if(hours > 11){
21881             period = 'PM';
21882         }
21883         
21884         if(hours == 0){
21885             hours = 12;
21886         }
21887         
21888         
21889         if(hours > 12){
21890             hours = hours - 12;
21891         }
21892         
21893         if(hours < 10){
21894             hours = '0' + hours;
21895         }
21896         
21897         if(minutes < 10){
21898             minutes = '0' + minutes;
21899         }
21900         
21901         this.pop.select('.timepicker-hour', true).first().dom.innerHTML = hours;
21902         this.pop.select('.timepicker-minute', true).first().dom.innerHTML = minutes;
21903         this.pop.select('button', true).first().dom.innerHTML = period;
21904         
21905     },
21906     
21907     place: function()
21908     {   
21909         this.picker().removeClass(['bottom-left', 'bottom-right', 'top-left', 'top-right']);
21910         
21911         var cls = ['bottom'];
21912         
21913         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){ // top
21914             cls.pop();
21915             cls.push('top');
21916         }
21917         
21918         cls.push('right');
21919         
21920         if((Roo.lib.Dom.getViewWidth() + Roo.get(document.body).getScroll().left) - (this.inputEl().getLeft() + this.picker().getWidth()) < 0){ // left
21921             cls.pop();
21922             cls.push('left');
21923         }
21924         
21925         this.picker().addClass(cls.join('-'));
21926         
21927         var _this = this;
21928         
21929         Roo.each(cls, function(c){
21930             if(c == 'bottom'){
21931                 _this.picker().setTop(_this.inputEl().getHeight());
21932                 return;
21933             }
21934             if(c == 'top'){
21935                 _this.picker().setTop(0 - _this.picker().getHeight());
21936                 return;
21937             }
21938             
21939             if(c == 'left'){
21940                 _this.picker().setLeft(_this.inputEl().getLeft() + _this.inputEl().getWidth() - _this.el.getLeft() - _this.picker().getWidth());
21941                 return;
21942             }
21943             if(c == 'right'){
21944                 _this.picker().setLeft(_this.inputEl().getLeft() - _this.el.getLeft());
21945                 return;
21946             }
21947         });
21948         
21949     },
21950   
21951     onFocus : function()
21952     {
21953         Roo.bootstrap.TimeField.superclass.onFocus.call(this);
21954         this.show();
21955     },
21956     
21957     onBlur : function()
21958     {
21959         Roo.bootstrap.TimeField.superclass.onBlur.call(this);
21960         this.hide();
21961     },
21962     
21963     show : function()
21964     {
21965         this.picker().show();
21966         this.pop.show();
21967         this.update();
21968         this.place();
21969         
21970         this.fireEvent('show', this, this.date);
21971     },
21972     
21973     hide : function()
21974     {
21975         this.picker().hide();
21976         this.pop.hide();
21977         
21978         this.fireEvent('hide', this, this.date);
21979     },
21980     
21981     setTime : function()
21982     {
21983         this.hide();
21984         this.setValue(this.time.format(this.format));
21985         
21986         this.fireEvent('select', this, this.date);
21987         
21988         
21989     },
21990     
21991     onMousedown: function(e){
21992         e.stopPropagation();
21993         e.preventDefault();
21994     },
21995     
21996     onIncrementHours: function()
21997     {
21998         Roo.log('onIncrementHours');
21999         this.time = this.time.add(Date.HOUR, 1);
22000         this.update();
22001         
22002     },
22003     
22004     onDecrementHours: function()
22005     {
22006         Roo.log('onDecrementHours');
22007         this.time = this.time.add(Date.HOUR, -1);
22008         this.update();
22009     },
22010     
22011     onIncrementMinutes: function()
22012     {
22013         Roo.log('onIncrementMinutes');
22014         this.time = this.time.add(Date.MINUTE, 1);
22015         this.update();
22016     },
22017     
22018     onDecrementMinutes: function()
22019     {
22020         Roo.log('onDecrementMinutes');
22021         this.time = this.time.add(Date.MINUTE, -1);
22022         this.update();
22023     },
22024     
22025     onTogglePeriod: function()
22026     {
22027         Roo.log('onTogglePeriod');
22028         this.time = this.time.add(Date.HOUR, 12);
22029         this.update();
22030     }
22031     
22032    
22033 });
22034
22035 Roo.apply(Roo.bootstrap.TimeField,  {
22036     
22037     content : {
22038         tag: 'tbody',
22039         cn: [
22040             {
22041                 tag: 'tr',
22042                 cn: [
22043                 {
22044                     tag: 'td',
22045                     colspan: '7'
22046                 }
22047                 ]
22048             }
22049         ]
22050     },
22051     
22052     footer : {
22053         tag: 'tfoot',
22054         cn: [
22055             {
22056                 tag: 'tr',
22057                 cn: [
22058                 {
22059                     tag: 'th',
22060                     colspan: '7',
22061                     cls: '',
22062                     cn: [
22063                         {
22064                             tag: 'button',
22065                             cls: 'btn btn-info ok',
22066                             html: 'OK'
22067                         }
22068                     ]
22069                 }
22070
22071                 ]
22072             }
22073         ]
22074     }
22075 });
22076
22077 Roo.apply(Roo.bootstrap.TimeField,  {
22078   
22079     template : {
22080         tag: 'div',
22081         cls: 'datepicker dropdown-menu',
22082         cn: [
22083             {
22084                 tag: 'div',
22085                 cls: 'datepicker-time',
22086                 cn: [
22087                 {
22088                     tag: 'table',
22089                     cls: 'table-condensed',
22090                     cn:[
22091                     Roo.bootstrap.TimeField.content,
22092                     Roo.bootstrap.TimeField.footer
22093                     ]
22094                 }
22095                 ]
22096             }
22097         ]
22098     }
22099 });
22100
22101  
22102
22103  /*
22104  * - LGPL
22105  *
22106  * MonthField
22107  * 
22108  */
22109
22110 /**
22111  * @class Roo.bootstrap.MonthField
22112  * @extends Roo.bootstrap.Input
22113  * Bootstrap MonthField class
22114  * 
22115  * @cfg {String} language default en
22116  * 
22117  * @constructor
22118  * Create a new MonthField
22119  * @param {Object} config The config object
22120  */
22121
22122 Roo.bootstrap.MonthField = function(config){
22123     Roo.bootstrap.MonthField.superclass.constructor.call(this, config);
22124     
22125     this.addEvents({
22126         /**
22127          * @event show
22128          * Fires when this field show.
22129          * @param {Roo.bootstrap.MonthField} this
22130          * @param {Mixed} date The date value
22131          */
22132         show : true,
22133         /**
22134          * @event show
22135          * Fires when this field hide.
22136          * @param {Roo.bootstrap.MonthField} this
22137          * @param {Mixed} date The date value
22138          */
22139         hide : true,
22140         /**
22141          * @event select
22142          * Fires when select a date.
22143          * @param {Roo.bootstrap.MonthField} this
22144          * @param {String} oldvalue The old value
22145          * @param {String} newvalue The new value
22146          */
22147         select : true
22148     });
22149 };
22150
22151 Roo.extend(Roo.bootstrap.MonthField, Roo.bootstrap.Input,  {
22152     
22153     onRender: function(ct, position)
22154     {
22155         
22156         Roo.bootstrap.MonthField.superclass.onRender.call(this, ct, position);
22157         
22158         this.language = this.language || 'en';
22159         this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : this.language.split('-')[0];
22160         this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : "en";
22161         
22162         this.isRTL = Roo.bootstrap.MonthField.dates[this.language].rtl || false;
22163         this.isInline = false;
22164         this.isInput = true;
22165         this.component = this.el.select('.add-on', true).first() || false;
22166         this.component = (this.component && this.component.length === 0) ? false : this.component;
22167         this.hasInput = this.component && this.inputEL().length;
22168         
22169         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.MonthField.template);
22170         
22171         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
22172         
22173         this.picker().on('mousedown', this.onMousedown, this);
22174         this.picker().on('click', this.onClick, this);
22175         
22176         this.picker().addClass('datepicker-dropdown');
22177         
22178         Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
22179             v.setStyle('width', '189px');
22180         });
22181         
22182         this.fillMonths();
22183         
22184         this.update();
22185         
22186         if(this.isInline) {
22187             this.show();
22188         }
22189         
22190     },
22191     
22192     setValue: function(v, suppressEvent)
22193     {   
22194         var o = this.getValue();
22195         
22196         Roo.bootstrap.MonthField.superclass.setValue.call(this, v);
22197         
22198         this.update();
22199
22200         if(suppressEvent !== true){
22201             this.fireEvent('select', this, o, v);
22202         }
22203         
22204     },
22205     
22206     getValue: function()
22207     {
22208         return this.value;
22209     },
22210     
22211     onClick: function(e) 
22212     {
22213         e.stopPropagation();
22214         e.preventDefault();
22215         
22216         var target = e.getTarget();
22217         
22218         if(target.nodeName.toLowerCase() === 'i'){
22219             target = Roo.get(target).dom.parentNode;
22220         }
22221         
22222         var nodeName = target.nodeName;
22223         var className = target.className;
22224         var html = target.innerHTML;
22225         
22226         if(nodeName.toLowerCase() != 'span' || className.indexOf('disabled') > -1 || className.indexOf('month') == -1){
22227             return;
22228         }
22229         
22230         this.vIndex = Roo.bootstrap.MonthField.dates[this.language].monthsShort.indexOf(html);
22231         
22232         this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
22233         
22234         this.hide();
22235                         
22236     },
22237     
22238     picker : function()
22239     {
22240         return this.pickerEl;
22241     },
22242     
22243     fillMonths: function()
22244     {    
22245         var i = 0;
22246         var months = this.picker().select('>.datepicker-months td', true).first();
22247         
22248         months.dom.innerHTML = '';
22249         
22250         while (i < 12) {
22251             var month = {
22252                 tag: 'span',
22253                 cls: 'month',
22254                 html: Roo.bootstrap.MonthField.dates[this.language].monthsShort[i++]
22255             };
22256             
22257             months.createChild(month);
22258         }
22259         
22260     },
22261     
22262     update: function()
22263     {
22264         var _this = this;
22265         
22266         if(typeof(this.vIndex) == 'undefined' && this.value.length){
22267             this.vIndex = Roo.bootstrap.MonthField.dates[this.language].months.indexOf(this.value);
22268         }
22269         
22270         Roo.each(this.pickerEl.select('> .datepicker-months tbody > tr > td > span', true).elements, function(e, k){
22271             e.removeClass('active');
22272             
22273             if(typeof(_this.vIndex) != 'undefined' && k == _this.vIndex){
22274                 e.addClass('active');
22275             }
22276         })
22277     },
22278     
22279     place: function()
22280     {
22281         if(this.isInline) {
22282             return;
22283         }
22284         
22285         this.picker().removeClass(['bottom', 'top']);
22286         
22287         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
22288             /*
22289              * place to the top of element!
22290              *
22291              */
22292             
22293             this.picker().addClass('top');
22294             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
22295             
22296             return;
22297         }
22298         
22299         this.picker().addClass('bottom');
22300         
22301         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
22302     },
22303     
22304     onFocus : function()
22305     {
22306         Roo.bootstrap.MonthField.superclass.onFocus.call(this);
22307         this.show();
22308     },
22309     
22310     onBlur : function()
22311     {
22312         Roo.bootstrap.MonthField.superclass.onBlur.call(this);
22313         
22314         var d = this.inputEl().getValue();
22315         
22316         this.setValue(d);
22317                 
22318         this.hide();
22319     },
22320     
22321     show : function()
22322     {
22323         this.picker().show();
22324         this.picker().select('>.datepicker-months', true).first().show();
22325         this.update();
22326         this.place();
22327         
22328         this.fireEvent('show', this, this.date);
22329     },
22330     
22331     hide : function()
22332     {
22333         if(this.isInline) {
22334             return;
22335         }
22336         this.picker().hide();
22337         this.fireEvent('hide', this, this.date);
22338         
22339     },
22340     
22341     onMousedown: function(e)
22342     {
22343         e.stopPropagation();
22344         e.preventDefault();
22345     },
22346     
22347     keyup: function(e)
22348     {
22349         Roo.bootstrap.MonthField.superclass.keyup.call(this);
22350         this.update();
22351     },
22352
22353     fireKey: function(e)
22354     {
22355         if (!this.picker().isVisible()){
22356             if (e.keyCode == 27)   {// allow escape to hide and re-show picker
22357                 this.show();
22358             }
22359             return;
22360         }
22361         
22362         var dir;
22363         
22364         switch(e.keyCode){
22365             case 27: // escape
22366                 this.hide();
22367                 e.preventDefault();
22368                 break;
22369             case 37: // left
22370             case 39: // right
22371                 dir = e.keyCode == 37 ? -1 : 1;
22372                 
22373                 this.vIndex = this.vIndex + dir;
22374                 
22375                 if(this.vIndex < 0){
22376                     this.vIndex = 0;
22377                 }
22378                 
22379                 if(this.vIndex > 11){
22380                     this.vIndex = 11;
22381                 }
22382                 
22383                 if(isNaN(this.vIndex)){
22384                     this.vIndex = 0;
22385                 }
22386                 
22387                 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
22388                 
22389                 break;
22390             case 38: // up
22391             case 40: // down
22392                 
22393                 dir = e.keyCode == 38 ? -1 : 1;
22394                 
22395                 this.vIndex = this.vIndex + dir * 4;
22396                 
22397                 if(this.vIndex < 0){
22398                     this.vIndex = 0;
22399                 }
22400                 
22401                 if(this.vIndex > 11){
22402                     this.vIndex = 11;
22403                 }
22404                 
22405                 if(isNaN(this.vIndex)){
22406                     this.vIndex = 0;
22407                 }
22408                 
22409                 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
22410                 break;
22411                 
22412             case 13: // enter
22413                 
22414                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
22415                     this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
22416                 }
22417                 
22418                 this.hide();
22419                 e.preventDefault();
22420                 break;
22421             case 9: // tab
22422                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
22423                     this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
22424                 }
22425                 this.hide();
22426                 break;
22427             case 16: // shift
22428             case 17: // ctrl
22429             case 18: // alt
22430                 break;
22431             default :
22432                 this.hide();
22433                 
22434         }
22435     },
22436     
22437     remove: function() 
22438     {
22439         this.picker().remove();
22440     }
22441    
22442 });
22443
22444 Roo.apply(Roo.bootstrap.MonthField,  {
22445     
22446     content : {
22447         tag: 'tbody',
22448         cn: [
22449         {
22450             tag: 'tr',
22451             cn: [
22452             {
22453                 tag: 'td',
22454                 colspan: '7'
22455             }
22456             ]
22457         }
22458         ]
22459     },
22460     
22461     dates:{
22462         en: {
22463             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
22464             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
22465         }
22466     }
22467 });
22468
22469 Roo.apply(Roo.bootstrap.MonthField,  {
22470   
22471     template : {
22472         tag: 'div',
22473         cls: 'datepicker dropdown-menu roo-dynamic',
22474         cn: [
22475             {
22476                 tag: 'div',
22477                 cls: 'datepicker-months',
22478                 cn: [
22479                 {
22480                     tag: 'table',
22481                     cls: 'table-condensed',
22482                     cn:[
22483                         Roo.bootstrap.DateField.content
22484                     ]
22485                 }
22486                 ]
22487             }
22488         ]
22489     }
22490 });
22491
22492  
22493
22494  
22495  /*
22496  * - LGPL
22497  *
22498  * CheckBox
22499  * 
22500  */
22501
22502 /**
22503  * @class Roo.bootstrap.CheckBox
22504  * @extends Roo.bootstrap.Input
22505  * Bootstrap CheckBox class
22506  * 
22507  * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
22508  * @cfg {String} inputValue The value that should go into the generated input element's value when checked.
22509  * @cfg {String} boxLabel The text that appears beside the checkbox
22510  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the checkbox
22511  * @cfg {Boolean} checked initnal the element
22512  * @cfg {Boolean} inline inline the element (default false)
22513  * @cfg {String} groupId the checkbox group id // normal just use for checkbox
22514  * @cfg {String} tooltip label tooltip
22515  * 
22516  * @constructor
22517  * Create a new CheckBox
22518  * @param {Object} config The config object
22519  */
22520
22521 Roo.bootstrap.CheckBox = function(config){
22522     Roo.bootstrap.CheckBox.superclass.constructor.call(this, config);
22523    
22524     this.addEvents({
22525         /**
22526         * @event check
22527         * Fires when the element is checked or unchecked.
22528         * @param {Roo.bootstrap.CheckBox} this This input
22529         * @param {Boolean} checked The new checked value
22530         */
22531        check : true,
22532        /**
22533         * @event click
22534         * Fires when the element is click.
22535         * @param {Roo.bootstrap.CheckBox} this This input
22536         */
22537        click : true
22538     });
22539     
22540 };
22541
22542 Roo.extend(Roo.bootstrap.CheckBox, Roo.bootstrap.Input,  {
22543   
22544     inputType: 'checkbox',
22545     inputValue: 1,
22546     valueOff: 0,
22547     boxLabel: false,
22548     checked: false,
22549     weight : false,
22550     inline: false,
22551     tooltip : '',
22552     
22553     // checkbox success does not make any sense really.. 
22554     invalidClass : "",
22555     validClass : "",
22556     
22557     
22558     getAutoCreate : function()
22559     {
22560         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
22561         
22562         var id = Roo.id();
22563         
22564         var cfg = {};
22565         
22566         cfg.cls = 'form-group form-check ' + this.inputType; //input-group
22567         
22568         if(this.inline){
22569             cfg.cls += ' ' + this.inputType + '-inline  form-check-inline';
22570         }
22571         
22572         var input =  {
22573             tag: 'input',
22574             id : id,
22575             type : this.inputType,
22576             value : this.inputValue,
22577             cls : 'roo-' + this.inputType, //'form-box',
22578             placeholder : this.placeholder || ''
22579             
22580         };
22581         
22582         if(this.inputType != 'radio'){
22583             var hidden =  {
22584                 tag: 'input',
22585                 type : 'hidden',
22586                 cls : 'roo-hidden-value',
22587                 value : this.checked ? this.inputValue : this.valueOff
22588             };
22589         }
22590         
22591             
22592         if (this.weight) { // Validity check?
22593             cfg.cls += " " + this.inputType + "-" + this.weight;
22594         }
22595         
22596         if (this.disabled) {
22597             input.disabled=true;
22598         }
22599         
22600         if(this.checked){
22601             input.checked = this.checked;
22602         }
22603         
22604         if (this.name) {
22605             
22606             input.name = this.name;
22607             
22608             if(this.inputType != 'radio'){
22609                 hidden.name = this.name;
22610                 input.name = '_hidden_' + this.name;
22611             }
22612         }
22613         
22614         if (this.size) {
22615             input.cls += ' input-' + this.size;
22616         }
22617         
22618         var settings=this;
22619         
22620         ['xs','sm','md','lg'].map(function(size){
22621             if (settings[size]) {
22622                 cfg.cls += ' col-' + size + '-' + settings[size];
22623             }
22624         });
22625         
22626         var inputblock = input;
22627          
22628         if (this.before || this.after) {
22629             
22630             inputblock = {
22631                 cls : 'input-group',
22632                 cn :  [] 
22633             };
22634             
22635             if (this.before) {
22636                 inputblock.cn.push({
22637                     tag :'span',
22638                     cls : 'input-group-addon',
22639                     html : this.before
22640                 });
22641             }
22642             
22643             inputblock.cn.push(input);
22644             
22645             if(this.inputType != 'radio'){
22646                 inputblock.cn.push(hidden);
22647             }
22648             
22649             if (this.after) {
22650                 inputblock.cn.push({
22651                     tag :'span',
22652                     cls : 'input-group-addon',
22653                     html : this.after
22654                 });
22655             }
22656             
22657         }
22658         var boxLabelCfg = false;
22659         
22660         if(this.boxLabel){
22661            
22662             boxLabelCfg = {
22663                 tag: 'label',
22664                 //'for': id, // box label is handled by onclick - so no for...
22665                 cls: 'box-label',
22666                 html: this.boxLabel
22667             };
22668             if(this.tooltip){
22669                 boxLabelCfg.tooltip = this.tooltip;
22670             }
22671              
22672         }
22673         
22674         
22675         if (align ==='left' && this.fieldLabel.length) {
22676 //                Roo.log("left and has label");
22677             cfg.cn = [
22678                 {
22679                     tag: 'label',
22680                     'for' :  id,
22681                     cls : 'control-label',
22682                     html : this.fieldLabel
22683                 },
22684                 {
22685                     cls : "", 
22686                     cn: [
22687                         inputblock
22688                     ]
22689                 }
22690             ];
22691             
22692             if (boxLabelCfg) {
22693                 cfg.cn[1].cn.push(boxLabelCfg);
22694             }
22695             
22696             if(this.labelWidth > 12){
22697                 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
22698             }
22699             
22700             if(this.labelWidth < 13 && this.labelmd == 0){
22701                 this.labelmd = this.labelWidth;
22702             }
22703             
22704             if(this.labellg > 0){
22705                 cfg.cn[0].cls += ' col-lg-' + this.labellg;
22706                 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
22707             }
22708             
22709             if(this.labelmd > 0){
22710                 cfg.cn[0].cls += ' col-md-' + this.labelmd;
22711                 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
22712             }
22713             
22714             if(this.labelsm > 0){
22715                 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
22716                 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
22717             }
22718             
22719             if(this.labelxs > 0){
22720                 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
22721                 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
22722             }
22723             
22724         } else if ( this.fieldLabel.length) {
22725 //                Roo.log(" label");
22726                 cfg.cn = [
22727                    
22728                     {
22729                         tag: this.boxLabel ? 'span' : 'label',
22730                         'for': id,
22731                         cls: 'control-label box-input-label',
22732                         //cls : 'input-group-addon',
22733                         html : this.fieldLabel
22734                     },
22735                     
22736                     inputblock
22737                     
22738                 ];
22739                 if (boxLabelCfg) {
22740                     cfg.cn.push(boxLabelCfg);
22741                 }
22742
22743         } else {
22744             
22745 //                Roo.log(" no label && no align");
22746                 cfg.cn = [  inputblock ] ;
22747                 if (boxLabelCfg) {
22748                     cfg.cn.push(boxLabelCfg);
22749                 }
22750
22751                 
22752         }
22753         
22754        
22755         
22756         if(this.inputType != 'radio'){
22757             cfg.cn.push(hidden);
22758         }
22759         
22760         return cfg;
22761         
22762     },
22763     
22764     /**
22765      * return the real input element.
22766      */
22767     inputEl: function ()
22768     {
22769         return this.el.select('input.roo-' + this.inputType,true).first();
22770     },
22771     hiddenEl: function ()
22772     {
22773         return this.el.select('input.roo-hidden-value',true).first();
22774     },
22775     
22776     labelEl: function()
22777     {
22778         return this.el.select('label.control-label',true).first();
22779     },
22780     /* depricated... */
22781     
22782     label: function()
22783     {
22784         return this.labelEl();
22785     },
22786     
22787     boxLabelEl: function()
22788     {
22789         return this.el.select('label.box-label',true).first();
22790     },
22791     
22792     initEvents : function()
22793     {
22794 //        Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
22795         
22796         this.inputEl().on('click', this.onClick,  this);
22797         
22798         if (this.boxLabel) { 
22799             this.el.select('label.box-label',true).first().on('click', this.onClick,  this);
22800         }
22801         
22802         this.startValue = this.getValue();
22803         
22804         if(this.groupId){
22805             Roo.bootstrap.CheckBox.register(this);
22806         }
22807     },
22808     
22809     onClick : function(e)
22810     {   
22811         if(this.fireEvent('click', this, e) !== false){
22812             this.setChecked(!this.checked);
22813         }
22814         
22815     },
22816     
22817     setChecked : function(state,suppressEvent)
22818     {
22819         this.startValue = this.getValue();
22820
22821         if(this.inputType == 'radio'){
22822             
22823             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
22824                 e.dom.checked = false;
22825             });
22826             
22827             this.inputEl().dom.checked = true;
22828             
22829             this.inputEl().dom.value = this.inputValue;
22830             
22831             if(suppressEvent !== true){
22832                 this.fireEvent('check', this, true);
22833             }
22834             
22835             this.validate();
22836             
22837             return;
22838         }
22839         
22840         this.checked = state;
22841         
22842         this.inputEl().dom.checked = state;
22843         
22844         
22845         this.hiddenEl().dom.value = state ? this.inputValue : this.valueOff;
22846         
22847         if(suppressEvent !== true){
22848             this.fireEvent('check', this, state);
22849         }
22850         
22851         this.validate();
22852     },
22853     
22854     getValue : function()
22855     {
22856         if(this.inputType == 'radio'){
22857             return this.getGroupValue();
22858         }
22859         
22860         return this.hiddenEl().dom.value;
22861         
22862     },
22863     
22864     getGroupValue : function()
22865     {
22866         if(typeof(this.el.up('form').child('input[name='+this.name+']:checked', true)) == 'undefined'){
22867             return '';
22868         }
22869         
22870         return this.el.up('form').child('input[name='+this.name+']:checked', true).value;
22871     },
22872     
22873     setValue : function(v,suppressEvent)
22874     {
22875         if(this.inputType == 'radio'){
22876             this.setGroupValue(v, suppressEvent);
22877             return;
22878         }
22879         
22880         this.setChecked(((typeof(v) == 'undefined') ? this.checked : (String(v) === String(this.inputValue))), suppressEvent);
22881         
22882         this.validate();
22883     },
22884     
22885     setGroupValue : function(v, suppressEvent)
22886     {
22887         this.startValue = this.getValue();
22888         
22889         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
22890             e.dom.checked = false;
22891             
22892             if(e.dom.value == v){
22893                 e.dom.checked = true;
22894             }
22895         });
22896         
22897         if(suppressEvent !== true){
22898             this.fireEvent('check', this, true);
22899         }
22900
22901         this.validate();
22902         
22903         return;
22904     },
22905     
22906     validate : function()
22907     {
22908         if(this.getVisibilityEl().hasClass('hidden')){
22909             return true;
22910         }
22911         
22912         if(
22913                 this.disabled || 
22914                 (this.inputType == 'radio' && this.validateRadio()) ||
22915                 (this.inputType == 'checkbox' && this.validateCheckbox())
22916         ){
22917             this.markValid();
22918             return true;
22919         }
22920         
22921         this.markInvalid();
22922         return false;
22923     },
22924     
22925     validateRadio : function()
22926     {
22927         if(this.getVisibilityEl().hasClass('hidden')){
22928             return true;
22929         }
22930         
22931         if(this.allowBlank){
22932             return true;
22933         }
22934         
22935         var valid = false;
22936         
22937         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
22938             if(!e.dom.checked){
22939                 return;
22940             }
22941             
22942             valid = true;
22943             
22944             return false;
22945         });
22946         
22947         return valid;
22948     },
22949     
22950     validateCheckbox : function()
22951     {
22952         if(!this.groupId){
22953             return (this.getValue() == this.inputValue || this.allowBlank) ? true : false;
22954             //return (this.getValue() == this.inputValue) ? true : false;
22955         }
22956         
22957         var group = Roo.bootstrap.CheckBox.get(this.groupId);
22958         
22959         if(!group){
22960             return false;
22961         }
22962         
22963         var r = false;
22964         
22965         for(var i in group){
22966             if(group[i].el.isVisible(true)){
22967                 r = false;
22968                 break;
22969             }
22970             
22971             r = true;
22972         }
22973         
22974         for(var i in group){
22975             if(r){
22976                 break;
22977             }
22978             
22979             r = (group[i].getValue() == group[i].inputValue) ? true : false;
22980         }
22981         
22982         return r;
22983     },
22984     
22985     /**
22986      * Mark this field as valid
22987      */
22988     markValid : function()
22989     {
22990         var _this = this;
22991         
22992         this.fireEvent('valid', this);
22993         
22994         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
22995         
22996         if(this.groupId){
22997             label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
22998         }
22999         
23000         if(label){
23001             label.markValid();
23002         }
23003
23004         if(this.inputType == 'radio'){
23005             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23006                 var fg = e.findParent('.form-group', false, true);
23007                 if (Roo.bootstrap.version == 3) {
23008                     fg.removeClass([_this.invalidClass, _this.validClass]);
23009                     fg.addClass(_this.validClass);
23010                 } else {
23011                     fg.removeClass(['is-valid', 'is-invalid']);
23012                     fg.addClass('is-valid');
23013                 }
23014             });
23015             
23016             return;
23017         }
23018
23019         if(!this.groupId){
23020             var fg = this.el.findParent('.form-group', false, true);
23021             if (Roo.bootstrap.version == 3) {
23022                 fg.removeClass([this.invalidClass, this.validClass]);
23023                 fg.addClass(this.validClass);
23024             } else {
23025                 fg.removeClass(['is-valid', 'is-invalid']);
23026                 fg.addClass('is-valid');
23027             }
23028             return;
23029         }
23030         
23031         var group = Roo.bootstrap.CheckBox.get(this.groupId);
23032         
23033         if(!group){
23034             return;
23035         }
23036         
23037         for(var i in group){
23038             var fg = group[i].el.findParent('.form-group', false, true);
23039             if (Roo.bootstrap.version == 3) {
23040                 fg.removeClass([this.invalidClass, this.validClass]);
23041                 fg.addClass(this.validClass);
23042             } else {
23043                 fg.removeClass(['is-valid', 'is-invalid']);
23044                 fg.addClass('is-valid');
23045             }
23046         }
23047     },
23048     
23049      /**
23050      * Mark this field as invalid
23051      * @param {String} msg The validation message
23052      */
23053     markInvalid : function(msg)
23054     {
23055         if(this.allowBlank){
23056             return;
23057         }
23058         
23059         var _this = this;
23060         
23061         this.fireEvent('invalid', this, msg);
23062         
23063         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
23064         
23065         if(this.groupId){
23066             label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
23067         }
23068         
23069         if(label){
23070             label.markInvalid();
23071         }
23072             
23073         if(this.inputType == 'radio'){
23074             
23075             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23076                 var fg = e.findParent('.form-group', false, true);
23077                 if (Roo.bootstrap.version == 3) {
23078                     fg.removeClass([_this.invalidClass, _this.validClass]);
23079                     fg.addClass(_this.invalidClass);
23080                 } else {
23081                     fg.removeClass(['is-invalid', 'is-valid']);
23082                     fg.addClass('is-invalid');
23083                 }
23084             });
23085             
23086             return;
23087         }
23088         
23089         if(!this.groupId){
23090             var fg = this.el.findParent('.form-group', false, true);
23091             if (Roo.bootstrap.version == 3) {
23092                 fg.removeClass([_this.invalidClass, _this.validClass]);
23093                 fg.addClass(_this.invalidClass);
23094             } else {
23095                 fg.removeClass(['is-invalid', 'is-valid']);
23096                 fg.addClass('is-invalid');
23097             }
23098             return;
23099         }
23100         
23101         var group = Roo.bootstrap.CheckBox.get(this.groupId);
23102         
23103         if(!group){
23104             return;
23105         }
23106         
23107         for(var i in group){
23108             var fg = group[i].el.findParent('.form-group', false, true);
23109             if (Roo.bootstrap.version == 3) {
23110                 fg.removeClass([_this.invalidClass, _this.validClass]);
23111                 fg.addClass(_this.invalidClass);
23112             } else {
23113                 fg.removeClass(['is-invalid', 'is-valid']);
23114                 fg.addClass('is-invalid');
23115             }
23116         }
23117         
23118     },
23119     
23120     clearInvalid : function()
23121     {
23122         Roo.bootstrap.Input.prototype.clearInvalid.call(this);
23123         
23124         // this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
23125         
23126         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
23127         
23128         if (label && label.iconEl) {
23129             label.iconEl.removeClass([ label.validClass, label.invalidClass ]);
23130             label.iconEl.removeClass(['is-invalid', 'is-valid']);
23131         }
23132     },
23133     
23134     disable : function()
23135     {
23136         if(this.inputType != 'radio'){
23137             Roo.bootstrap.CheckBox.superclass.disable.call(this);
23138             return;
23139         }
23140         
23141         var _this = this;
23142         
23143         if(this.rendered){
23144             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23145                 _this.getActionEl().addClass(this.disabledClass);
23146                 e.dom.disabled = true;
23147             });
23148         }
23149         
23150         this.disabled = true;
23151         this.fireEvent("disable", this);
23152         return this;
23153     },
23154
23155     enable : function()
23156     {
23157         if(this.inputType != 'radio'){
23158             Roo.bootstrap.CheckBox.superclass.enable.call(this);
23159             return;
23160         }
23161         
23162         var _this = this;
23163         
23164         if(this.rendered){
23165             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23166                 _this.getActionEl().removeClass(this.disabledClass);
23167                 e.dom.disabled = false;
23168             });
23169         }
23170         
23171         this.disabled = false;
23172         this.fireEvent("enable", this);
23173         return this;
23174     },
23175     
23176     setBoxLabel : function(v)
23177     {
23178         this.boxLabel = v;
23179         
23180         if(this.rendered){
23181             this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
23182         }
23183     }
23184
23185 });
23186
23187 Roo.apply(Roo.bootstrap.CheckBox, {
23188     
23189     groups: {},
23190     
23191      /**
23192     * register a CheckBox Group
23193     * @param {Roo.bootstrap.CheckBox} the CheckBox to add
23194     */
23195     register : function(checkbox)
23196     {
23197         if(typeof(this.groups[checkbox.groupId]) == 'undefined'){
23198             this.groups[checkbox.groupId] = {};
23199         }
23200         
23201         if(this.groups[checkbox.groupId].hasOwnProperty(checkbox.name)){
23202             return;
23203         }
23204         
23205         this.groups[checkbox.groupId][checkbox.name] = checkbox;
23206         
23207     },
23208     /**
23209     * fetch a CheckBox Group based on the group ID
23210     * @param {string} the group ID
23211     * @returns {Roo.bootstrap.CheckBox} the CheckBox group
23212     */
23213     get: function(groupId) {
23214         if (typeof(this.groups[groupId]) == 'undefined') {
23215             return false;
23216         }
23217         
23218         return this.groups[groupId] ;
23219     }
23220     
23221     
23222 });
23223 /*
23224  * - LGPL
23225  *
23226  * RadioItem
23227  * 
23228  */
23229
23230 /**
23231  * @class Roo.bootstrap.Radio
23232  * @extends Roo.bootstrap.Component
23233  * Bootstrap Radio class
23234  * @cfg {String} boxLabel - the label associated
23235  * @cfg {String} value - the value of radio
23236  * 
23237  * @constructor
23238  * Create a new Radio
23239  * @param {Object} config The config object
23240  */
23241 Roo.bootstrap.Radio = function(config){
23242     Roo.bootstrap.Radio.superclass.constructor.call(this, config);
23243     
23244 };
23245
23246 Roo.extend(Roo.bootstrap.Radio, Roo.bootstrap.Component, {
23247     
23248     boxLabel : '',
23249     
23250     value : '',
23251     
23252     getAutoCreate : function()
23253     {
23254         var cfg = {
23255             tag : 'div',
23256             cls : 'form-group radio',
23257             cn : [
23258                 {
23259                     tag : 'label',
23260                     cls : 'box-label',
23261                     html : this.boxLabel
23262                 }
23263             ]
23264         };
23265         
23266         return cfg;
23267     },
23268     
23269     initEvents : function() 
23270     {
23271         this.parent().register(this);
23272         
23273         this.el.on('click', this.onClick, this);
23274         
23275     },
23276     
23277     onClick : function(e)
23278     {
23279         if(this.parent().fireEvent('click', this.parent(), this, e) !== false){
23280             this.setChecked(true);
23281         }
23282     },
23283     
23284     setChecked : function(state, suppressEvent)
23285     {
23286         this.parent().setValue(this.value, suppressEvent);
23287         
23288     },
23289     
23290     setBoxLabel : function(v)
23291     {
23292         this.boxLabel = v;
23293         
23294         if(this.rendered){
23295             this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
23296         }
23297     }
23298     
23299 });
23300  
23301
23302  /*
23303  * - LGPL
23304  *
23305  * Input
23306  * 
23307  */
23308
23309 /**
23310  * @class Roo.bootstrap.SecurePass
23311  * @extends Roo.bootstrap.Input
23312  * Bootstrap SecurePass class
23313  *
23314  * 
23315  * @constructor
23316  * Create a new SecurePass
23317  * @param {Object} config The config object
23318  */
23319  
23320 Roo.bootstrap.SecurePass = function (config) {
23321     // these go here, so the translation tool can replace them..
23322     this.errors = {
23323         PwdEmpty: "Please type a password, and then retype it to confirm.",
23324         PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
23325         PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
23326         PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
23327         IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
23328         FNInPwd: "Your password can't contain your first name. Please type a different password.",
23329         LNInPwd: "Your password can't contain your last name. Please type a different password.",
23330         TooWeak: "Your password is Too Weak."
23331     },
23332     this.meterLabel = "Password strength:";
23333     this.pwdStrengths = ["Too Weak", "Weak", "Medium", "Strong"];
23334     this.meterClass = [
23335         "roo-password-meter-tooweak", 
23336         "roo-password-meter-weak", 
23337         "roo-password-meter-medium", 
23338         "roo-password-meter-strong", 
23339         "roo-password-meter-grey"
23340     ];
23341     
23342     this.errors = {};
23343     
23344     Roo.bootstrap.SecurePass.superclass.constructor.call(this, config);
23345 }
23346
23347 Roo.extend(Roo.bootstrap.SecurePass, Roo.bootstrap.Input, {
23348     /**
23349      * @cfg {String/Object} errors A Error spec, or true for a default spec (defaults to
23350      * {
23351      *  PwdEmpty: "Please type a password, and then retype it to confirm.",
23352      *  PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
23353      *  PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
23354      *  PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
23355      *  IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
23356      *  FNInPwd: "Your password can't contain your first name. Please type a different password.",
23357      *  LNInPwd: "Your password can't contain your last name. Please type a different password."
23358      * })
23359      */
23360     // private
23361     
23362     meterWidth: 300,
23363     errorMsg :'',    
23364     errors: false,
23365     imageRoot: '/',
23366     /**
23367      * @cfg {String/Object} Label for the strength meter (defaults to
23368      * 'Password strength:')
23369      */
23370     // private
23371     meterLabel: '',
23372     /**
23373      * @cfg {String/Object} pwdStrengths A pwdStrengths spec, or true for a default spec (defaults to
23374      * ['Weak', 'Medium', 'Strong'])
23375      */
23376     // private    
23377     pwdStrengths: false,    
23378     // private
23379     strength: 0,
23380     // private
23381     _lastPwd: null,
23382     // private
23383     kCapitalLetter: 0,
23384     kSmallLetter: 1,
23385     kDigit: 2,
23386     kPunctuation: 3,
23387     
23388     insecure: false,
23389     // private
23390     initEvents: function ()
23391     {
23392         Roo.bootstrap.SecurePass.superclass.initEvents.call(this);
23393
23394         if (this.el.is('input[type=password]') && Roo.isSafari) {
23395             this.el.on('keydown', this.SafariOnKeyDown, this);
23396         }
23397
23398         this.el.on('keyup', this.checkStrength, this, {buffer: 50});
23399     },
23400     // private
23401     onRender: function (ct, position)
23402     {
23403         Roo.bootstrap.SecurePass.superclass.onRender.call(this, ct, position);
23404         this.wrap = this.el.wrap({cls: 'x-form-field-wrap'});
23405         this.trigger = this.wrap.createChild({tag: 'div', cls: 'StrengthMeter ' + this.triggerClass});
23406
23407         this.trigger.createChild({
23408                    cn: [
23409                     {
23410                     //id: 'PwdMeter',
23411                     tag: 'div',
23412                     cls: 'roo-password-meter-grey col-xs-12',
23413                     style: {
23414                         //width: 0,
23415                         //width: this.meterWidth + 'px'                                                
23416                         }
23417                     },
23418                     {                            
23419                          cls: 'roo-password-meter-text'                          
23420                     }
23421                 ]            
23422         });
23423
23424          
23425         if (this.hideTrigger) {
23426             this.trigger.setDisplayed(false);
23427         }
23428         this.setSize(this.width || '', this.height || '');
23429     },
23430     // private
23431     onDestroy: function ()
23432     {
23433         if (this.trigger) {
23434             this.trigger.removeAllListeners();
23435             this.trigger.remove();
23436         }
23437         if (this.wrap) {
23438             this.wrap.remove();
23439         }
23440         Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
23441     },
23442     // private
23443     checkStrength: function ()
23444     {
23445         var pwd = this.inputEl().getValue();
23446         if (pwd == this._lastPwd) {
23447             return;
23448         }
23449
23450         var strength;
23451         if (this.ClientSideStrongPassword(pwd)) {
23452             strength = 3;
23453         } else if (this.ClientSideMediumPassword(pwd)) {
23454             strength = 2;
23455         } else if (this.ClientSideWeakPassword(pwd)) {
23456             strength = 1;
23457         } else {
23458             strength = 0;
23459         }
23460         
23461         Roo.log('strength1: ' + strength);
23462         
23463         //var pm = this.trigger.child('div/div/div').dom;
23464         var pm = this.trigger.child('div/div');
23465         pm.removeClass(this.meterClass);
23466         pm.addClass(this.meterClass[strength]);
23467                 
23468         
23469         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
23470                 
23471         pt.innerHTML = this.meterLabel + '&nbsp;' + this.pwdStrengths[strength];
23472         
23473         this._lastPwd = pwd;
23474     },
23475     reset: function ()
23476     {
23477         Roo.bootstrap.SecurePass.superclass.reset.call(this);
23478         
23479         this._lastPwd = '';
23480         
23481         var pm = this.trigger.child('div/div');
23482         pm.removeClass(this.meterClass);
23483         pm.addClass('roo-password-meter-grey');        
23484         
23485         
23486         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
23487         
23488         pt.innerHTML = '';
23489         this.inputEl().dom.type='password';
23490     },
23491     // private
23492     validateValue: function (value)
23493     {
23494         if (!Roo.bootstrap.SecurePass.superclass.validateValue.call(this, value)) {
23495             return false;
23496         }
23497         if (value.length == 0) {
23498             if (this.allowBlank) {
23499                 this.clearInvalid();
23500                 return true;
23501             }
23502
23503             this.markInvalid(this.errors.PwdEmpty);
23504             this.errorMsg = this.errors.PwdEmpty;
23505             return false;
23506         }
23507         
23508         if(this.insecure){
23509             return true;
23510         }
23511         
23512         if (!value.match(/[\x21-\x7e]+/)) {
23513             this.markInvalid(this.errors.PwdBadChar);
23514             this.errorMsg = this.errors.PwdBadChar;
23515             return false;
23516         }
23517         if (value.length < 6) {
23518             this.markInvalid(this.errors.PwdShort);
23519             this.errorMsg = this.errors.PwdShort;
23520             return false;
23521         }
23522         if (value.length > 16) {
23523             this.markInvalid(this.errors.PwdLong);
23524             this.errorMsg = this.errors.PwdLong;
23525             return false;
23526         }
23527         var strength;
23528         if (this.ClientSideStrongPassword(value)) {
23529             strength = 3;
23530         } else if (this.ClientSideMediumPassword(value)) {
23531             strength = 2;
23532         } else if (this.ClientSideWeakPassword(value)) {
23533             strength = 1;
23534         } else {
23535             strength = 0;
23536         }
23537
23538         
23539         if (strength < 2) {
23540             //this.markInvalid(this.errors.TooWeak);
23541             this.errorMsg = this.errors.TooWeak;
23542             //return false;
23543         }
23544         
23545         
23546         console.log('strength2: ' + strength);
23547         
23548         //var pm = this.trigger.child('div/div/div').dom;
23549         
23550         var pm = this.trigger.child('div/div');
23551         pm.removeClass(this.meterClass);
23552         pm.addClass(this.meterClass[strength]);
23553                 
23554         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
23555                 
23556         pt.innerHTML = this.meterLabel + '&nbsp;' + this.pwdStrengths[strength];
23557         
23558         this.errorMsg = ''; 
23559         return true;
23560     },
23561     // private
23562     CharacterSetChecks: function (type)
23563     {
23564         this.type = type;
23565         this.fResult = false;
23566     },
23567     // private
23568     isctype: function (character, type)
23569     {
23570         switch (type) {  
23571             case this.kCapitalLetter:
23572                 if (character >= 'A' && character <= 'Z') {
23573                     return true;
23574                 }
23575                 break;
23576             
23577             case this.kSmallLetter:
23578                 if (character >= 'a' && character <= 'z') {
23579                     return true;
23580                 }
23581                 break;
23582             
23583             case this.kDigit:
23584                 if (character >= '0' && character <= '9') {
23585                     return true;
23586                 }
23587                 break;
23588             
23589             case this.kPunctuation:
23590                 if ('!@#$%^&*()_+-=\'";:[{]}|.>,</?`~'.indexOf(character) >= 0) {
23591                     return true;
23592                 }
23593                 break;
23594             
23595             default:
23596                 return false;
23597         }
23598
23599     },
23600     // private
23601     IsLongEnough: function (pwd, size)
23602     {
23603         return !(pwd == null || isNaN(size) || pwd.length < size);
23604     },
23605     // private
23606     SpansEnoughCharacterSets: function (word, nb)
23607     {
23608         if (!this.IsLongEnough(word, nb))
23609         {
23610             return false;
23611         }
23612
23613         var characterSetChecks = new Array(
23614             new this.CharacterSetChecks(this.kCapitalLetter), new this.CharacterSetChecks(this.kSmallLetter),
23615             new this.CharacterSetChecks(this.kDigit), new this.CharacterSetChecks(this.kPunctuation)
23616         );
23617         
23618         for (var index = 0; index < word.length; ++index) {
23619             for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
23620                 if (!characterSetChecks[nCharSet].fResult && this.isctype(word.charAt(index), characterSetChecks[nCharSet].type)) {
23621                     characterSetChecks[nCharSet].fResult = true;
23622                     break;
23623                 }
23624             }
23625         }
23626
23627         var nCharSets = 0;
23628         for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
23629             if (characterSetChecks[nCharSet].fResult) {
23630                 ++nCharSets;
23631             }
23632         }
23633
23634         if (nCharSets < nb) {
23635             return false;
23636         }
23637         return true;
23638     },
23639     // private
23640     ClientSideStrongPassword: function (pwd)
23641     {
23642         return this.IsLongEnough(pwd, 8) && this.SpansEnoughCharacterSets(pwd, 3);
23643     },
23644     // private
23645     ClientSideMediumPassword: function (pwd)
23646     {
23647         return this.IsLongEnough(pwd, 7) && this.SpansEnoughCharacterSets(pwd, 2);
23648     },
23649     // private
23650     ClientSideWeakPassword: function (pwd)
23651     {
23652         return this.IsLongEnough(pwd, 6) || !this.IsLongEnough(pwd, 0);
23653     }
23654           
23655 })//<script type="text/javascript">
23656
23657 /*
23658  * Based  Ext JS Library 1.1.1
23659  * Copyright(c) 2006-2007, Ext JS, LLC.
23660  * LGPL
23661  *
23662  */
23663  
23664 /**
23665  * @class Roo.HtmlEditorCore
23666  * @extends Roo.Component
23667  * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
23668  *
23669  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
23670  */
23671
23672 Roo.HtmlEditorCore = function(config){
23673     
23674     
23675     Roo.HtmlEditorCore.superclass.constructor.call(this, config);
23676     
23677     
23678     this.addEvents({
23679         /**
23680          * @event initialize
23681          * Fires when the editor is fully initialized (including the iframe)
23682          * @param {Roo.HtmlEditorCore} this
23683          */
23684         initialize: true,
23685         /**
23686          * @event activate
23687          * Fires when the editor is first receives the focus. Any insertion must wait
23688          * until after this event.
23689          * @param {Roo.HtmlEditorCore} this
23690          */
23691         activate: true,
23692          /**
23693          * @event beforesync
23694          * Fires before the textarea is updated with content from the editor iframe. Return false
23695          * to cancel the sync.
23696          * @param {Roo.HtmlEditorCore} this
23697          * @param {String} html
23698          */
23699         beforesync: true,
23700          /**
23701          * @event beforepush
23702          * Fires before the iframe editor is updated with content from the textarea. Return false
23703          * to cancel the push.
23704          * @param {Roo.HtmlEditorCore} this
23705          * @param {String} html
23706          */
23707         beforepush: true,
23708          /**
23709          * @event sync
23710          * Fires when the textarea is updated with content from the editor iframe.
23711          * @param {Roo.HtmlEditorCore} this
23712          * @param {String} html
23713          */
23714         sync: true,
23715          /**
23716          * @event push
23717          * Fires when the iframe editor is updated with content from the textarea.
23718          * @param {Roo.HtmlEditorCore} this
23719          * @param {String} html
23720          */
23721         push: true,
23722         
23723         /**
23724          * @event editorevent
23725          * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
23726          * @param {Roo.HtmlEditorCore} this
23727          */
23728         editorevent: true
23729         
23730     });
23731     
23732     // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
23733     
23734     // defaults : white / black...
23735     this.applyBlacklists();
23736     
23737     
23738     
23739 };
23740
23741
23742 Roo.extend(Roo.HtmlEditorCore, Roo.Component,  {
23743
23744
23745      /**
23746      * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field 
23747      */
23748     
23749     owner : false,
23750     
23751      /**
23752      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
23753      *                        Roo.resizable.
23754      */
23755     resizable : false,
23756      /**
23757      * @cfg {Number} height (in pixels)
23758      */   
23759     height: 300,
23760    /**
23761      * @cfg {Number} width (in pixels)
23762      */   
23763     width: 500,
23764     
23765     /**
23766      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
23767      * 
23768      */
23769     stylesheets: false,
23770     
23771     // id of frame..
23772     frameId: false,
23773     
23774     // private properties
23775     validationEvent : false,
23776     deferHeight: true,
23777     initialized : false,
23778     activated : false,
23779     sourceEditMode : false,
23780     onFocus : Roo.emptyFn,
23781     iframePad:3,
23782     hideMode:'offsets',
23783     
23784     clearUp: true,
23785     
23786     // blacklist + whitelisted elements..
23787     black: false,
23788     white: false,
23789      
23790     bodyCls : '',
23791
23792     /**
23793      * Protected method that will not generally be called directly. It
23794      * is called when the editor initializes the iframe with HTML contents. Override this method if you
23795      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
23796      */
23797     getDocMarkup : function(){
23798         // body styles..
23799         var st = '';
23800         
23801         // inherit styels from page...?? 
23802         if (this.stylesheets === false) {
23803             
23804             Roo.get(document.head).select('style').each(function(node) {
23805                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
23806             });
23807             
23808             Roo.get(document.head).select('link').each(function(node) { 
23809                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
23810             });
23811             
23812         } else if (!this.stylesheets.length) {
23813                 // simple..
23814                 st = '<style type="text/css">' +
23815                     'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
23816                    '</style>';
23817         } else {
23818             for (var i in this.stylesheets) { 
23819                 st += '<link rel="stylesheet" href="' + this.stylesheets[i] +'" type="text/css">';
23820             }
23821             
23822         }
23823         
23824         st +=  '<style type="text/css">' +
23825             'IMG { cursor: pointer } ' +
23826         '</style>';
23827
23828         var cls = 'roo-htmleditor-body';
23829         
23830         if(this.bodyCls.length){
23831             cls += ' ' + this.bodyCls;
23832         }
23833         
23834         return '<html><head>' + st  +
23835             //<style type="text/css">' +
23836             //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
23837             //'</style>' +
23838             ' </head><body contenteditable="true" data-enable-grammerly="true" class="' +  cls + '"></body></html>';
23839     },
23840
23841     // private
23842     onRender : function(ct, position)
23843     {
23844         var _t = this;
23845         //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
23846         this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
23847         
23848         
23849         this.el.dom.style.border = '0 none';
23850         this.el.dom.setAttribute('tabIndex', -1);
23851         this.el.addClass('x-hidden hide');
23852         
23853         
23854         
23855         if(Roo.isIE){ // fix IE 1px bogus margin
23856             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
23857         }
23858        
23859         
23860         this.frameId = Roo.id();
23861         
23862          
23863         
23864         var iframe = this.owner.wrap.createChild({
23865             tag: 'iframe',
23866             cls: 'form-control', // bootstrap..
23867             id: this.frameId,
23868             name: this.frameId,
23869             frameBorder : 'no',
23870             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
23871         }, this.el
23872         );
23873         
23874         
23875         this.iframe = iframe.dom;
23876
23877          this.assignDocWin();
23878         
23879         this.doc.designMode = 'on';
23880        
23881         this.doc.open();
23882         this.doc.write(this.getDocMarkup());
23883         this.doc.close();
23884
23885         
23886         var task = { // must defer to wait for browser to be ready
23887             run : function(){
23888                 //console.log("run task?" + this.doc.readyState);
23889                 this.assignDocWin();
23890                 if(this.doc.body || this.doc.readyState == 'complete'){
23891                     try {
23892                         this.doc.designMode="on";
23893                     } catch (e) {
23894                         return;
23895                     }
23896                     Roo.TaskMgr.stop(task);
23897                     this.initEditor.defer(10, this);
23898                 }
23899             },
23900             interval : 10,
23901             duration: 10000,
23902             scope: this
23903         };
23904         Roo.TaskMgr.start(task);
23905
23906     },
23907
23908     // private
23909     onResize : function(w, h)
23910     {
23911          Roo.log('resize: ' +w + ',' + h );
23912         //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
23913         if(!this.iframe){
23914             return;
23915         }
23916         if(typeof w == 'number'){
23917             
23918             this.iframe.style.width = w + 'px';
23919         }
23920         if(typeof h == 'number'){
23921             
23922             this.iframe.style.height = h + 'px';
23923             if(this.doc){
23924                 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
23925             }
23926         }
23927         
23928     },
23929
23930     /**
23931      * Toggles the editor between standard and source edit mode.
23932      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
23933      */
23934     toggleSourceEdit : function(sourceEditMode){
23935         
23936         this.sourceEditMode = sourceEditMode === true;
23937         
23938         if(this.sourceEditMode){
23939  
23940             Roo.get(this.iframe).addClass(['x-hidden','hide']);     //FIXME - what's the BS styles for these
23941             
23942         }else{
23943             Roo.get(this.iframe).removeClass(['x-hidden','hide']);
23944             //this.iframe.className = '';
23945             this.deferFocus();
23946         }
23947         //this.setSize(this.owner.wrap.getSize());
23948         //this.fireEvent('editmodechange', this, this.sourceEditMode);
23949     },
23950
23951     
23952   
23953
23954     /**
23955      * Protected method that will not generally be called directly. If you need/want
23956      * custom HTML cleanup, this is the method you should override.
23957      * @param {String} html The HTML to be cleaned
23958      * return {String} The cleaned HTML
23959      */
23960     cleanHtml : function(html){
23961         html = String(html);
23962         if(html.length > 5){
23963             if(Roo.isSafari){ // strip safari nonsense
23964                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
23965             }
23966         }
23967         if(html == '&nbsp;'){
23968             html = '';
23969         }
23970         return html;
23971     },
23972
23973     /**
23974      * HTML Editor -> Textarea
23975      * Protected method that will not generally be called directly. Syncs the contents
23976      * of the editor iframe with the textarea.
23977      */
23978     syncValue : function(){
23979         if(this.initialized){
23980             var bd = (this.doc.body || this.doc.documentElement);
23981             //this.cleanUpPaste(); -- this is done else where and causes havoc..
23982             var html = bd.innerHTML;
23983             if(Roo.isSafari){
23984                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
23985                 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
23986                 if(m && m[1]){
23987                     html = '<div style="'+m[0]+'">' + html + '</div>';
23988                 }
23989             }
23990             html = this.cleanHtml(html);
23991             // fix up the special chars.. normaly like back quotes in word...
23992             // however we do not want to do this with chinese..
23993             html = html.replace(/[\uD800-\uDBFF][\uDC00-\uDFFF]|[\u0080-\uFFFF]/g, function(match) {
23994                 
23995                 var cc = match.charCodeAt();
23996
23997                 // Get the character value, handling surrogate pairs
23998                 if (match.length == 2) {
23999                     // It's a surrogate pair, calculate the Unicode code point
24000                     var high = match.charCodeAt(0) - 0xD800;
24001                     var low  = match.charCodeAt(1) - 0xDC00;
24002                     cc = (high * 0x400) + low + 0x10000;
24003                 }  else if (
24004                     (cc >= 0x4E00 && cc < 0xA000 ) ||
24005                     (cc >= 0x3400 && cc < 0x4E00 ) ||
24006                     (cc >= 0xf900 && cc < 0xfb00 )
24007                 ) {
24008                         return match;
24009                 }  
24010          
24011                 // No, use a numeric entity. Here we brazenly (and possibly mistakenly)
24012                 return "&#" + cc + ";";
24013                 
24014                 
24015             });
24016             
24017             
24018              
24019             if(this.owner.fireEvent('beforesync', this, html) !== false){
24020                 this.el.dom.value = html;
24021                 this.owner.fireEvent('sync', this, html);
24022             }
24023         }
24024     },
24025
24026     /**
24027      * Protected method that will not generally be called directly. Pushes the value of the textarea
24028      * into the iframe editor.
24029      */
24030     pushValue : function(){
24031         if(this.initialized){
24032             var v = this.el.dom.value.trim();
24033             
24034 //            if(v.length < 1){
24035 //                v = '&#160;';
24036 //            }
24037             
24038             if(this.owner.fireEvent('beforepush', this, v) !== false){
24039                 var d = (this.doc.body || this.doc.documentElement);
24040                 d.innerHTML = v;
24041                 this.cleanUpPaste();
24042                 this.el.dom.value = d.innerHTML;
24043                 this.owner.fireEvent('push', this, v);
24044             }
24045         }
24046     },
24047
24048     // private
24049     deferFocus : function(){
24050         this.focus.defer(10, this);
24051     },
24052
24053     // doc'ed in Field
24054     focus : function(){
24055         if(this.win && !this.sourceEditMode){
24056             this.win.focus();
24057         }else{
24058             this.el.focus();
24059         }
24060     },
24061     
24062     assignDocWin: function()
24063     {
24064         var iframe = this.iframe;
24065         
24066          if(Roo.isIE){
24067             this.doc = iframe.contentWindow.document;
24068             this.win = iframe.contentWindow;
24069         } else {
24070 //            if (!Roo.get(this.frameId)) {
24071 //                return;
24072 //            }
24073 //            this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
24074 //            this.win = Roo.get(this.frameId).dom.contentWindow;
24075             
24076             if (!Roo.get(this.frameId) && !iframe.contentDocument) {
24077                 return;
24078             }
24079             
24080             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
24081             this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
24082         }
24083     },
24084     
24085     // private
24086     initEditor : function(){
24087         //console.log("INIT EDITOR");
24088         this.assignDocWin();
24089         
24090         
24091         
24092         this.doc.designMode="on";
24093         this.doc.open();
24094         this.doc.write(this.getDocMarkup());
24095         this.doc.close();
24096         
24097         var dbody = (this.doc.body || this.doc.documentElement);
24098         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
24099         // this copies styles from the containing element into thsi one..
24100         // not sure why we need all of this..
24101         //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
24102         
24103         //var ss = this.el.getStyles( 'background-image', 'background-repeat');
24104         //ss['background-attachment'] = 'fixed'; // w3c
24105         dbody.bgProperties = 'fixed'; // ie
24106         //Roo.DomHelper.applyStyles(dbody, ss);
24107         Roo.EventManager.on(this.doc, {
24108             //'mousedown': this.onEditorEvent,
24109             'mouseup': this.onEditorEvent,
24110             'dblclick': this.onEditorEvent,
24111             'click': this.onEditorEvent,
24112             'keyup': this.onEditorEvent,
24113             buffer:100,
24114             scope: this
24115         });
24116         if(Roo.isGecko){
24117             Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
24118         }
24119         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
24120             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
24121         }
24122         this.initialized = true;
24123
24124         this.owner.fireEvent('initialize', this);
24125         this.pushValue();
24126     },
24127
24128     // private
24129     onDestroy : function(){
24130         
24131         
24132         
24133         if(this.rendered){
24134             
24135             //for (var i =0; i < this.toolbars.length;i++) {
24136             //    // fixme - ask toolbars for heights?
24137             //    this.toolbars[i].onDestroy();
24138            // }
24139             
24140             //this.wrap.dom.innerHTML = '';
24141             //this.wrap.remove();
24142         }
24143     },
24144
24145     // private
24146     onFirstFocus : function(){
24147         
24148         this.assignDocWin();
24149         
24150         
24151         this.activated = true;
24152          
24153     
24154         if(Roo.isGecko){ // prevent silly gecko errors
24155             this.win.focus();
24156             var s = this.win.getSelection();
24157             if(!s.focusNode || s.focusNode.nodeType != 3){
24158                 var r = s.getRangeAt(0);
24159                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
24160                 r.collapse(true);
24161                 this.deferFocus();
24162             }
24163             try{
24164                 this.execCmd('useCSS', true);
24165                 this.execCmd('styleWithCSS', false);
24166             }catch(e){}
24167         }
24168         this.owner.fireEvent('activate', this);
24169     },
24170
24171     // private
24172     adjustFont: function(btn){
24173         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
24174         //if(Roo.isSafari){ // safari
24175         //    adjust *= 2;
24176        // }
24177         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
24178         if(Roo.isSafari){ // safari
24179             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
24180             v =  (v < 10) ? 10 : v;
24181             v =  (v > 48) ? 48 : v;
24182             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
24183             
24184         }
24185         
24186         
24187         v = Math.max(1, v+adjust);
24188         
24189         this.execCmd('FontSize', v  );
24190     },
24191
24192     onEditorEvent : function(e)
24193     {
24194         this.owner.fireEvent('editorevent', this, e);
24195       //  this.updateToolbar();
24196         this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
24197     },
24198
24199     insertTag : function(tg)
24200     {
24201         // could be a bit smarter... -> wrap the current selected tRoo..
24202         if (tg.toLowerCase() == 'span' ||
24203             tg.toLowerCase() == 'code' ||
24204             tg.toLowerCase() == 'sup' ||
24205             tg.toLowerCase() == 'sub' 
24206             ) {
24207             
24208             range = this.createRange(this.getSelection());
24209             var wrappingNode = this.doc.createElement(tg.toLowerCase());
24210             wrappingNode.appendChild(range.extractContents());
24211             range.insertNode(wrappingNode);
24212
24213             return;
24214             
24215             
24216             
24217         }
24218         this.execCmd("formatblock",   tg);
24219         
24220     },
24221     
24222     insertText : function(txt)
24223     {
24224         
24225         
24226         var range = this.createRange();
24227         range.deleteContents();
24228                //alert(Sender.getAttribute('label'));
24229                
24230         range.insertNode(this.doc.createTextNode(txt));
24231     } ,
24232     
24233      
24234
24235     /**
24236      * Executes a Midas editor command on the editor document and performs necessary focus and
24237      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
24238      * @param {String} cmd The Midas command
24239      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
24240      */
24241     relayCmd : function(cmd, value){
24242         this.win.focus();
24243         this.execCmd(cmd, value);
24244         this.owner.fireEvent('editorevent', this);
24245         //this.updateToolbar();
24246         this.owner.deferFocus();
24247     },
24248
24249     /**
24250      * Executes a Midas editor command directly on the editor document.
24251      * For visual commands, you should use {@link #relayCmd} instead.
24252      * <b>This should only be called after the editor is initialized.</b>
24253      * @param {String} cmd The Midas command
24254      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
24255      */
24256     execCmd : function(cmd, value){
24257         this.doc.execCommand(cmd, false, value === undefined ? null : value);
24258         this.syncValue();
24259     },
24260  
24261  
24262    
24263     /**
24264      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
24265      * to insert tRoo.
24266      * @param {String} text | dom node.. 
24267      */
24268     insertAtCursor : function(text)
24269     {
24270         
24271         if(!this.activated){
24272             return;
24273         }
24274         /*
24275         if(Roo.isIE){
24276             this.win.focus();
24277             var r = this.doc.selection.createRange();
24278             if(r){
24279                 r.collapse(true);
24280                 r.pasteHTML(text);
24281                 this.syncValue();
24282                 this.deferFocus();
24283             
24284             }
24285             return;
24286         }
24287         */
24288         if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
24289             this.win.focus();
24290             
24291             
24292             // from jquery ui (MIT licenced)
24293             var range, node;
24294             var win = this.win;
24295             
24296             if (win.getSelection && win.getSelection().getRangeAt) {
24297                 range = win.getSelection().getRangeAt(0);
24298                 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
24299                 range.insertNode(node);
24300             } else if (win.document.selection && win.document.selection.createRange) {
24301                 // no firefox support
24302                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
24303                 win.document.selection.createRange().pasteHTML(txt);
24304             } else {
24305                 // no firefox support
24306                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
24307                 this.execCmd('InsertHTML', txt);
24308             } 
24309             
24310             this.syncValue();
24311             
24312             this.deferFocus();
24313         }
24314     },
24315  // private
24316     mozKeyPress : function(e){
24317         if(e.ctrlKey){
24318             var c = e.getCharCode(), cmd;
24319           
24320             if(c > 0){
24321                 c = String.fromCharCode(c).toLowerCase();
24322                 switch(c){
24323                     case 'b':
24324                         cmd = 'bold';
24325                         break;
24326                     case 'i':
24327                         cmd = 'italic';
24328                         break;
24329                     
24330                     case 'u':
24331                         cmd = 'underline';
24332                         break;
24333                     
24334                     case 'v':
24335                         this.cleanUpPaste.defer(100, this);
24336                         return;
24337                         
24338                 }
24339                 if(cmd){
24340                     this.win.focus();
24341                     this.execCmd(cmd);
24342                     this.deferFocus();
24343                     e.preventDefault();
24344                 }
24345                 
24346             }
24347         }
24348     },
24349
24350     // private
24351     fixKeys : function(){ // load time branching for fastest keydown performance
24352         if(Roo.isIE){
24353             return function(e){
24354                 var k = e.getKey(), r;
24355                 if(k == e.TAB){
24356                     e.stopEvent();
24357                     r = this.doc.selection.createRange();
24358                     if(r){
24359                         r.collapse(true);
24360                         r.pasteHTML('&#160;&#160;&#160;&#160;');
24361                         this.deferFocus();
24362                     }
24363                     return;
24364                 }
24365                 
24366                 if(k == e.ENTER){
24367                     r = this.doc.selection.createRange();
24368                     if(r){
24369                         var target = r.parentElement();
24370                         if(!target || target.tagName.toLowerCase() != 'li'){
24371                             e.stopEvent();
24372                             r.pasteHTML('<br />');
24373                             r.collapse(false);
24374                             r.select();
24375                         }
24376                     }
24377                 }
24378                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
24379                     this.cleanUpPaste.defer(100, this);
24380                     return;
24381                 }
24382                 
24383                 
24384             };
24385         }else if(Roo.isOpera){
24386             return function(e){
24387                 var k = e.getKey();
24388                 if(k == e.TAB){
24389                     e.stopEvent();
24390                     this.win.focus();
24391                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
24392                     this.deferFocus();
24393                 }
24394                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
24395                     this.cleanUpPaste.defer(100, this);
24396                     return;
24397                 }
24398                 
24399             };
24400         }else if(Roo.isSafari){
24401             return function(e){
24402                 var k = e.getKey();
24403                 
24404                 if(k == e.TAB){
24405                     e.stopEvent();
24406                     this.execCmd('InsertText','\t');
24407                     this.deferFocus();
24408                     return;
24409                 }
24410                if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
24411                     this.cleanUpPaste.defer(100, this);
24412                     return;
24413                 }
24414                 
24415              };
24416         }
24417     }(),
24418     
24419     getAllAncestors: function()
24420     {
24421         var p = this.getSelectedNode();
24422         var a = [];
24423         if (!p) {
24424             a.push(p); // push blank onto stack..
24425             p = this.getParentElement();
24426         }
24427         
24428         
24429         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
24430             a.push(p);
24431             p = p.parentNode;
24432         }
24433         a.push(this.doc.body);
24434         return a;
24435     },
24436     lastSel : false,
24437     lastSelNode : false,
24438     
24439     
24440     getSelection : function() 
24441     {
24442         this.assignDocWin();
24443         return Roo.isIE ? this.doc.selection : this.win.getSelection();
24444     },
24445     
24446     getSelectedNode: function() 
24447     {
24448         // this may only work on Gecko!!!
24449         
24450         // should we cache this!!!!
24451         
24452         
24453         
24454          
24455         var range = this.createRange(this.getSelection()).cloneRange();
24456         
24457         if (Roo.isIE) {
24458             var parent = range.parentElement();
24459             while (true) {
24460                 var testRange = range.duplicate();
24461                 testRange.moveToElementText(parent);
24462                 if (testRange.inRange(range)) {
24463                     break;
24464                 }
24465                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
24466                     break;
24467                 }
24468                 parent = parent.parentElement;
24469             }
24470             return parent;
24471         }
24472         
24473         // is ancestor a text element.
24474         var ac =  range.commonAncestorContainer;
24475         if (ac.nodeType == 3) {
24476             ac = ac.parentNode;
24477         }
24478         
24479         var ar = ac.childNodes;
24480          
24481         var nodes = [];
24482         var other_nodes = [];
24483         var has_other_nodes = false;
24484         for (var i=0;i<ar.length;i++) {
24485             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
24486                 continue;
24487             }
24488             // fullly contained node.
24489             
24490             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
24491                 nodes.push(ar[i]);
24492                 continue;
24493             }
24494             
24495             // probably selected..
24496             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
24497                 other_nodes.push(ar[i]);
24498                 continue;
24499             }
24500             // outer..
24501             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
24502                 continue;
24503             }
24504             
24505             
24506             has_other_nodes = true;
24507         }
24508         if (!nodes.length && other_nodes.length) {
24509             nodes= other_nodes;
24510         }
24511         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
24512             return false;
24513         }
24514         
24515         return nodes[0];
24516     },
24517     createRange: function(sel)
24518     {
24519         // this has strange effects when using with 
24520         // top toolbar - not sure if it's a great idea.
24521         //this.editor.contentWindow.focus();
24522         if (typeof sel != "undefined") {
24523             try {
24524                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
24525             } catch(e) {
24526                 return this.doc.createRange();
24527             }
24528         } else {
24529             return this.doc.createRange();
24530         }
24531     },
24532     getParentElement: function()
24533     {
24534         
24535         this.assignDocWin();
24536         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
24537         
24538         var range = this.createRange(sel);
24539          
24540         try {
24541             var p = range.commonAncestorContainer;
24542             while (p.nodeType == 3) { // text node
24543                 p = p.parentNode;
24544             }
24545             return p;
24546         } catch (e) {
24547             return null;
24548         }
24549     
24550     },
24551     /***
24552      *
24553      * Range intersection.. the hard stuff...
24554      *  '-1' = before
24555      *  '0' = hits..
24556      *  '1' = after.
24557      *         [ -- selected range --- ]
24558      *   [fail]                        [fail]
24559      *
24560      *    basically..
24561      *      if end is before start or  hits it. fail.
24562      *      if start is after end or hits it fail.
24563      *
24564      *   if either hits (but other is outside. - then it's not 
24565      *   
24566      *    
24567      **/
24568     
24569     
24570     // @see http://www.thismuchiknow.co.uk/?p=64.
24571     rangeIntersectsNode : function(range, node)
24572     {
24573         var nodeRange = node.ownerDocument.createRange();
24574         try {
24575             nodeRange.selectNode(node);
24576         } catch (e) {
24577             nodeRange.selectNodeContents(node);
24578         }
24579     
24580         var rangeStartRange = range.cloneRange();
24581         rangeStartRange.collapse(true);
24582     
24583         var rangeEndRange = range.cloneRange();
24584         rangeEndRange.collapse(false);
24585     
24586         var nodeStartRange = nodeRange.cloneRange();
24587         nodeStartRange.collapse(true);
24588     
24589         var nodeEndRange = nodeRange.cloneRange();
24590         nodeEndRange.collapse(false);
24591     
24592         return rangeStartRange.compareBoundaryPoints(
24593                  Range.START_TO_START, nodeEndRange) == -1 &&
24594                rangeEndRange.compareBoundaryPoints(
24595                  Range.START_TO_START, nodeStartRange) == 1;
24596         
24597          
24598     },
24599     rangeCompareNode : function(range, node)
24600     {
24601         var nodeRange = node.ownerDocument.createRange();
24602         try {
24603             nodeRange.selectNode(node);
24604         } catch (e) {
24605             nodeRange.selectNodeContents(node);
24606         }
24607         
24608         
24609         range.collapse(true);
24610     
24611         nodeRange.collapse(true);
24612      
24613         var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
24614         var ee = range.compareBoundaryPoints(  Range.END_TO_END, nodeRange);
24615          
24616         //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
24617         
24618         var nodeIsBefore   =  ss == 1;
24619         var nodeIsAfter    = ee == -1;
24620         
24621         if (nodeIsBefore && nodeIsAfter) {
24622             return 0; // outer
24623         }
24624         if (!nodeIsBefore && nodeIsAfter) {
24625             return 1; //right trailed.
24626         }
24627         
24628         if (nodeIsBefore && !nodeIsAfter) {
24629             return 2;  // left trailed.
24630         }
24631         // fully contined.
24632         return 3;
24633     },
24634
24635     // private? - in a new class?
24636     cleanUpPaste :  function()
24637     {
24638         // cleans up the whole document..
24639         Roo.log('cleanuppaste');
24640         
24641         this.cleanUpChildren(this.doc.body);
24642         var clean = this.cleanWordChars(this.doc.body.innerHTML);
24643         if (clean != this.doc.body.innerHTML) {
24644             this.doc.body.innerHTML = clean;
24645         }
24646         
24647     },
24648     
24649     cleanWordChars : function(input) {// change the chars to hex code
24650         var he = Roo.HtmlEditorCore;
24651         
24652         var output = input;
24653         Roo.each(he.swapCodes, function(sw) { 
24654             var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
24655             
24656             output = output.replace(swapper, sw[1]);
24657         });
24658         
24659         return output;
24660     },
24661     
24662     
24663     cleanUpChildren : function (n)
24664     {
24665         if (!n.childNodes.length) {
24666             return;
24667         }
24668         for (var i = n.childNodes.length-1; i > -1 ; i--) {
24669            this.cleanUpChild(n.childNodes[i]);
24670         }
24671     },
24672     
24673     
24674         
24675     
24676     cleanUpChild : function (node)
24677     {
24678         var ed = this;
24679         //console.log(node);
24680         if (node.nodeName == "#text") {
24681             // clean up silly Windows -- stuff?
24682             return; 
24683         }
24684         if (node.nodeName == "#comment") {
24685             node.parentNode.removeChild(node);
24686             // clean up silly Windows -- stuff?
24687             return; 
24688         }
24689         var lcname = node.tagName.toLowerCase();
24690         // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
24691         // whitelist of tags..
24692         
24693         if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
24694             // remove node.
24695             node.parentNode.removeChild(node);
24696             return;
24697             
24698         }
24699         
24700         var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
24701         
24702         // spans with no attributes - just remove them..
24703         if ((!node.attributes || !node.attributes.length) && lcname == 'span') { 
24704             remove_keep_children = true;
24705         }
24706         
24707         // remove <a name=....> as rendering on yahoo mailer is borked with this.
24708         // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
24709         
24710         //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
24711         //    remove_keep_children = true;
24712         //}
24713         
24714         if (remove_keep_children) {
24715             this.cleanUpChildren(node);
24716             // inserts everything just before this node...
24717             while (node.childNodes.length) {
24718                 var cn = node.childNodes[0];
24719                 node.removeChild(cn);
24720                 node.parentNode.insertBefore(cn, node);
24721             }
24722             node.parentNode.removeChild(node);
24723             return;
24724         }
24725         
24726         if (!node.attributes || !node.attributes.length) {
24727             
24728           
24729             
24730             
24731             this.cleanUpChildren(node);
24732             return;
24733         }
24734         
24735         function cleanAttr(n,v)
24736         {
24737             
24738             if (v.match(/^\./) || v.match(/^\//)) {
24739                 return;
24740             }
24741             if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/) || v.match(/^ftp:/)) {
24742                 return;
24743             }
24744             if (v.match(/^#/)) {
24745                 return;
24746             }
24747             if (v.match(/^\{/)) { // allow template editing.
24748                 return;
24749             }
24750 //            Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
24751             node.removeAttribute(n);
24752             
24753         }
24754         
24755         var cwhite = this.cwhite;
24756         var cblack = this.cblack;
24757             
24758         function cleanStyle(n,v)
24759         {
24760             if (v.match(/expression/)) { //XSS?? should we even bother..
24761                 node.removeAttribute(n);
24762                 return;
24763             }
24764             
24765             var parts = v.split(/;/);
24766             var clean = [];
24767             
24768             Roo.each(parts, function(p) {
24769                 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
24770                 if (!p.length) {
24771                     return true;
24772                 }
24773                 var l = p.split(':').shift().replace(/\s+/g,'');
24774                 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
24775                 
24776                 if ( cwhite.length && cblack.indexOf(l) > -1) {
24777 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
24778                     //node.removeAttribute(n);
24779                     return true;
24780                 }
24781                 //Roo.log()
24782                 // only allow 'c whitelisted system attributes'
24783                 if ( cwhite.length &&  cwhite.indexOf(l) < 0) {
24784 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
24785                     //node.removeAttribute(n);
24786                     return true;
24787                 }
24788                 
24789                 
24790                  
24791                 
24792                 clean.push(p);
24793                 return true;
24794             });
24795             if (clean.length) { 
24796                 node.setAttribute(n, clean.join(';'));
24797             } else {
24798                 node.removeAttribute(n);
24799             }
24800             
24801         }
24802         
24803         
24804         for (var i = node.attributes.length-1; i > -1 ; i--) {
24805             var a = node.attributes[i];
24806             //console.log(a);
24807             
24808             if (a.name.toLowerCase().substr(0,2)=='on')  {
24809                 node.removeAttribute(a.name);
24810                 continue;
24811             }
24812             if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
24813                 node.removeAttribute(a.name);
24814                 continue;
24815             }
24816             if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
24817                 cleanAttr(a.name,a.value); // fixme..
24818                 continue;
24819             }
24820             if (a.name == 'style') {
24821                 cleanStyle(a.name,a.value);
24822                 continue;
24823             }
24824             /// clean up MS crap..
24825             // tecnically this should be a list of valid class'es..
24826             
24827             
24828             if (a.name == 'class') {
24829                 if (a.value.match(/^Mso/)) {
24830                     node.removeAttribute('class');
24831                 }
24832                 
24833                 if (a.value.match(/^body$/)) {
24834                     node.removeAttribute('class');
24835                 }
24836                 continue;
24837             }
24838             
24839             // style cleanup!?
24840             // class cleanup?
24841             
24842         }
24843         
24844         
24845         this.cleanUpChildren(node);
24846         
24847         
24848     },
24849     
24850     /**
24851      * Clean up MS wordisms...
24852      */
24853     cleanWord : function(node)
24854     {
24855         if (!node) {
24856             this.cleanWord(this.doc.body);
24857             return;
24858         }
24859         
24860         if(
24861                 node.nodeName == 'SPAN' &&
24862                 !node.hasAttributes() &&
24863                 node.childNodes.length == 1 &&
24864                 node.firstChild.nodeName == "#text"  
24865         ) {
24866             var textNode = node.firstChild;
24867             node.removeChild(textNode);
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.insertBefore(textNode, node);
24872             if (node.getAttribute('lang') != 'zh-CN') {   // do not space pad on chinese characters..
24873                 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" ") , node);
24874             }
24875             node.parentNode.removeChild(node);
24876         }
24877         
24878         if (node.nodeName == "#text") {
24879             // clean up silly Windows -- stuff?
24880             return; 
24881         }
24882         if (node.nodeName == "#comment") {
24883             node.parentNode.removeChild(node);
24884             // clean up silly Windows -- stuff?
24885             return; 
24886         }
24887         
24888         if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
24889             node.parentNode.removeChild(node);
24890             return;
24891         }
24892         //Roo.log(node.tagName);
24893         // remove - but keep children..
24894         if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|v:|font)/)) {
24895             //Roo.log('-- removed');
24896             while (node.childNodes.length) {
24897                 var cn = node.childNodes[0];
24898                 node.removeChild(cn);
24899                 node.parentNode.insertBefore(cn, node);
24900                 // move node to parent - and clean it..
24901                 this.cleanWord(cn);
24902             }
24903             node.parentNode.removeChild(node);
24904             /// no need to iterate chidlren = it's got none..
24905             //this.iterateChildren(node, this.cleanWord);
24906             return;
24907         }
24908         // clean styles
24909         if (node.className.length) {
24910             
24911             var cn = node.className.split(/\W+/);
24912             var cna = [];
24913             Roo.each(cn, function(cls) {
24914                 if (cls.match(/Mso[a-zA-Z]+/)) {
24915                     return;
24916                 }
24917                 cna.push(cls);
24918             });
24919             node.className = cna.length ? cna.join(' ') : '';
24920             if (!cna.length) {
24921                 node.removeAttribute("class");
24922             }
24923         }
24924         
24925         if (node.hasAttribute("lang")) {
24926             node.removeAttribute("lang");
24927         }
24928         
24929         if (node.hasAttribute("style")) {
24930             
24931             var styles = node.getAttribute("style").split(";");
24932             var nstyle = [];
24933             Roo.each(styles, function(s) {
24934                 if (!s.match(/:/)) {
24935                     return;
24936                 }
24937                 var kv = s.split(":");
24938                 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
24939                     return;
24940                 }
24941                 // what ever is left... we allow.
24942                 nstyle.push(s);
24943             });
24944             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
24945             if (!nstyle.length) {
24946                 node.removeAttribute('style');
24947             }
24948         }
24949         this.iterateChildren(node, this.cleanWord);
24950         
24951         
24952         
24953     },
24954     /**
24955      * iterateChildren of a Node, calling fn each time, using this as the scole..
24956      * @param {DomNode} node node to iterate children of.
24957      * @param {Function} fn method of this class to call on each item.
24958      */
24959     iterateChildren : function(node, fn)
24960     {
24961         if (!node.childNodes.length) {
24962                 return;
24963         }
24964         for (var i = node.childNodes.length-1; i > -1 ; i--) {
24965            fn.call(this, node.childNodes[i])
24966         }
24967     },
24968     
24969     
24970     /**
24971      * cleanTableWidths.
24972      *
24973      * Quite often pasting from word etc.. results in tables with column and widths.
24974      * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
24975      *
24976      */
24977     cleanTableWidths : function(node)
24978     {
24979          
24980          
24981         if (!node) {
24982             this.cleanTableWidths(this.doc.body);
24983             return;
24984         }
24985         
24986         // ignore list...
24987         if (node.nodeName == "#text" || node.nodeName == "#comment") {
24988             return; 
24989         }
24990         Roo.log(node.tagName);
24991         if (!node.tagName.toLowerCase().match(/^(table|td|tr)$/)) {
24992             this.iterateChildren(node, this.cleanTableWidths);
24993             return;
24994         }
24995         if (node.hasAttribute('width')) {
24996             node.removeAttribute('width');
24997         }
24998         
24999          
25000         if (node.hasAttribute("style")) {
25001             // pretty basic...
25002             
25003             var styles = node.getAttribute("style").split(";");
25004             var nstyle = [];
25005             Roo.each(styles, function(s) {
25006                 if (!s.match(/:/)) {
25007                     return;
25008                 }
25009                 var kv = s.split(":");
25010                 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
25011                     return;
25012                 }
25013                 // what ever is left... we allow.
25014                 nstyle.push(s);
25015             });
25016             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
25017             if (!nstyle.length) {
25018                 node.removeAttribute('style');
25019             }
25020         }
25021         
25022         this.iterateChildren(node, this.cleanTableWidths);
25023         
25024         
25025     },
25026     
25027     
25028     
25029     
25030     domToHTML : function(currentElement, depth, nopadtext) {
25031         
25032         depth = depth || 0;
25033         nopadtext = nopadtext || false;
25034     
25035         if (!currentElement) {
25036             return this.domToHTML(this.doc.body);
25037         }
25038         
25039         //Roo.log(currentElement);
25040         var j;
25041         var allText = false;
25042         var nodeName = currentElement.nodeName;
25043         var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
25044         
25045         if  (nodeName == '#text') {
25046             
25047             return nopadtext ? currentElement.nodeValue : currentElement.nodeValue.trim();
25048         }
25049         
25050         
25051         var ret = '';
25052         if (nodeName != 'BODY') {
25053              
25054             var i = 0;
25055             // Prints the node tagName, such as <A>, <IMG>, etc
25056             if (tagName) {
25057                 var attr = [];
25058                 for(i = 0; i < currentElement.attributes.length;i++) {
25059                     // quoting?
25060                     var aname = currentElement.attributes.item(i).name;
25061                     if (!currentElement.attributes.item(i).value.length) {
25062                         continue;
25063                     }
25064                     attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
25065                 }
25066                 
25067                 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
25068             } 
25069             else {
25070                 
25071                 // eack
25072             }
25073         } else {
25074             tagName = false;
25075         }
25076         if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
25077             return ret;
25078         }
25079         if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
25080             nopadtext = true;
25081         }
25082         
25083         
25084         // Traverse the tree
25085         i = 0;
25086         var currentElementChild = currentElement.childNodes.item(i);
25087         var allText = true;
25088         var innerHTML  = '';
25089         lastnode = '';
25090         while (currentElementChild) {
25091             // Formatting code (indent the tree so it looks nice on the screen)
25092             var nopad = nopadtext;
25093             if (lastnode == 'SPAN') {
25094                 nopad  = true;
25095             }
25096             // text
25097             if  (currentElementChild.nodeName == '#text') {
25098                 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
25099                 toadd = nopadtext ? toadd : toadd.trim();
25100                 if (!nopad && toadd.length > 80) {
25101                     innerHTML  += "\n" + (new Array( depth + 1 )).join( "  "  );
25102                 }
25103                 innerHTML  += toadd;
25104                 
25105                 i++;
25106                 currentElementChild = currentElement.childNodes.item(i);
25107                 lastNode = '';
25108                 continue;
25109             }
25110             allText = false;
25111             
25112             innerHTML  += nopad ? '' : "\n" + (new Array( depth + 1 )).join( "  "  );
25113                 
25114             // Recursively traverse the tree structure of the child node
25115             innerHTML   += this.domToHTML(currentElementChild, depth+1, nopadtext);
25116             lastnode = currentElementChild.nodeName;
25117             i++;
25118             currentElementChild=currentElement.childNodes.item(i);
25119         }
25120         
25121         ret += innerHTML;
25122         
25123         if (!allText) {
25124                 // The remaining code is mostly for formatting the tree
25125             ret+= nopadtext ? '' : "\n" + (new Array( depth  )).join( "  "  );
25126         }
25127         
25128         
25129         if (tagName) {
25130             ret+= "</"+tagName+">";
25131         }
25132         return ret;
25133         
25134     },
25135         
25136     applyBlacklists : function()
25137     {
25138         var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white  : [];
25139         var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black :  [];
25140         
25141         this.white = [];
25142         this.black = [];
25143         Roo.each(Roo.HtmlEditorCore.white, function(tag) {
25144             if (b.indexOf(tag) > -1) {
25145                 return;
25146             }
25147             this.white.push(tag);
25148             
25149         }, this);
25150         
25151         Roo.each(w, function(tag) {
25152             if (b.indexOf(tag) > -1) {
25153                 return;
25154             }
25155             if (this.white.indexOf(tag) > -1) {
25156                 return;
25157             }
25158             this.white.push(tag);
25159             
25160         }, this);
25161         
25162         
25163         Roo.each(Roo.HtmlEditorCore.black, function(tag) {
25164             if (w.indexOf(tag) > -1) {
25165                 return;
25166             }
25167             this.black.push(tag);
25168             
25169         }, this);
25170         
25171         Roo.each(b, function(tag) {
25172             if (w.indexOf(tag) > -1) {
25173                 return;
25174             }
25175             if (this.black.indexOf(tag) > -1) {
25176                 return;
25177             }
25178             this.black.push(tag);
25179             
25180         }, this);
25181         
25182         
25183         w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite  : [];
25184         b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack :  [];
25185         
25186         this.cwhite = [];
25187         this.cblack = [];
25188         Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
25189             if (b.indexOf(tag) > -1) {
25190                 return;
25191             }
25192             this.cwhite.push(tag);
25193             
25194         }, this);
25195         
25196         Roo.each(w, function(tag) {
25197             if (b.indexOf(tag) > -1) {
25198                 return;
25199             }
25200             if (this.cwhite.indexOf(tag) > -1) {
25201                 return;
25202             }
25203             this.cwhite.push(tag);
25204             
25205         }, this);
25206         
25207         
25208         Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
25209             if (w.indexOf(tag) > -1) {
25210                 return;
25211             }
25212             this.cblack.push(tag);
25213             
25214         }, this);
25215         
25216         Roo.each(b, function(tag) {
25217             if (w.indexOf(tag) > -1) {
25218                 return;
25219             }
25220             if (this.cblack.indexOf(tag) > -1) {
25221                 return;
25222             }
25223             this.cblack.push(tag);
25224             
25225         }, this);
25226     },
25227     
25228     setStylesheets : function(stylesheets)
25229     {
25230         if(typeof(stylesheets) == 'string'){
25231             Roo.get(this.iframe.contentDocument.head).createChild({
25232                 tag : 'link',
25233                 rel : 'stylesheet',
25234                 type : 'text/css',
25235                 href : stylesheets
25236             });
25237             
25238             return;
25239         }
25240         var _this = this;
25241      
25242         Roo.each(stylesheets, function(s) {
25243             if(!s.length){
25244                 return;
25245             }
25246             
25247             Roo.get(_this.iframe.contentDocument.head).createChild({
25248                 tag : 'link',
25249                 rel : 'stylesheet',
25250                 type : 'text/css',
25251                 href : s
25252             });
25253         });
25254
25255         
25256     },
25257     
25258     removeStylesheets : function()
25259     {
25260         var _this = this;
25261         
25262         Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
25263             s.remove();
25264         });
25265     },
25266     
25267     setStyle : function(style)
25268     {
25269         Roo.get(this.iframe.contentDocument.head).createChild({
25270             tag : 'style',
25271             type : 'text/css',
25272             html : style
25273         });
25274
25275         return;
25276     }
25277     
25278     // hide stuff that is not compatible
25279     /**
25280      * @event blur
25281      * @hide
25282      */
25283     /**
25284      * @event change
25285      * @hide
25286      */
25287     /**
25288      * @event focus
25289      * @hide
25290      */
25291     /**
25292      * @event specialkey
25293      * @hide
25294      */
25295     /**
25296      * @cfg {String} fieldClass @hide
25297      */
25298     /**
25299      * @cfg {String} focusClass @hide
25300      */
25301     /**
25302      * @cfg {String} autoCreate @hide
25303      */
25304     /**
25305      * @cfg {String} inputType @hide
25306      */
25307     /**
25308      * @cfg {String} invalidClass @hide
25309      */
25310     /**
25311      * @cfg {String} invalidText @hide
25312      */
25313     /**
25314      * @cfg {String} msgFx @hide
25315      */
25316     /**
25317      * @cfg {String} validateOnBlur @hide
25318      */
25319 });
25320
25321 Roo.HtmlEditorCore.white = [
25322         'area', 'br', 'img', 'input', 'hr', 'wbr',
25323         
25324        'address', 'blockquote', 'center', 'dd',      'dir',       'div', 
25325        'dl',      'dt',         'h1',     'h2',      'h3',        'h4', 
25326        'h5',      'h6',         'hr',     'isindex', 'listing',   'marquee', 
25327        'menu',    'multicol',   'ol',     'p',       'plaintext', 'pre', 
25328        'table',   'ul',         'xmp', 
25329        
25330        'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th', 
25331       'thead',   'tr', 
25332      
25333       'dir', 'menu', 'ol', 'ul', 'dl',
25334        
25335       'embed',  'object'
25336 ];
25337
25338
25339 Roo.HtmlEditorCore.black = [
25340     //    'embed',  'object', // enable - backend responsiblity to clean thiese
25341         'applet', // 
25342         'base',   'basefont', 'bgsound', 'blink',  'body', 
25343         'frame',  'frameset', 'head',    'html',   'ilayer', 
25344         'iframe', 'layer',  'link',     'meta',    'object',   
25345         'script', 'style' ,'title',  'xml' // clean later..
25346 ];
25347 Roo.HtmlEditorCore.clean = [
25348     'script', 'style', 'title', 'xml'
25349 ];
25350 Roo.HtmlEditorCore.remove = [
25351     'font'
25352 ];
25353 // attributes..
25354
25355 Roo.HtmlEditorCore.ablack = [
25356     'on'
25357 ];
25358     
25359 Roo.HtmlEditorCore.aclean = [ 
25360     'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc' 
25361 ];
25362
25363 // protocols..
25364 Roo.HtmlEditorCore.pwhite= [
25365         'http',  'https',  'mailto'
25366 ];
25367
25368 // white listed style attributes.
25369 Roo.HtmlEditorCore.cwhite= [
25370       //  'text-align', /// default is to allow most things..
25371       
25372          
25373 //        'font-size'//??
25374 ];
25375
25376 // black listed style attributes.
25377 Roo.HtmlEditorCore.cblack= [
25378       //  'font-size' -- this can be set by the project 
25379 ];
25380
25381
25382 Roo.HtmlEditorCore.swapCodes   =[ 
25383     [    8211, "--" ], 
25384     [    8212, "--" ], 
25385     [    8216,  "'" ],  
25386     [    8217, "'" ],  
25387     [    8220, '"' ],  
25388     [    8221, '"' ],  
25389     [    8226, "*" ],  
25390     [    8230, "..." ]
25391 ]; 
25392
25393     /*
25394  * - LGPL
25395  *
25396  * HtmlEditor
25397  * 
25398  */
25399
25400 /**
25401  * @class Roo.bootstrap.HtmlEditor
25402  * @extends Roo.bootstrap.TextArea
25403  * Bootstrap HtmlEditor class
25404
25405  * @constructor
25406  * Create a new HtmlEditor
25407  * @param {Object} config The config object
25408  */
25409
25410 Roo.bootstrap.HtmlEditor = function(config){
25411     Roo.bootstrap.HtmlEditor.superclass.constructor.call(this, config);
25412     if (!this.toolbars) {
25413         this.toolbars = [];
25414     }
25415     
25416     this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
25417     this.addEvents({
25418             /**
25419              * @event initialize
25420              * Fires when the editor is fully initialized (including the iframe)
25421              * @param {HtmlEditor} this
25422              */
25423             initialize: true,
25424             /**
25425              * @event activate
25426              * Fires when the editor is first receives the focus. Any insertion must wait
25427              * until after this event.
25428              * @param {HtmlEditor} this
25429              */
25430             activate: true,
25431              /**
25432              * @event beforesync
25433              * Fires before the textarea is updated with content from the editor iframe. Return false
25434              * to cancel the sync.
25435              * @param {HtmlEditor} this
25436              * @param {String} html
25437              */
25438             beforesync: true,
25439              /**
25440              * @event beforepush
25441              * Fires before the iframe editor is updated with content from the textarea. Return false
25442              * to cancel the push.
25443              * @param {HtmlEditor} this
25444              * @param {String} html
25445              */
25446             beforepush: true,
25447              /**
25448              * @event sync
25449              * Fires when the textarea is updated with content from the editor iframe.
25450              * @param {HtmlEditor} this
25451              * @param {String} html
25452              */
25453             sync: true,
25454              /**
25455              * @event push
25456              * Fires when the iframe editor is updated with content from the textarea.
25457              * @param {HtmlEditor} this
25458              * @param {String} html
25459              */
25460             push: true,
25461              /**
25462              * @event editmodechange
25463              * Fires when the editor switches edit modes
25464              * @param {HtmlEditor} this
25465              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
25466              */
25467             editmodechange: true,
25468             /**
25469              * @event editorevent
25470              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
25471              * @param {HtmlEditor} this
25472              */
25473             editorevent: true,
25474             /**
25475              * @event firstfocus
25476              * Fires when on first focus - needed by toolbars..
25477              * @param {HtmlEditor} this
25478              */
25479             firstfocus: true,
25480             /**
25481              * @event autosave
25482              * Auto save the htmlEditor value as a file into Events
25483              * @param {HtmlEditor} this
25484              */
25485             autosave: true,
25486             /**
25487              * @event savedpreview
25488              * preview the saved version of htmlEditor
25489              * @param {HtmlEditor} this
25490              */
25491             savedpreview: true
25492         });
25493 };
25494
25495
25496 Roo.extend(Roo.bootstrap.HtmlEditor, Roo.bootstrap.TextArea,  {
25497     
25498     
25499       /**
25500      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
25501      */
25502     toolbars : false,
25503     
25504      /**
25505     * @cfg {Array} buttons Array of toolbar's buttons. - defaults to empty
25506     */
25507     btns : [],
25508    
25509      /**
25510      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
25511      *                        Roo.resizable.
25512      */
25513     resizable : false,
25514      /**
25515      * @cfg {Number} height (in pixels)
25516      */   
25517     height: 300,
25518    /**
25519      * @cfg {Number} width (in pixels)
25520      */   
25521     width: false,
25522     
25523     /**
25524      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
25525      * 
25526      */
25527     stylesheets: false,
25528     
25529     // id of frame..
25530     frameId: false,
25531     
25532     // private properties
25533     validationEvent : false,
25534     deferHeight: true,
25535     initialized : false,
25536     activated : false,
25537     
25538     onFocus : Roo.emptyFn,
25539     iframePad:3,
25540     hideMode:'offsets',
25541     
25542     tbContainer : false,
25543     
25544     bodyCls : '',
25545     
25546     toolbarContainer :function() {
25547         return this.wrap.select('.x-html-editor-tb',true).first();
25548     },
25549
25550     /**
25551      * Protected method that will not generally be called directly. It
25552      * is called when the editor creates its toolbar. Override this method if you need to
25553      * add custom toolbar buttons.
25554      * @param {HtmlEditor} editor
25555      */
25556     createToolbar : function(){
25557         Roo.log('renewing');
25558         Roo.log("create toolbars");
25559         
25560         this.toolbars = [ new Roo.bootstrap.htmleditor.ToolbarStandard({editor: this} ) ];
25561         this.toolbars[0].render(this.toolbarContainer());
25562         
25563         return;
25564         
25565 //        if (!editor.toolbars || !editor.toolbars.length) {
25566 //            editor.toolbars = [ new Roo.bootstrap.HtmlEditor.ToolbarStandard() ]; // can be empty?
25567 //        }
25568 //        
25569 //        for (var i =0 ; i < editor.toolbars.length;i++) {
25570 //            editor.toolbars[i] = Roo.factory(
25571 //                    typeof(editor.toolbars[i]) == 'string' ?
25572 //                        { xtype: editor.toolbars[i]} : editor.toolbars[i],
25573 //                Roo.bootstrap.HtmlEditor);
25574 //            editor.toolbars[i].init(editor);
25575 //        }
25576     },
25577
25578      
25579     // private
25580     onRender : function(ct, position)
25581     {
25582        // Roo.log("Call onRender: " + this.xtype);
25583         var _t = this;
25584         Roo.bootstrap.HtmlEditor.superclass.onRender.call(this, ct, position);
25585       
25586         this.wrap = this.inputEl().wrap({
25587             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
25588         });
25589         
25590         this.editorcore.onRender(ct, position);
25591          
25592         if (this.resizable) {
25593             this.resizeEl = new Roo.Resizable(this.wrap, {
25594                 pinned : true,
25595                 wrap: true,
25596                 dynamic : true,
25597                 minHeight : this.height,
25598                 height: this.height,
25599                 handles : this.resizable,
25600                 width: this.width,
25601                 listeners : {
25602                     resize : function(r, w, h) {
25603                         _t.onResize(w,h); // -something
25604                     }
25605                 }
25606             });
25607             
25608         }
25609         this.createToolbar(this);
25610        
25611         
25612         if(!this.width && this.resizable){
25613             this.setSize(this.wrap.getSize());
25614         }
25615         if (this.resizeEl) {
25616             this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
25617             // should trigger onReize..
25618         }
25619         
25620     },
25621
25622     // private
25623     onResize : function(w, h)
25624     {
25625         Roo.log('resize: ' +w + ',' + h );
25626         Roo.bootstrap.HtmlEditor.superclass.onResize.apply(this, arguments);
25627         var ew = false;
25628         var eh = false;
25629         
25630         if(this.inputEl() ){
25631             if(typeof w == 'number'){
25632                 var aw = w - this.wrap.getFrameWidth('lr');
25633                 this.inputEl().setWidth(this.adjustWidth('textarea', aw));
25634                 ew = aw;
25635             }
25636             if(typeof h == 'number'){
25637                  var tbh = -11;  // fixme it needs to tool bar size!
25638                 for (var i =0; i < this.toolbars.length;i++) {
25639                     // fixme - ask toolbars for heights?
25640                     tbh += this.toolbars[i].el.getHeight();
25641                     //if (this.toolbars[i].footer) {
25642                     //    tbh += this.toolbars[i].footer.el.getHeight();
25643                     //}
25644                 }
25645               
25646                 
25647                 
25648                 
25649                 
25650                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
25651                 ah -= 5; // knock a few pixes off for look..
25652                 this.inputEl().setHeight(this.adjustWidth('textarea', ah));
25653                 var eh = ah;
25654             }
25655         }
25656         Roo.log('onResize:' + [w,h,ew,eh].join(',') );
25657         this.editorcore.onResize(ew,eh);
25658         
25659     },
25660
25661     /**
25662      * Toggles the editor between standard and source edit mode.
25663      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
25664      */
25665     toggleSourceEdit : function(sourceEditMode)
25666     {
25667         this.editorcore.toggleSourceEdit(sourceEditMode);
25668         
25669         if(this.editorcore.sourceEditMode){
25670             Roo.log('editor - showing textarea');
25671             
25672 //            Roo.log('in');
25673 //            Roo.log(this.syncValue());
25674             this.syncValue();
25675             this.inputEl().removeClass(['hide', 'x-hidden']);
25676             this.inputEl().dom.removeAttribute('tabIndex');
25677             this.inputEl().focus();
25678         }else{
25679             Roo.log('editor - hiding textarea');
25680 //            Roo.log('out')
25681 //            Roo.log(this.pushValue()); 
25682             this.pushValue();
25683             
25684             this.inputEl().addClass(['hide', 'x-hidden']);
25685             this.inputEl().dom.setAttribute('tabIndex', -1);
25686             //this.deferFocus();
25687         }
25688          
25689         if(this.resizable){
25690             this.setSize(this.wrap.getSize());
25691         }
25692         
25693         this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
25694     },
25695  
25696     // private (for BoxComponent)
25697     adjustSize : Roo.BoxComponent.prototype.adjustSize,
25698
25699     // private (for BoxComponent)
25700     getResizeEl : function(){
25701         return this.wrap;
25702     },
25703
25704     // private (for BoxComponent)
25705     getPositionEl : function(){
25706         return this.wrap;
25707     },
25708
25709     // private
25710     initEvents : function(){
25711         this.originalValue = this.getValue();
25712     },
25713
25714 //    /**
25715 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
25716 //     * @method
25717 //     */
25718 //    markInvalid : Roo.emptyFn,
25719 //    /**
25720 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
25721 //     * @method
25722 //     */
25723 //    clearInvalid : Roo.emptyFn,
25724
25725     setValue : function(v){
25726         Roo.bootstrap.HtmlEditor.superclass.setValue.call(this, v);
25727         this.editorcore.pushValue();
25728     },
25729
25730      
25731     // private
25732     deferFocus : function(){
25733         this.focus.defer(10, this);
25734     },
25735
25736     // doc'ed in Field
25737     focus : function(){
25738         this.editorcore.focus();
25739         
25740     },
25741       
25742
25743     // private
25744     onDestroy : function(){
25745         
25746         
25747         
25748         if(this.rendered){
25749             
25750             for (var i =0; i < this.toolbars.length;i++) {
25751                 // fixme - ask toolbars for heights?
25752                 this.toolbars[i].onDestroy();
25753             }
25754             
25755             this.wrap.dom.innerHTML = '';
25756             this.wrap.remove();
25757         }
25758     },
25759
25760     // private
25761     onFirstFocus : function(){
25762         //Roo.log("onFirstFocus");
25763         this.editorcore.onFirstFocus();
25764          for (var i =0; i < this.toolbars.length;i++) {
25765             this.toolbars[i].onFirstFocus();
25766         }
25767         
25768     },
25769     
25770     // private
25771     syncValue : function()
25772     {   
25773         this.editorcore.syncValue();
25774     },
25775     
25776     pushValue : function()
25777     {   
25778         this.editorcore.pushValue();
25779     }
25780      
25781     
25782     // hide stuff that is not compatible
25783     /**
25784      * @event blur
25785      * @hide
25786      */
25787     /**
25788      * @event change
25789      * @hide
25790      */
25791     /**
25792      * @event focus
25793      * @hide
25794      */
25795     /**
25796      * @event specialkey
25797      * @hide
25798      */
25799     /**
25800      * @cfg {String} fieldClass @hide
25801      */
25802     /**
25803      * @cfg {String} focusClass @hide
25804      */
25805     /**
25806      * @cfg {String} autoCreate @hide
25807      */
25808     /**
25809      * @cfg {String} inputType @hide
25810      */
25811      
25812     /**
25813      * @cfg {String} invalidText @hide
25814      */
25815     /**
25816      * @cfg {String} msgFx @hide
25817      */
25818     /**
25819      * @cfg {String} validateOnBlur @hide
25820      */
25821 });
25822  
25823     
25824    
25825    
25826    
25827       
25828 Roo.namespace('Roo.bootstrap.htmleditor');
25829 /**
25830  * @class Roo.bootstrap.HtmlEditorToolbar1
25831  * Basic Toolbar
25832  * 
25833  * @example
25834  * Usage:
25835  *
25836  new Roo.bootstrap.HtmlEditor({
25837     ....
25838     toolbars : [
25839         new Roo.bootstrap.HtmlEditorToolbar1({
25840             disable : { fonts: 1 , format: 1, ..., ... , ...],
25841             btns : [ .... ]
25842         })
25843     }
25844      
25845  * 
25846  * @cfg {Object} disable List of elements to disable..
25847  * @cfg {Array} btns List of additional buttons.
25848  * 
25849  * 
25850  * NEEDS Extra CSS? 
25851  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
25852  */
25853  
25854 Roo.bootstrap.htmleditor.ToolbarStandard = function(config)
25855 {
25856     
25857     Roo.apply(this, config);
25858     
25859     // default disabled, based on 'good practice'..
25860     this.disable = this.disable || {};
25861     Roo.applyIf(this.disable, {
25862         fontSize : true,
25863         colors : true,
25864         specialElements : true
25865     });
25866     Roo.bootstrap.htmleditor.ToolbarStandard.superclass.constructor.call(this, config);
25867     
25868     this.editor = config.editor;
25869     this.editorcore = config.editor.editorcore;
25870     
25871     this.buttons   = new Roo.util.MixedCollection(false, function(o) { return o.cmd; });
25872     
25873     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
25874     // dont call parent... till later.
25875 }
25876 Roo.extend(Roo.bootstrap.htmleditor.ToolbarStandard, Roo.bootstrap.NavSimplebar,  {
25877      
25878     bar : true,
25879     
25880     editor : false,
25881     editorcore : false,
25882     
25883     
25884     formats : [
25885         "p" ,  
25886         "h1","h2","h3","h4","h5","h6", 
25887         "pre", "code", 
25888         "abbr", "acronym", "address", "cite", "samp", "var",
25889         'div','span'
25890     ],
25891     
25892     onRender : function(ct, position)
25893     {
25894        // Roo.log("Call onRender: " + this.xtype);
25895         
25896        Roo.bootstrap.htmleditor.ToolbarStandard.superclass.onRender.call(this, ct, position);
25897        Roo.log(this.el);
25898        this.el.dom.style.marginBottom = '0';
25899        var _this = this;
25900        var editorcore = this.editorcore;
25901        var editor= this.editor;
25902        
25903        var children = [];
25904        var btn = function(id,cmd , toggle, handler, html){
25905        
25906             var  event = toggle ? 'toggle' : 'click';
25907        
25908             var a = {
25909                 size : 'sm',
25910                 xtype: 'Button',
25911                 xns: Roo.bootstrap,
25912                 //glyphicon : id,
25913                 fa: id,
25914                 cmd : id || cmd,
25915                 enableToggle:toggle !== false,
25916                 html : html || '',
25917                 pressed : toggle ? false : null,
25918                 listeners : {}
25919             };
25920             a.listeners[toggle ? 'toggle' : 'click'] = function() {
25921                 handler ? handler.call(_this,this) :_this.onBtnClick.call(_this, cmd ||  id);
25922             };
25923             children.push(a);
25924             return a;
25925        }
25926        
25927     //    var cb_box = function...
25928         
25929         var style = {
25930                 xtype: 'Button',
25931                 size : 'sm',
25932                 xns: Roo.bootstrap,
25933                 fa : 'font',
25934                 //html : 'submit'
25935                 menu : {
25936                     xtype: 'Menu',
25937                     xns: Roo.bootstrap,
25938                     items:  []
25939                 }
25940         };
25941         Roo.each(this.formats, function(f) {
25942             style.menu.items.push({
25943                 xtype :'MenuItem',
25944                 xns: Roo.bootstrap,
25945                 html : '<'+ f+' style="margin:2px">'+f +'</'+ f+'>',
25946                 tagname : f,
25947                 listeners : {
25948                     click : function()
25949                     {
25950                         editorcore.insertTag(this.tagname);
25951                         editor.focus();
25952                     }
25953                 }
25954                 
25955             });
25956         });
25957         children.push(style);   
25958         
25959         btn('bold',false,true);
25960         btn('italic',false,true);
25961         btn('align-left', 'justifyleft',true);
25962         btn('align-center', 'justifycenter',true);
25963         btn('align-right' , 'justifyright',true);
25964         btn('link', false, false, function(btn) {
25965             //Roo.log("create link?");
25966             var url = prompt(this.createLinkText, this.defaultLinkValue);
25967             if(url && url != 'http:/'+'/'){
25968                 this.editorcore.relayCmd('createlink', url);
25969             }
25970         }),
25971         btn('list','insertunorderedlist',true);
25972         btn('pencil', false,true, function(btn){
25973                 Roo.log(this);
25974                 this.toggleSourceEdit(btn.pressed);
25975         });
25976         
25977         if (this.editor.btns.length > 0) {
25978             for (var i = 0; i<this.editor.btns.length; i++) {
25979                 children.push(this.editor.btns[i]);
25980             }
25981         }
25982         
25983         /*
25984         var cog = {
25985                 xtype: 'Button',
25986                 size : 'sm',
25987                 xns: Roo.bootstrap,
25988                 glyphicon : 'cog',
25989                 //html : 'submit'
25990                 menu : {
25991                     xtype: 'Menu',
25992                     xns: Roo.bootstrap,
25993                     items:  []
25994                 }
25995         };
25996         
25997         cog.menu.items.push({
25998             xtype :'MenuItem',
25999             xns: Roo.bootstrap,
26000             html : Clean styles,
26001             tagname : f,
26002             listeners : {
26003                 click : function()
26004                 {
26005                     editorcore.insertTag(this.tagname);
26006                     editor.focus();
26007                 }
26008             }
26009             
26010         });
26011        */
26012         
26013          
26014        this.xtype = 'NavSimplebar';
26015         
26016         for(var i=0;i< children.length;i++) {
26017             
26018             this.buttons.add(this.addxtypeChild(children[i]));
26019             
26020         }
26021         
26022         editor.on('editorevent', this.updateToolbar, this);
26023     },
26024     onBtnClick : function(id)
26025     {
26026        this.editorcore.relayCmd(id);
26027        this.editorcore.focus();
26028     },
26029     
26030     /**
26031      * Protected method that will not generally be called directly. It triggers
26032      * a toolbar update by reading the markup state of the current selection in the editor.
26033      */
26034     updateToolbar: function(){
26035
26036         if(!this.editorcore.activated){
26037             this.editor.onFirstFocus(); // is this neeed?
26038             return;
26039         }
26040
26041         var btns = this.buttons; 
26042         var doc = this.editorcore.doc;
26043         btns.get('bold').setActive(doc.queryCommandState('bold'));
26044         btns.get('italic').setActive(doc.queryCommandState('italic'));
26045         //btns.get('underline').setActive(doc.queryCommandState('underline'));
26046         
26047         btns.get('align-left').setActive(doc.queryCommandState('justifyleft'));
26048         btns.get('align-center').setActive(doc.queryCommandState('justifycenter'));
26049         btns.get('align-right').setActive(doc.queryCommandState('justifyright'));
26050         
26051         //btns[frameId + '-insertorderedlist').setActive(doc.queryCommandState('insertorderedlist'));
26052         btns.get('list').setActive(doc.queryCommandState('insertunorderedlist'));
26053          /*
26054         
26055         var ans = this.editorcore.getAllAncestors();
26056         if (this.formatCombo) {
26057             
26058             
26059             var store = this.formatCombo.store;
26060             this.formatCombo.setValue("");
26061             for (var i =0; i < ans.length;i++) {
26062                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
26063                     // select it..
26064                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
26065                     break;
26066                 }
26067             }
26068         }
26069         
26070         
26071         
26072         // hides menus... - so this cant be on a menu...
26073         Roo.bootstrap.MenuMgr.hideAll();
26074         */
26075         Roo.bootstrap.MenuMgr.hideAll();
26076         //this.editorsyncValue();
26077     },
26078     onFirstFocus: function() {
26079         this.buttons.each(function(item){
26080            item.enable();
26081         });
26082     },
26083     toggleSourceEdit : function(sourceEditMode){
26084         
26085           
26086         if(sourceEditMode){
26087             Roo.log("disabling buttons");
26088            this.buttons.each( function(item){
26089                 if(item.cmd != 'pencil'){
26090                     item.disable();
26091                 }
26092             });
26093           
26094         }else{
26095             Roo.log("enabling buttons");
26096             if(this.editorcore.initialized){
26097                 this.buttons.each( function(item){
26098                     item.enable();
26099                 });
26100             }
26101             
26102         }
26103         Roo.log("calling toggole on editor");
26104         // tell the editor that it's been pressed..
26105         this.editor.toggleSourceEdit(sourceEditMode);
26106        
26107     }
26108 });
26109
26110
26111
26112
26113  
26114 /*
26115  * - LGPL
26116  */
26117
26118 /**
26119  * @class Roo.bootstrap.Markdown
26120  * @extends Roo.bootstrap.TextArea
26121  * Bootstrap Showdown editable area
26122  * @cfg {string} content
26123  * 
26124  * @constructor
26125  * Create a new Showdown
26126  */
26127
26128 Roo.bootstrap.Markdown = function(config){
26129     Roo.bootstrap.Markdown.superclass.constructor.call(this, config);
26130    
26131 };
26132
26133 Roo.extend(Roo.bootstrap.Markdown, Roo.bootstrap.TextArea,  {
26134     
26135     editing :false,
26136     
26137     initEvents : function()
26138     {
26139         
26140         Roo.bootstrap.TextArea.prototype.initEvents.call(this);
26141         this.markdownEl = this.el.createChild({
26142             cls : 'roo-markdown-area'
26143         });
26144         this.inputEl().addClass('d-none');
26145         if (this.getValue() == '') {
26146             this.markdownEl.dom.innerHTML = String.format('<span class="roo-placeholder">{0}</span>', this.placeholder || '');
26147             
26148         } else {
26149             this.markdownEl.dom.innerHTML = Roo.Markdown.toHtml(Roo.util.Format.htmlEncode(this.getValue()));
26150         }
26151         this.markdownEl.on('click', this.toggleTextEdit, this);
26152         this.on('blur', this.toggleTextEdit, this);
26153         this.on('specialkey', this.resizeTextArea, this);
26154     },
26155     
26156     toggleTextEdit : function()
26157     {
26158         var sh = this.markdownEl.getHeight();
26159         this.inputEl().addClass('d-none');
26160         this.markdownEl.addClass('d-none');
26161         if (!this.editing) {
26162             // show editor?
26163             this.inputEl().setHeight(Math.min(500, Math.max(sh,(this.getValue().split("\n").length+1) * 30)));
26164             this.inputEl().removeClass('d-none');
26165             this.inputEl().focus();
26166             this.editing = true;
26167             return;
26168         }
26169         // show showdown...
26170         this.updateMarkdown();
26171         this.markdownEl.removeClass('d-none');
26172         this.editing = false;
26173         return;
26174     },
26175     updateMarkdown : function()
26176     {
26177         if (this.getValue() == '') {
26178             this.markdownEl.dom.innerHTML = String.format('<span class="roo-placeholder">{0}</span>', this.placeholder || '');
26179             return;
26180         }
26181  
26182         this.markdownEl.dom.innerHTML = Roo.Markdown.toHtml(Roo.util.Format.htmlEncode(this.getValue()));
26183     },
26184     
26185     resizeTextArea: function () {
26186         
26187         var sh = 100;
26188         Roo.log([sh, this.getValue().split("\n").length * 30]);
26189         this.inputEl().setHeight(Math.min(500, Math.max(sh, (this.getValue().split("\n").length +1) * 30)));
26190     },
26191     setValue : function(val)
26192     {
26193         Roo.bootstrap.TextArea.prototype.setValue.call(this,val);
26194         if (!this.editing) {
26195             this.updateMarkdown();
26196         }
26197         
26198     },
26199     focus : function()
26200     {
26201         if (!this.editing) {
26202             this.toggleTextEdit();
26203         }
26204         
26205     }
26206
26207
26208 });
26209 /**
26210  * @class Roo.bootstrap.Table.AbstractSelectionModel
26211  * @extends Roo.util.Observable
26212  * Abstract base class for grid SelectionModels.  It provides the interface that should be
26213  * implemented by descendant classes.  This class should not be directly instantiated.
26214  * @constructor
26215  */
26216 Roo.bootstrap.Table.AbstractSelectionModel = function(){
26217     this.locked = false;
26218     Roo.bootstrap.Table.AbstractSelectionModel.superclass.constructor.call(this);
26219 };
26220
26221
26222 Roo.extend(Roo.bootstrap.Table.AbstractSelectionModel, Roo.util.Observable,  {
26223     /** @ignore Called by the grid automatically. Do not call directly. */
26224     init : function(grid){
26225         this.grid = grid;
26226         this.initEvents();
26227     },
26228
26229     /**
26230      * Locks the selections.
26231      */
26232     lock : function(){
26233         this.locked = true;
26234     },
26235
26236     /**
26237      * Unlocks the selections.
26238      */
26239     unlock : function(){
26240         this.locked = false;
26241     },
26242
26243     /**
26244      * Returns true if the selections are locked.
26245      * @return {Boolean}
26246      */
26247     isLocked : function(){
26248         return this.locked;
26249     },
26250     
26251     
26252     initEvents : function ()
26253     {
26254         
26255     }
26256 });
26257 /**
26258  * @extends Roo.bootstrap.Table.AbstractSelectionModel
26259  * @class Roo.bootstrap.Table.RowSelectionModel
26260  * The default SelectionModel used by {@link Roo.bootstrap.Table}.
26261  * It supports multiple selections and keyboard selection/navigation. 
26262  * @constructor
26263  * @param {Object} config
26264  */
26265
26266 Roo.bootstrap.Table.RowSelectionModel = function(config){
26267     Roo.apply(this, config);
26268     this.selections = new Roo.util.MixedCollection(false, function(o){
26269         return o.id;
26270     });
26271
26272     this.last = false;
26273     this.lastActive = false;
26274
26275     this.addEvents({
26276         /**
26277              * @event selectionchange
26278              * Fires when the selection changes
26279              * @param {SelectionModel} this
26280              */
26281             "selectionchange" : true,
26282         /**
26283              * @event afterselectionchange
26284              * Fires after the selection changes (eg. by key press or clicking)
26285              * @param {SelectionModel} this
26286              */
26287             "afterselectionchange" : true,
26288         /**
26289              * @event beforerowselect
26290              * Fires when a row is selected being selected, return false to cancel.
26291              * @param {SelectionModel} this
26292              * @param {Number} rowIndex The selected index
26293              * @param {Boolean} keepExisting False if other selections will be cleared
26294              */
26295             "beforerowselect" : true,
26296         /**
26297              * @event rowselect
26298              * Fires when a row is selected.
26299              * @param {SelectionModel} this
26300              * @param {Number} rowIndex The selected index
26301              * @param {Roo.data.Record} r The record
26302              */
26303             "rowselect" : true,
26304         /**
26305              * @event rowdeselect
26306              * Fires when a row is deselected.
26307              * @param {SelectionModel} this
26308              * @param {Number} rowIndex The selected index
26309              */
26310         "rowdeselect" : true
26311     });
26312     Roo.bootstrap.Table.RowSelectionModel.superclass.constructor.call(this);
26313     this.locked = false;
26314  };
26315
26316 Roo.extend(Roo.bootstrap.Table.RowSelectionModel, Roo.bootstrap.Table.AbstractSelectionModel,  {
26317     /**
26318      * @cfg {Boolean} singleSelect
26319      * True to allow selection of only one row at a time (defaults to false)
26320      */
26321     singleSelect : false,
26322
26323     // private
26324     initEvents : function()
26325     {
26326
26327         //if(!this.grid.enableDragDrop && !this.grid.enableDrag){
26328         //    this.growclickrid.on("mousedown", this.handleMouseDown, this);
26329         //}else{ // allow click to work like normal
26330          //   this.grid.on("rowclick", this.handleDragableRowClick, this);
26331         //}
26332         //this.grid.on("rowdblclick", this.handleMouseDBClick, this);
26333         this.grid.on("rowclick", this.handleMouseDown, this);
26334         
26335         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
26336             "up" : function(e){
26337                 if(!e.shiftKey){
26338                     this.selectPrevious(e.shiftKey);
26339                 }else if(this.last !== false && this.lastActive !== false){
26340                     var last = this.last;
26341                     this.selectRange(this.last,  this.lastActive-1);
26342                     this.grid.getView().focusRow(this.lastActive);
26343                     if(last !== false){
26344                         this.last = last;
26345                     }
26346                 }else{
26347                     this.selectFirstRow();
26348                 }
26349                 this.fireEvent("afterselectionchange", this);
26350             },
26351             "down" : function(e){
26352                 if(!e.shiftKey){
26353                     this.selectNext(e.shiftKey);
26354                 }else if(this.last !== false && this.lastActive !== false){
26355                     var last = this.last;
26356                     this.selectRange(this.last,  this.lastActive+1);
26357                     this.grid.getView().focusRow(this.lastActive);
26358                     if(last !== false){
26359                         this.last = last;
26360                     }
26361                 }else{
26362                     this.selectFirstRow();
26363                 }
26364                 this.fireEvent("afterselectionchange", this);
26365             },
26366             scope: this
26367         });
26368         this.grid.store.on('load', function(){
26369             this.selections.clear();
26370         },this);
26371         /*
26372         var view = this.grid.view;
26373         view.on("refresh", this.onRefresh, this);
26374         view.on("rowupdated", this.onRowUpdated, this);
26375         view.on("rowremoved", this.onRemove, this);
26376         */
26377     },
26378
26379     // private
26380     onRefresh : function()
26381     {
26382         var ds = this.grid.store, i, v = this.grid.view;
26383         var s = this.selections;
26384         s.each(function(r){
26385             if((i = ds.indexOfId(r.id)) != -1){
26386                 v.onRowSelect(i);
26387             }else{
26388                 s.remove(r);
26389             }
26390         });
26391     },
26392
26393     // private
26394     onRemove : function(v, index, r){
26395         this.selections.remove(r);
26396     },
26397
26398     // private
26399     onRowUpdated : function(v, index, r){
26400         if(this.isSelected(r)){
26401             v.onRowSelect(index);
26402         }
26403     },
26404
26405     /**
26406      * Select records.
26407      * @param {Array} records The records to select
26408      * @param {Boolean} keepExisting (optional) True to keep existing selections
26409      */
26410     selectRecords : function(records, keepExisting)
26411     {
26412         if(!keepExisting){
26413             this.clearSelections();
26414         }
26415             var ds = this.grid.store;
26416         for(var i = 0, len = records.length; i < len; i++){
26417             this.selectRow(ds.indexOf(records[i]), true);
26418         }
26419     },
26420
26421     /**
26422      * Gets the number of selected rows.
26423      * @return {Number}
26424      */
26425     getCount : function(){
26426         return this.selections.length;
26427     },
26428
26429     /**
26430      * Selects the first row in the grid.
26431      */
26432     selectFirstRow : function(){
26433         this.selectRow(0);
26434     },
26435
26436     /**
26437      * Select the last row.
26438      * @param {Boolean} keepExisting (optional) True to keep existing selections
26439      */
26440     selectLastRow : function(keepExisting){
26441         //this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
26442         this.selectRow(this.grid.store.getCount() - 1, keepExisting);
26443     },
26444
26445     /**
26446      * Selects the row immediately following the last selected row.
26447      * @param {Boolean} keepExisting (optional) True to keep existing selections
26448      */
26449     selectNext : function(keepExisting)
26450     {
26451             if(this.last !== false && (this.last+1) < this.grid.store.getCount()){
26452             this.selectRow(this.last+1, keepExisting);
26453             this.grid.getView().focusRow(this.last);
26454         }
26455     },
26456
26457     /**
26458      * Selects the row that precedes the last selected row.
26459      * @param {Boolean} keepExisting (optional) True to keep existing selections
26460      */
26461     selectPrevious : function(keepExisting){
26462         if(this.last){
26463             this.selectRow(this.last-1, keepExisting);
26464             this.grid.getView().focusRow(this.last);
26465         }
26466     },
26467
26468     /**
26469      * Returns the selected records
26470      * @return {Array} Array of selected records
26471      */
26472     getSelections : function(){
26473         return [].concat(this.selections.items);
26474     },
26475
26476     /**
26477      * Returns the first selected record.
26478      * @return {Record}
26479      */
26480     getSelected : function(){
26481         return this.selections.itemAt(0);
26482     },
26483
26484
26485     /**
26486      * Clears all selections.
26487      */
26488     clearSelections : function(fast)
26489     {
26490         if(this.locked) {
26491             return;
26492         }
26493         if(fast !== true){
26494                 var ds = this.grid.store;
26495             var s = this.selections;
26496             s.each(function(r){
26497                 this.deselectRow(ds.indexOfId(r.id));
26498             }, this);
26499             s.clear();
26500         }else{
26501             this.selections.clear();
26502         }
26503         this.last = false;
26504     },
26505
26506
26507     /**
26508      * Selects all rows.
26509      */
26510     selectAll : function(){
26511         if(this.locked) {
26512             return;
26513         }
26514         this.selections.clear();
26515         for(var i = 0, len = this.grid.store.getCount(); i < len; i++){
26516             this.selectRow(i, true);
26517         }
26518     },
26519
26520     /**
26521      * Returns True if there is a selection.
26522      * @return {Boolean}
26523      */
26524     hasSelection : function(){
26525         return this.selections.length > 0;
26526     },
26527
26528     /**
26529      * Returns True if the specified row is selected.
26530      * @param {Number/Record} record The record or index of the record to check
26531      * @return {Boolean}
26532      */
26533     isSelected : function(index){
26534             var r = typeof index == "number" ? this.grid.store.getAt(index) : index;
26535         return (r && this.selections.key(r.id) ? true : false);
26536     },
26537
26538     /**
26539      * Returns True if the specified record id is selected.
26540      * @param {String} id The id of record to check
26541      * @return {Boolean}
26542      */
26543     isIdSelected : function(id){
26544         return (this.selections.key(id) ? true : false);
26545     },
26546
26547
26548     // private
26549     handleMouseDBClick : function(e, t){
26550         
26551     },
26552     // private
26553     handleMouseDown : function(e, t)
26554     {
26555             var rowIndex = this.grid.headerShow  ? t.dom.rowIndex - 1 : t.dom.rowIndex ; // first row is header???
26556         if(this.isLocked() || rowIndex < 0 ){
26557             return;
26558         };
26559         if(e.shiftKey && this.last !== false){
26560             var last = this.last;
26561             this.selectRange(last, rowIndex, e.ctrlKey);
26562             this.last = last; // reset the last
26563             t.focus();
26564     
26565         }else{
26566             var isSelected = this.isSelected(rowIndex);
26567             //Roo.log("select row:" + rowIndex);
26568             if(isSelected){
26569                 this.deselectRow(rowIndex);
26570             } else {
26571                         this.selectRow(rowIndex, true);
26572             }
26573     
26574             /*
26575                 if(e.button !== 0 && isSelected){
26576                 alert('rowIndex 2: ' + rowIndex);
26577                     view.focusRow(rowIndex);
26578                 }else if(e.ctrlKey && isSelected){
26579                     this.deselectRow(rowIndex);
26580                 }else if(!isSelected){
26581                     this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
26582                     view.focusRow(rowIndex);
26583                 }
26584             */
26585         }
26586         this.fireEvent("afterselectionchange", this);
26587     },
26588     // private
26589     handleDragableRowClick :  function(grid, rowIndex, e) 
26590     {
26591         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
26592             this.selectRow(rowIndex, false);
26593             grid.view.focusRow(rowIndex);
26594              this.fireEvent("afterselectionchange", this);
26595         }
26596     },
26597     
26598     /**
26599      * Selects multiple rows.
26600      * @param {Array} rows Array of the indexes of the row to select
26601      * @param {Boolean} keepExisting (optional) True to keep existing selections
26602      */
26603     selectRows : function(rows, keepExisting){
26604         if(!keepExisting){
26605             this.clearSelections();
26606         }
26607         for(var i = 0, len = rows.length; i < len; i++){
26608             this.selectRow(rows[i], true);
26609         }
26610     },
26611
26612     /**
26613      * Selects a range of rows. All rows in between startRow and endRow are also selected.
26614      * @param {Number} startRow The index of the first row in the range
26615      * @param {Number} endRow The index of the last row in the range
26616      * @param {Boolean} keepExisting (optional) True to retain existing selections
26617      */
26618     selectRange : function(startRow, endRow, keepExisting){
26619         if(this.locked) {
26620             return;
26621         }
26622         if(!keepExisting){
26623             this.clearSelections();
26624         }
26625         if(startRow <= endRow){
26626             for(var i = startRow; i <= endRow; i++){
26627                 this.selectRow(i, true);
26628             }
26629         }else{
26630             for(var i = startRow; i >= endRow; i--){
26631                 this.selectRow(i, true);
26632             }
26633         }
26634     },
26635
26636     /**
26637      * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
26638      * @param {Number} startRow The index of the first row in the range
26639      * @param {Number} endRow The index of the last row in the range
26640      */
26641     deselectRange : function(startRow, endRow, preventViewNotify){
26642         if(this.locked) {
26643             return;
26644         }
26645         for(var i = startRow; i <= endRow; i++){
26646             this.deselectRow(i, preventViewNotify);
26647         }
26648     },
26649
26650     /**
26651      * Selects a row.
26652      * @param {Number} row The index of the row to select
26653      * @param {Boolean} keepExisting (optional) True to keep existing selections
26654      */
26655     selectRow : function(index, keepExisting, preventViewNotify)
26656     {
26657             if(this.locked || (index < 0 || index > this.grid.store.getCount())) {
26658             return;
26659         }
26660         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
26661             if(!keepExisting || this.singleSelect){
26662                 this.clearSelections();
26663             }
26664             
26665             var r = this.grid.store.getAt(index);
26666             //console.log('selectRow - record id :' + r.id);
26667             
26668             this.selections.add(r);
26669             this.last = this.lastActive = index;
26670             if(!preventViewNotify){
26671                 var proxy = new Roo.Element(
26672                                 this.grid.getRowDom(index)
26673                 );
26674                 proxy.addClass('bg-info info');
26675             }
26676             this.fireEvent("rowselect", this, index, r);
26677             this.fireEvent("selectionchange", this);
26678         }
26679     },
26680
26681     /**
26682      * Deselects a row.
26683      * @param {Number} row The index of the row to deselect
26684      */
26685     deselectRow : function(index, preventViewNotify)
26686     {
26687         if(this.locked) {
26688             return;
26689         }
26690         if(this.last == index){
26691             this.last = false;
26692         }
26693         if(this.lastActive == index){
26694             this.lastActive = false;
26695         }
26696         
26697         var r = this.grid.store.getAt(index);
26698         if (!r) {
26699             return;
26700         }
26701         
26702         this.selections.remove(r);
26703         //.console.log('deselectRow - record id :' + r.id);
26704         if(!preventViewNotify){
26705         
26706             var proxy = new Roo.Element(
26707                 this.grid.getRowDom(index)
26708             );
26709             proxy.removeClass('bg-info info');
26710         }
26711         this.fireEvent("rowdeselect", this, index);
26712         this.fireEvent("selectionchange", this);
26713     },
26714
26715     // private
26716     restoreLast : function(){
26717         if(this._last){
26718             this.last = this._last;
26719         }
26720     },
26721
26722     // private
26723     acceptsNav : function(row, col, cm){
26724         return !cm.isHidden(col) && cm.isCellEditable(col, row);
26725     },
26726
26727     // private
26728     onEditorKey : function(field, e){
26729         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
26730         if(k == e.TAB){
26731             e.stopEvent();
26732             ed.completeEdit();
26733             if(e.shiftKey){
26734                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
26735             }else{
26736                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
26737             }
26738         }else if(k == e.ENTER && !e.ctrlKey){
26739             e.stopEvent();
26740             ed.completeEdit();
26741             if(e.shiftKey){
26742                 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
26743             }else{
26744                 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
26745             }
26746         }else if(k == e.ESC){
26747             ed.cancelEdit();
26748         }
26749         if(newCell){
26750             g.startEditing(newCell[0], newCell[1]);
26751         }
26752     }
26753 });
26754 /*
26755  * Based on:
26756  * Ext JS Library 1.1.1
26757  * Copyright(c) 2006-2007, Ext JS, LLC.
26758  *
26759  * Originally Released Under LGPL - original licence link has changed is not relivant.
26760  *
26761  * Fork - LGPL
26762  * <script type="text/javascript">
26763  */
26764  
26765 /**
26766  * @class Roo.bootstrap.PagingToolbar
26767  * @extends Roo.bootstrap.NavSimplebar
26768  * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
26769  * @constructor
26770  * Create a new PagingToolbar
26771  * @param {Object} config The config object
26772  * @param {Roo.data.Store} store
26773  */
26774 Roo.bootstrap.PagingToolbar = function(config)
26775 {
26776     // old args format still supported... - xtype is prefered..
26777         // created from xtype...
26778     
26779     this.ds = config.dataSource;
26780     
26781     if (config.store && !this.ds) {
26782         this.store= Roo.factory(config.store, Roo.data);
26783         this.ds = this.store;
26784         this.ds.xmodule = this.xmodule || false;
26785     }
26786     
26787     this.toolbarItems = [];
26788     if (config.items) {
26789         this.toolbarItems = config.items;
26790     }
26791     
26792     Roo.bootstrap.PagingToolbar.superclass.constructor.call(this, config);
26793     
26794     this.cursor = 0;
26795     
26796     if (this.ds) { 
26797         this.bind(this.ds);
26798     }
26799     
26800     if (Roo.bootstrap.version == 4) {
26801         this.navgroup = new Roo.bootstrap.ButtonGroup({ cls: 'pagination' });
26802     } else {
26803         this.navgroup = new Roo.bootstrap.NavGroup({ cls: 'pagination' });
26804     }
26805     
26806 };
26807
26808 Roo.extend(Roo.bootstrap.PagingToolbar, Roo.bootstrap.NavSimplebar, {
26809     /**
26810      * @cfg {Roo.data.Store} dataSource
26811      * The underlying data store providing the paged data
26812      */
26813     /**
26814      * @cfg {String/HTMLElement/Element} container
26815      * container The id or element that will contain the toolbar
26816      */
26817     /**
26818      * @cfg {Boolean} displayInfo
26819      * True to display the displayMsg (defaults to false)
26820      */
26821     /**
26822      * @cfg {Number} pageSize
26823      * The number of records to display per page (defaults to 20)
26824      */
26825     pageSize: 20,
26826     /**
26827      * @cfg {String} displayMsg
26828      * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
26829      */
26830     displayMsg : 'Displaying {0} - {1} of {2}',
26831     /**
26832      * @cfg {String} emptyMsg
26833      * The message to display when no records are found (defaults to "No data to display")
26834      */
26835     emptyMsg : 'No data to display',
26836     /**
26837      * Customizable piece of the default paging text (defaults to "Page")
26838      * @type String
26839      */
26840     beforePageText : "Page",
26841     /**
26842      * Customizable piece of the default paging text (defaults to "of %0")
26843      * @type String
26844      */
26845     afterPageText : "of {0}",
26846     /**
26847      * Customizable piece of the default paging text (defaults to "First Page")
26848      * @type String
26849      */
26850     firstText : "First Page",
26851     /**
26852      * Customizable piece of the default paging text (defaults to "Previous Page")
26853      * @type String
26854      */
26855     prevText : "Previous Page",
26856     /**
26857      * Customizable piece of the default paging text (defaults to "Next Page")
26858      * @type String
26859      */
26860     nextText : "Next Page",
26861     /**
26862      * Customizable piece of the default paging text (defaults to "Last Page")
26863      * @type String
26864      */
26865     lastText : "Last Page",
26866     /**
26867      * Customizable piece of the default paging text (defaults to "Refresh")
26868      * @type String
26869      */
26870     refreshText : "Refresh",
26871
26872     buttons : false,
26873     // private
26874     onRender : function(ct, position) 
26875     {
26876         Roo.bootstrap.PagingToolbar.superclass.onRender.call(this, ct, position);
26877         this.navgroup.parentId = this.id;
26878         this.navgroup.onRender(this.el, null);
26879         // add the buttons to the navgroup
26880         
26881         if(this.displayInfo){
26882             this.el.select('ul.navbar-nav',true).first().createChild({cls:'x-paging-info'});
26883             this.displayEl = this.el.select('.x-paging-info', true).first();
26884 //            var navel = this.navgroup.addItem( { tagtype : 'span', html : '', cls : 'x-paging-info', preventDefault : true } );
26885 //            this.displayEl = navel.el.select('span',true).first();
26886         }
26887         
26888         var _this = this;
26889         
26890         if(this.buttons){
26891             Roo.each(_this.buttons, function(e){ // this might need to use render????
26892                Roo.factory(e).render(_this.el);
26893             });
26894         }
26895             
26896         Roo.each(_this.toolbarItems, function(e) {
26897             _this.navgroup.addItem(e);
26898         });
26899         
26900         
26901         this.first = this.navgroup.addItem({
26902             tooltip: this.firstText,
26903             cls: "prev btn-outline-secondary",
26904             html : ' <i class="fa fa-step-backward"></i>',
26905             disabled: true,
26906             preventDefault: true,
26907             listeners : { click : this.onClick.createDelegate(this, ["first"]) }
26908         });
26909         
26910         this.prev =  this.navgroup.addItem({
26911             tooltip: this.prevText,
26912             cls: "prev btn-outline-secondary",
26913             html : ' <i class="fa fa-backward"></i>',
26914             disabled: true,
26915             preventDefault: true,
26916             listeners : { click :  this.onClick.createDelegate(this, ["prev"]) }
26917         });
26918     //this.addSeparator();
26919         
26920         
26921         var field = this.navgroup.addItem( {
26922             tagtype : 'span',
26923             cls : 'x-paging-position  btn-outline-secondary',
26924              disabled: true,
26925             html : this.beforePageText  +
26926                 '<input type="text" size="3" value="1" class="x-grid-page-number">' +
26927                 '<span class="x-paging-after">' +  String.format(this.afterPageText, 1) + '</span>'
26928          } ); //?? escaped?
26929         
26930         this.field = field.el.select('input', true).first();
26931         this.field.on("keydown", this.onPagingKeydown, this);
26932         this.field.on("focus", function(){this.dom.select();});
26933     
26934     
26935         this.afterTextEl =  field.el.select('.x-paging-after',true).first();
26936         //this.field.setHeight(18);
26937         //this.addSeparator();
26938         this.next = this.navgroup.addItem({
26939             tooltip: this.nextText,
26940             cls: "next btn-outline-secondary",
26941             html : ' <i class="fa fa-forward"></i>',
26942             disabled: true,
26943             preventDefault: true,
26944             listeners : { click :  this.onClick.createDelegate(this, ["next"]) }
26945         });
26946         this.last = this.navgroup.addItem({
26947             tooltip: this.lastText,
26948             html : ' <i class="fa fa-step-forward"></i>',
26949             cls: "next btn-outline-secondary",
26950             disabled: true,
26951             preventDefault: true,
26952             listeners : { click :  this.onClick.createDelegate(this, ["last"]) }
26953         });
26954     //this.addSeparator();
26955         this.loading = this.navgroup.addItem({
26956             tooltip: this.refreshText,
26957             cls: "btn-outline-secondary",
26958             html : ' <i class="fa fa-refresh"></i>',
26959             preventDefault: true,
26960             listeners : { click : this.onClick.createDelegate(this, ["refresh"]) }
26961         });
26962         
26963     },
26964
26965     // private
26966     updateInfo : function(){
26967         if(this.displayEl){
26968             var count = (typeof(this.getCount) == 'undefined') ? this.ds.getCount() : this.getCount();
26969             var msg = count == 0 ?
26970                 this.emptyMsg :
26971                 String.format(
26972                     this.displayMsg,
26973                     this.cursor+1, this.cursor+count, this.ds.getTotalCount()    
26974                 );
26975             this.displayEl.update(msg);
26976         }
26977     },
26978
26979     // private
26980     onLoad : function(ds, r, o)
26981     {
26982         this.cursor = o.params.start ? o.params.start : 0;
26983         
26984         var d = this.getPageData(),
26985             ap = d.activePage,
26986             ps = d.pages;
26987         
26988         
26989         this.afterTextEl.dom.innerHTML = String.format(this.afterPageText, d.pages);
26990         this.field.dom.value = ap;
26991         this.first.setDisabled(ap == 1);
26992         this.prev.setDisabled(ap == 1);
26993         this.next.setDisabled(ap == ps);
26994         this.last.setDisabled(ap == ps);
26995         this.loading.enable();
26996         this.updateInfo();
26997     },
26998
26999     // private
27000     getPageData : function(){
27001         var total = this.ds.getTotalCount();
27002         return {
27003             total : total,
27004             activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
27005             pages :  total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
27006         };
27007     },
27008
27009     // private
27010     onLoadError : function(){
27011         this.loading.enable();
27012     },
27013
27014     // private
27015     onPagingKeydown : function(e){
27016         var k = e.getKey();
27017         var d = this.getPageData();
27018         if(k == e.RETURN){
27019             var v = this.field.dom.value, pageNum;
27020             if(!v || isNaN(pageNum = parseInt(v, 10))){
27021                 this.field.dom.value = d.activePage;
27022                 return;
27023             }
27024             pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
27025             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
27026             e.stopEvent();
27027         }
27028         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))
27029         {
27030           var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
27031           this.field.dom.value = pageNum;
27032           this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
27033           e.stopEvent();
27034         }
27035         else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
27036         {
27037           var v = this.field.dom.value, pageNum; 
27038           var increment = (e.shiftKey) ? 10 : 1;
27039           if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN) {
27040                 increment *= -1;
27041           }
27042           if(!v || isNaN(pageNum = parseInt(v, 10))) {
27043             this.field.dom.value = d.activePage;
27044             return;
27045           }
27046           else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
27047           {
27048             this.field.dom.value = parseInt(v, 10) + increment;
27049             pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
27050             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
27051           }
27052           e.stopEvent();
27053         }
27054     },
27055
27056     // private
27057     beforeLoad : function(){
27058         if(this.loading){
27059             this.loading.disable();
27060         }
27061     },
27062
27063     // private
27064     onClick : function(which){
27065         
27066         var ds = this.ds;
27067         if (!ds) {
27068             return;
27069         }
27070         
27071         switch(which){
27072             case "first":
27073                 ds.load({params:{start: 0, limit: this.pageSize}});
27074             break;
27075             case "prev":
27076                 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
27077             break;
27078             case "next":
27079                 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
27080             break;
27081             case "last":
27082                 var total = ds.getTotalCount();
27083                 var extra = total % this.pageSize;
27084                 var lastStart = extra ? (total - extra) : total-this.pageSize;
27085                 ds.load({params:{start: lastStart, limit: this.pageSize}});
27086             break;
27087             case "refresh":
27088                 ds.load({params:{start: this.cursor, limit: this.pageSize}});
27089             break;
27090         }
27091     },
27092
27093     /**
27094      * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
27095      * @param {Roo.data.Store} store The data store to unbind
27096      */
27097     unbind : function(ds){
27098         ds.un("beforeload", this.beforeLoad, this);
27099         ds.un("load", this.onLoad, this);
27100         ds.un("loadexception", this.onLoadError, this);
27101         ds.un("remove", this.updateInfo, this);
27102         ds.un("add", this.updateInfo, this);
27103         this.ds = undefined;
27104     },
27105
27106     /**
27107      * Binds the paging toolbar to the specified {@link Roo.data.Store}
27108      * @param {Roo.data.Store} store The data store to bind
27109      */
27110     bind : function(ds){
27111         ds.on("beforeload", this.beforeLoad, this);
27112         ds.on("load", this.onLoad, this);
27113         ds.on("loadexception", this.onLoadError, this);
27114         ds.on("remove", this.updateInfo, this);
27115         ds.on("add", this.updateInfo, this);
27116         this.ds = ds;
27117     }
27118 });/*
27119  * - LGPL
27120  *
27121  * element
27122  * 
27123  */
27124
27125 /**
27126  * @class Roo.bootstrap.MessageBar
27127  * @extends Roo.bootstrap.Component
27128  * Bootstrap MessageBar class
27129  * @cfg {String} html contents of the MessageBar
27130  * @cfg {String} weight (info | success | warning | danger) default info
27131  * @cfg {String} beforeClass insert the bar before the given class
27132  * @cfg {Boolean} closable (true | false) default false
27133  * @cfg {Boolean} fixed (true | false) default false, fix the bar at the top
27134  * 
27135  * @constructor
27136  * Create a new Element
27137  * @param {Object} config The config object
27138  */
27139
27140 Roo.bootstrap.MessageBar = function(config){
27141     Roo.bootstrap.MessageBar.superclass.constructor.call(this, config);
27142 };
27143
27144 Roo.extend(Roo.bootstrap.MessageBar, Roo.bootstrap.Component,  {
27145     
27146     html: '',
27147     weight: 'info',
27148     closable: false,
27149     fixed: false,
27150     beforeClass: 'bootstrap-sticky-wrap',
27151     
27152     getAutoCreate : function(){
27153         
27154         var cfg = {
27155             tag: 'div',
27156             cls: 'alert alert-dismissable alert-' + this.weight,
27157             cn: [
27158                 {
27159                     tag: 'span',
27160                     cls: 'message',
27161                     html: this.html || ''
27162                 }
27163             ]
27164         };
27165         
27166         if(this.fixed){
27167             cfg.cls += ' alert-messages-fixed';
27168         }
27169         
27170         if(this.closable){
27171             cfg.cn.push({
27172                 tag: 'button',
27173                 cls: 'close',
27174                 html: 'x'
27175             });
27176         }
27177         
27178         return cfg;
27179     },
27180     
27181     onRender : function(ct, position)
27182     {
27183         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
27184         
27185         if(!this.el){
27186             var cfg = Roo.apply({},  this.getAutoCreate());
27187             cfg.id = Roo.id();
27188             
27189             if (this.cls) {
27190                 cfg.cls += ' ' + this.cls;
27191             }
27192             if (this.style) {
27193                 cfg.style = this.style;
27194             }
27195             this.el = Roo.get(document.body).createChild(cfg, Roo.select('.'+this.beforeClass, true).first());
27196             
27197             this.el.setVisibilityMode(Roo.Element.DISPLAY);
27198         }
27199         
27200         this.el.select('>button.close').on('click', this.hide, this);
27201         
27202     },
27203     
27204     show : function()
27205     {
27206         if (!this.rendered) {
27207             this.render();
27208         }
27209         
27210         this.el.show();
27211         
27212         this.fireEvent('show', this);
27213         
27214     },
27215     
27216     hide : function()
27217     {
27218         if (!this.rendered) {
27219             this.render();
27220         }
27221         
27222         this.el.hide();
27223         
27224         this.fireEvent('hide', this);
27225     },
27226     
27227     update : function()
27228     {
27229 //        var e = this.el.dom.firstChild;
27230 //        
27231 //        if(this.closable){
27232 //            e = e.nextSibling;
27233 //        }
27234 //        
27235 //        e.data = this.html || '';
27236
27237         this.el.select('>.message', true).first().dom.innerHTML = this.html || '';
27238     }
27239    
27240 });
27241
27242  
27243
27244      /*
27245  * - LGPL
27246  *
27247  * Graph
27248  * 
27249  */
27250
27251
27252 /**
27253  * @class Roo.bootstrap.Graph
27254  * @extends Roo.bootstrap.Component
27255  * Bootstrap Graph class
27256 > Prameters
27257  -sm {number} sm 4
27258  -md {number} md 5
27259  @cfg {String} graphtype  bar | vbar | pie
27260  @cfg {number} g_x coodinator | centre x (pie)
27261  @cfg {number} g_y coodinator | centre y (pie)
27262  @cfg {number} g_r radius (pie)
27263  @cfg {number} g_height height of the chart (respected by all elements in the set)
27264  @cfg {number} g_width width of the chart (respected by all elements in the set)
27265  @cfg {Object} title The title of the chart
27266     
27267  -{Array}  values
27268  -opts (object) options for the chart 
27269      o {
27270      o type (string) type of endings of the bar. Default: 'square'. Other options are: 'round', 'sharp', 'soft'.
27271      o gutter (number)(string) default '20%' (WHAT DOES IT DO?)
27272      o vgutter (number)
27273      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.
27274      o stacked (boolean) whether or not to tread values as in a stacked bar chart
27275      o to
27276      o stretch (boolean)
27277      o }
27278  -opts (object) options for the pie
27279      o{
27280      o cut
27281      o startAngle (number)
27282      o endAngle (number)
27283      } 
27284  *
27285  * @constructor
27286  * Create a new Input
27287  * @param {Object} config The config object
27288  */
27289
27290 Roo.bootstrap.Graph = function(config){
27291     Roo.bootstrap.Graph.superclass.constructor.call(this, config);
27292     
27293     this.addEvents({
27294         // img events
27295         /**
27296          * @event click
27297          * The img click event for the img.
27298          * @param {Roo.EventObject} e
27299          */
27300         "click" : true
27301     });
27302 };
27303
27304 Roo.extend(Roo.bootstrap.Graph, Roo.bootstrap.Component,  {
27305     
27306     sm: 4,
27307     md: 5,
27308     graphtype: 'bar',
27309     g_height: 250,
27310     g_width: 400,
27311     g_x: 50,
27312     g_y: 50,
27313     g_r: 30,
27314     opts:{
27315         //g_colors: this.colors,
27316         g_type: 'soft',
27317         g_gutter: '20%'
27318
27319     },
27320     title : false,
27321
27322     getAutoCreate : function(){
27323         
27324         var cfg = {
27325             tag: 'div',
27326             html : null
27327         };
27328         
27329         
27330         return  cfg;
27331     },
27332
27333     onRender : function(ct,position){
27334         
27335         
27336         Roo.bootstrap.Graph.superclass.onRender.call(this,ct,position);
27337         
27338         if (typeof(Raphael) == 'undefined') {
27339             Roo.bootstrap.MessageBox.alert("Error","Raphael is not availabe");
27340             return;
27341         }
27342         
27343         this.raphael = Raphael(this.el.dom);
27344         
27345                     // data1 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
27346                     // data2 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
27347                     // data3 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
27348                     // txtattr = { font: "12px 'Fontin Sans', Fontin-Sans, sans-serif" };
27349                 /*
27350                 r.text(160, 10, "Single Series Chart").attr(txtattr);
27351                 r.text(480, 10, "Multiline Series Chart").attr(txtattr);
27352                 r.text(160, 250, "Multiple Series Stacked Chart").attr(txtattr);
27353                 r.text(480, 250, 'Multiline Series Stacked Vertical Chart. Type "round"').attr(txtattr);
27354                 
27355                 r.barchart(10, 10, 300, 220, [[55, 20, 13, 32, 5, 1, 2, 10]], 0, {type: "sharp"});
27356                 r.barchart(330, 10, 300, 220, data1);
27357                 r.barchart(10, 250, 300, 220, data2, {stacked: true});
27358                 r.barchart(330, 250, 300, 220, data3, {stacked: true, type: "round"});
27359                 */
27360                 
27361                 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
27362                 // r.barchart(30, 30, 560, 250,  xdata, {
27363                 //    labels : [55, 20, 13, 32, 5, 1, 2, 10,5 , 10],
27364                 //     axis : "0 0 1 1",
27365                 //     axisxlabels :  xdata
27366                 //     //yvalues : cols,
27367                    
27368                 // });
27369 //        var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
27370 //        
27371 //        this.load(null,xdata,{
27372 //                axis : "0 0 1 1",
27373 //                axisxlabels :  xdata
27374 //                });
27375
27376     },
27377
27378     load : function(graphtype,xdata,opts)
27379     {
27380         this.raphael.clear();
27381         if(!graphtype) {
27382             graphtype = this.graphtype;
27383         }
27384         if(!opts){
27385             opts = this.opts;
27386         }
27387         var r = this.raphael,
27388             fin = function () {
27389                 this.flag = r.popup(this.bar.x, this.bar.y, this.bar.value || "0").insertBefore(this);
27390             },
27391             fout = function () {
27392                 this.flag.animate({opacity: 0}, 300, function () {this.remove();});
27393             },
27394             pfin = function() {
27395                 this.sector.stop();
27396                 this.sector.scale(1.1, 1.1, this.cx, this.cy);
27397
27398                 if (this.label) {
27399                     this.label[0].stop();
27400                     this.label[0].attr({ r: 7.5 });
27401                     this.label[1].attr({ "font-weight": 800 });
27402                 }
27403             },
27404             pfout = function() {
27405                 this.sector.animate({ transform: 's1 1 ' + this.cx + ' ' + this.cy }, 500, "bounce");
27406
27407                 if (this.label) {
27408                     this.label[0].animate({ r: 5 }, 500, "bounce");
27409                     this.label[1].attr({ "font-weight": 400 });
27410                 }
27411             };
27412
27413         switch(graphtype){
27414             case 'bar':
27415                 this.raphael.barchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
27416                 break;
27417             case 'hbar':
27418                 this.raphael.hbarchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
27419                 break;
27420             case 'pie':
27421 //                opts = { legend: ["%% - Enterprise Users", "% - ddd","Chrome Users"], legendpos: "west", 
27422 //                href: ["http://raphaeljs.com", "http://g.raphaeljs.com"]};
27423 //            
27424                 this.raphael.piechart(this.g_x,this.g_y,this.g_r,xdata,opts).hover(pfin, pfout);
27425                 
27426                 break;
27427
27428         }
27429         
27430         if(this.title){
27431             this.raphael.text(this.title.x, this.title.y, this.title.text).attr(this.title.attr);
27432         }
27433         
27434     },
27435     
27436     setTitle: function(o)
27437     {
27438         this.title = o;
27439     },
27440     
27441     initEvents: function() {
27442         
27443         if(!this.href){
27444             this.el.on('click', this.onClick, this);
27445         }
27446     },
27447     
27448     onClick : function(e)
27449     {
27450         Roo.log('img onclick');
27451         this.fireEvent('click', this, e);
27452     }
27453    
27454 });
27455
27456  
27457 /*
27458  * - LGPL
27459  *
27460  * numberBox
27461  * 
27462  */
27463 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
27464
27465 /**
27466  * @class Roo.bootstrap.dash.NumberBox
27467  * @extends Roo.bootstrap.Component
27468  * Bootstrap NumberBox class
27469  * @cfg {String} headline Box headline
27470  * @cfg {String} content Box content
27471  * @cfg {String} icon Box icon
27472  * @cfg {String} footer Footer text
27473  * @cfg {String} fhref Footer href
27474  * 
27475  * @constructor
27476  * Create a new NumberBox
27477  * @param {Object} config The config object
27478  */
27479
27480
27481 Roo.bootstrap.dash.NumberBox = function(config){
27482     Roo.bootstrap.dash.NumberBox.superclass.constructor.call(this, config);
27483     
27484 };
27485
27486 Roo.extend(Roo.bootstrap.dash.NumberBox, Roo.bootstrap.Component,  {
27487     
27488     headline : '',
27489     content : '',
27490     icon : '',
27491     footer : '',
27492     fhref : '',
27493     ficon : '',
27494     
27495     getAutoCreate : function(){
27496         
27497         var cfg = {
27498             tag : 'div',
27499             cls : 'small-box ',
27500             cn : [
27501                 {
27502                     tag : 'div',
27503                     cls : 'inner',
27504                     cn :[
27505                         {
27506                             tag : 'h3',
27507                             cls : 'roo-headline',
27508                             html : this.headline
27509                         },
27510                         {
27511                             tag : 'p',
27512                             cls : 'roo-content',
27513                             html : this.content
27514                         }
27515                     ]
27516                 }
27517             ]
27518         };
27519         
27520         if(this.icon){
27521             cfg.cn.push({
27522                 tag : 'div',
27523                 cls : 'icon',
27524                 cn :[
27525                     {
27526                         tag : 'i',
27527                         cls : 'ion ' + this.icon
27528                     }
27529                 ]
27530             });
27531         }
27532         
27533         if(this.footer){
27534             var footer = {
27535                 tag : 'a',
27536                 cls : 'small-box-footer',
27537                 href : this.fhref || '#',
27538                 html : this.footer
27539             };
27540             
27541             cfg.cn.push(footer);
27542             
27543         }
27544         
27545         return  cfg;
27546     },
27547
27548     onRender : function(ct,position){
27549         Roo.bootstrap.dash.NumberBox.superclass.onRender.call(this,ct,position);
27550
27551
27552        
27553                 
27554     },
27555
27556     setHeadline: function (value)
27557     {
27558         this.el.select('.roo-headline',true).first().dom.innerHTML = value;
27559     },
27560     
27561     setFooter: function (value, href)
27562     {
27563         this.el.select('a.small-box-footer',true).first().dom.innerHTML = value;
27564         
27565         if(href){
27566             this.el.select('a.small-box-footer',true).first().attr('href', href);
27567         }
27568         
27569     },
27570
27571     setContent: function (value)
27572     {
27573         this.el.select('.roo-content',true).first().dom.innerHTML = value;
27574     },
27575
27576     initEvents: function() 
27577     {   
27578         
27579     }
27580     
27581 });
27582
27583  
27584 /*
27585  * - LGPL
27586  *
27587  * TabBox
27588  * 
27589  */
27590 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
27591
27592 /**
27593  * @class Roo.bootstrap.dash.TabBox
27594  * @extends Roo.bootstrap.Component
27595  * Bootstrap TabBox class
27596  * @cfg {String} title Title of the TabBox
27597  * @cfg {String} icon Icon of the TabBox
27598  * @cfg {Boolean} showtabs (true|false) show the tabs default true
27599  * @cfg {Boolean} tabScrollable (true|false) tab scrollable when mobile view default false
27600  * 
27601  * @constructor
27602  * Create a new TabBox
27603  * @param {Object} config The config object
27604  */
27605
27606
27607 Roo.bootstrap.dash.TabBox = function(config){
27608     Roo.bootstrap.dash.TabBox.superclass.constructor.call(this, config);
27609     this.addEvents({
27610         // raw events
27611         /**
27612          * @event addpane
27613          * When a pane is added
27614          * @param {Roo.bootstrap.dash.TabPane} pane
27615          */
27616         "addpane" : true,
27617         /**
27618          * @event activatepane
27619          * When a pane is activated
27620          * @param {Roo.bootstrap.dash.TabPane} pane
27621          */
27622         "activatepane" : true
27623         
27624          
27625     });
27626     
27627     this.panes = [];
27628 };
27629
27630 Roo.extend(Roo.bootstrap.dash.TabBox, Roo.bootstrap.Component,  {
27631
27632     title : '',
27633     icon : false,
27634     showtabs : true,
27635     tabScrollable : false,
27636     
27637     getChildContainer : function()
27638     {
27639         return this.el.select('.tab-content', true).first();
27640     },
27641     
27642     getAutoCreate : function(){
27643         
27644         var header = {
27645             tag: 'li',
27646             cls: 'pull-left header',
27647             html: this.title,
27648             cn : []
27649         };
27650         
27651         if(this.icon){
27652             header.cn.push({
27653                 tag: 'i',
27654                 cls: 'fa ' + this.icon
27655             });
27656         }
27657         
27658         var h = {
27659             tag: 'ul',
27660             cls: 'nav nav-tabs pull-right',
27661             cn: [
27662                 header
27663             ]
27664         };
27665         
27666         if(this.tabScrollable){
27667             h = {
27668                 tag: 'div',
27669                 cls: 'tab-header',
27670                 cn: [
27671                     {
27672                         tag: 'ul',
27673                         cls: 'nav nav-tabs pull-right',
27674                         cn: [
27675                             header
27676                         ]
27677                     }
27678                 ]
27679             };
27680         }
27681         
27682         var cfg = {
27683             tag: 'div',
27684             cls: 'nav-tabs-custom',
27685             cn: [
27686                 h,
27687                 {
27688                     tag: 'div',
27689                     cls: 'tab-content no-padding',
27690                     cn: []
27691                 }
27692             ]
27693         };
27694
27695         return  cfg;
27696     },
27697     initEvents : function()
27698     {
27699         //Roo.log('add add pane handler');
27700         this.on('addpane', this.onAddPane, this);
27701     },
27702      /**
27703      * Updates the box title
27704      * @param {String} html to set the title to.
27705      */
27706     setTitle : function(value)
27707     {
27708         this.el.select('.nav-tabs .header', true).first().dom.innerHTML = value;
27709     },
27710     onAddPane : function(pane)
27711     {
27712         this.panes.push(pane);
27713         //Roo.log('addpane');
27714         //Roo.log(pane);
27715         // tabs are rendere left to right..
27716         if(!this.showtabs){
27717             return;
27718         }
27719         
27720         var ctr = this.el.select('.nav-tabs', true).first();
27721          
27722          
27723         var existing = ctr.select('.nav-tab',true);
27724         var qty = existing.getCount();;
27725         
27726         
27727         var tab = ctr.createChild({
27728             tag : 'li',
27729             cls : 'nav-tab' + (qty ? '' : ' active'),
27730             cn : [
27731                 {
27732                     tag : 'a',
27733                     href:'#',
27734                     html : pane.title
27735                 }
27736             ]
27737         }, qty ? existing.first().dom : ctr.select('.header', true).first().dom );
27738         pane.tab = tab;
27739         
27740         tab.on('click', this.onTabClick.createDelegate(this, [pane], true));
27741         if (!qty) {
27742             pane.el.addClass('active');
27743         }
27744         
27745                 
27746     },
27747     onTabClick : function(ev,un,ob,pane)
27748     {
27749         //Roo.log('tab - prev default');
27750         ev.preventDefault();
27751         
27752         
27753         this.el.select('.nav-tabs li.nav-tab', true).removeClass('active');
27754         pane.tab.addClass('active');
27755         //Roo.log(pane.title);
27756         this.getChildContainer().select('.tab-pane',true).removeClass('active');
27757         // technically we should have a deactivate event.. but maybe add later.
27758         // and it should not de-activate the selected tab...
27759         this.fireEvent('activatepane', pane);
27760         pane.el.addClass('active');
27761         pane.fireEvent('activate');
27762         
27763         
27764     },
27765     
27766     getActivePane : function()
27767     {
27768         var r = false;
27769         Roo.each(this.panes, function(p) {
27770             if(p.el.hasClass('active')){
27771                 r = p;
27772                 return false;
27773             }
27774             
27775             return;
27776         });
27777         
27778         return r;
27779     }
27780     
27781     
27782 });
27783
27784  
27785 /*
27786  * - LGPL
27787  *
27788  * Tab pane
27789  * 
27790  */
27791 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
27792 /**
27793  * @class Roo.bootstrap.TabPane
27794  * @extends Roo.bootstrap.Component
27795  * Bootstrap TabPane class
27796  * @cfg {Boolean} active (false | true) Default false
27797  * @cfg {String} title title of panel
27798
27799  * 
27800  * @constructor
27801  * Create a new TabPane
27802  * @param {Object} config The config object
27803  */
27804
27805 Roo.bootstrap.dash.TabPane = function(config){
27806     Roo.bootstrap.dash.TabPane.superclass.constructor.call(this, config);
27807     
27808     this.addEvents({
27809         // raw events
27810         /**
27811          * @event activate
27812          * When a pane is activated
27813          * @param {Roo.bootstrap.dash.TabPane} pane
27814          */
27815         "activate" : true
27816          
27817     });
27818 };
27819
27820 Roo.extend(Roo.bootstrap.dash.TabPane, Roo.bootstrap.Component,  {
27821     
27822     active : false,
27823     title : '',
27824     
27825     // the tabBox that this is attached to.
27826     tab : false,
27827      
27828     getAutoCreate : function() 
27829     {
27830         var cfg = {
27831             tag: 'div',
27832             cls: 'tab-pane'
27833         };
27834         
27835         if(this.active){
27836             cfg.cls += ' active';
27837         }
27838         
27839         return cfg;
27840     },
27841     initEvents  : function()
27842     {
27843         //Roo.log('trigger add pane handler');
27844         this.parent().fireEvent('addpane', this)
27845     },
27846     
27847      /**
27848      * Updates the tab title 
27849      * @param {String} html to set the title to.
27850      */
27851     setTitle: function(str)
27852     {
27853         if (!this.tab) {
27854             return;
27855         }
27856         this.title = str;
27857         this.tab.select('a', true).first().dom.innerHTML = str;
27858         
27859     }
27860     
27861     
27862     
27863 });
27864
27865  
27866
27867
27868  /*
27869  * - LGPL
27870  *
27871  * menu
27872  * 
27873  */
27874 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
27875
27876 /**
27877  * @class Roo.bootstrap.menu.Menu
27878  * @extends Roo.bootstrap.Component
27879  * Bootstrap Menu class - container for Menu
27880  * @cfg {String} html Text of the menu
27881  * @cfg {String} weight (default | primary | success | info | warning | danger | inverse)
27882  * @cfg {String} icon Font awesome icon
27883  * @cfg {String} pos Menu align to (top | bottom) default bottom
27884  * 
27885  * 
27886  * @constructor
27887  * Create a new Menu
27888  * @param {Object} config The config object
27889  */
27890
27891
27892 Roo.bootstrap.menu.Menu = function(config){
27893     Roo.bootstrap.menu.Menu.superclass.constructor.call(this, config);
27894     
27895     this.addEvents({
27896         /**
27897          * @event beforeshow
27898          * Fires before this menu is displayed
27899          * @param {Roo.bootstrap.menu.Menu} this
27900          */
27901         beforeshow : true,
27902         /**
27903          * @event beforehide
27904          * Fires before this menu is hidden
27905          * @param {Roo.bootstrap.menu.Menu} this
27906          */
27907         beforehide : true,
27908         /**
27909          * @event show
27910          * Fires after this menu is displayed
27911          * @param {Roo.bootstrap.menu.Menu} this
27912          */
27913         show : true,
27914         /**
27915          * @event hide
27916          * Fires after this menu is hidden
27917          * @param {Roo.bootstrap.menu.Menu} this
27918          */
27919         hide : true,
27920         /**
27921          * @event click
27922          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
27923          * @param {Roo.bootstrap.menu.Menu} this
27924          * @param {Roo.EventObject} e
27925          */
27926         click : true
27927     });
27928     
27929 };
27930
27931 Roo.extend(Roo.bootstrap.menu.Menu, Roo.bootstrap.Component,  {
27932     
27933     submenu : false,
27934     html : '',
27935     weight : 'default',
27936     icon : false,
27937     pos : 'bottom',
27938     
27939     
27940     getChildContainer : function() {
27941         if(this.isSubMenu){
27942             return this.el;
27943         }
27944         
27945         return this.el.select('ul.dropdown-menu', true).first();  
27946     },
27947     
27948     getAutoCreate : function()
27949     {
27950         var text = [
27951             {
27952                 tag : 'span',
27953                 cls : 'roo-menu-text',
27954                 html : this.html
27955             }
27956         ];
27957         
27958         if(this.icon){
27959             text.unshift({
27960                 tag : 'i',
27961                 cls : 'fa ' + this.icon
27962             })
27963         }
27964         
27965         
27966         var cfg = {
27967             tag : 'div',
27968             cls : 'btn-group',
27969             cn : [
27970                 {
27971                     tag : 'button',
27972                     cls : 'dropdown-button btn btn-' + this.weight,
27973                     cn : text
27974                 },
27975                 {
27976                     tag : 'button',
27977                     cls : 'dropdown-toggle btn btn-' + this.weight,
27978                     cn : [
27979                         {
27980                             tag : 'span',
27981                             cls : 'caret'
27982                         }
27983                     ]
27984                 },
27985                 {
27986                     tag : 'ul',
27987                     cls : 'dropdown-menu'
27988                 }
27989             ]
27990             
27991         };
27992         
27993         if(this.pos == 'top'){
27994             cfg.cls += ' dropup';
27995         }
27996         
27997         if(this.isSubMenu){
27998             cfg = {
27999                 tag : 'ul',
28000                 cls : 'dropdown-menu'
28001             }
28002         }
28003         
28004         return cfg;
28005     },
28006     
28007     onRender : function(ct, position)
28008     {
28009         this.isSubMenu = ct.hasClass('dropdown-submenu');
28010         
28011         Roo.bootstrap.menu.Menu.superclass.onRender.call(this, ct, position);
28012     },
28013     
28014     initEvents : function() 
28015     {
28016         if(this.isSubMenu){
28017             return;
28018         }
28019         
28020         this.hidden = true;
28021         
28022         this.triggerEl = this.el.select('button.dropdown-toggle', true).first();
28023         this.triggerEl.on('click', this.onTriggerPress, this);
28024         
28025         this.buttonEl = this.el.select('button.dropdown-button', true).first();
28026         this.buttonEl.on('click', this.onClick, this);
28027         
28028     },
28029     
28030     list : function()
28031     {
28032         if(this.isSubMenu){
28033             return this.el;
28034         }
28035         
28036         return this.el.select('ul.dropdown-menu', true).first();
28037     },
28038     
28039     onClick : function(e)
28040     {
28041         this.fireEvent("click", this, e);
28042     },
28043     
28044     onTriggerPress  : function(e)
28045     {   
28046         if (this.isVisible()) {
28047             this.hide();
28048         } else {
28049             this.show();
28050         }
28051     },
28052     
28053     isVisible : function(){
28054         return !this.hidden;
28055     },
28056     
28057     show : function()
28058     {
28059         this.fireEvent("beforeshow", this);
28060         
28061         this.hidden = false;
28062         this.el.addClass('open');
28063         
28064         Roo.get(document).on("mouseup", this.onMouseUp, this);
28065         
28066         this.fireEvent("show", this);
28067         
28068         
28069     },
28070     
28071     hide : function()
28072     {
28073         this.fireEvent("beforehide", this);
28074         
28075         this.hidden = true;
28076         this.el.removeClass('open');
28077         
28078         Roo.get(document).un("mouseup", this.onMouseUp);
28079         
28080         this.fireEvent("hide", this);
28081     },
28082     
28083     onMouseUp : function()
28084     {
28085         this.hide();
28086     }
28087     
28088 });
28089
28090  
28091  /*
28092  * - LGPL
28093  *
28094  * menu item
28095  * 
28096  */
28097 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
28098
28099 /**
28100  * @class Roo.bootstrap.menu.Item
28101  * @extends Roo.bootstrap.Component
28102  * Bootstrap MenuItem class
28103  * @cfg {Boolean} submenu (true | false) default false
28104  * @cfg {String} html text of the item
28105  * @cfg {String} href the link
28106  * @cfg {Boolean} disable (true | false) default false
28107  * @cfg {Boolean} preventDefault (true | false) default true
28108  * @cfg {String} icon Font awesome icon
28109  * @cfg {String} pos Submenu align to (left | right) default right 
28110  * 
28111  * 
28112  * @constructor
28113  * Create a new Item
28114  * @param {Object} config The config object
28115  */
28116
28117
28118 Roo.bootstrap.menu.Item = function(config){
28119     Roo.bootstrap.menu.Item.superclass.constructor.call(this, config);
28120     this.addEvents({
28121         /**
28122          * @event mouseover
28123          * Fires when the mouse is hovering over this menu
28124          * @param {Roo.bootstrap.menu.Item} this
28125          * @param {Roo.EventObject} e
28126          */
28127         mouseover : true,
28128         /**
28129          * @event mouseout
28130          * Fires when the mouse exits this menu
28131          * @param {Roo.bootstrap.menu.Item} this
28132          * @param {Roo.EventObject} e
28133          */
28134         mouseout : true,
28135         // raw events
28136         /**
28137          * @event click
28138          * The raw click event for the entire grid.
28139          * @param {Roo.EventObject} e
28140          */
28141         click : true
28142     });
28143 };
28144
28145 Roo.extend(Roo.bootstrap.menu.Item, Roo.bootstrap.Component,  {
28146     
28147     submenu : false,
28148     href : '',
28149     html : '',
28150     preventDefault: true,
28151     disable : false,
28152     icon : false,
28153     pos : 'right',
28154     
28155     getAutoCreate : function()
28156     {
28157         var text = [
28158             {
28159                 tag : 'span',
28160                 cls : 'roo-menu-item-text',
28161                 html : this.html
28162             }
28163         ];
28164         
28165         if(this.icon){
28166             text.unshift({
28167                 tag : 'i',
28168                 cls : 'fa ' + this.icon
28169             })
28170         }
28171         
28172         var cfg = {
28173             tag : 'li',
28174             cn : [
28175                 {
28176                     tag : 'a',
28177                     href : this.href || '#',
28178                     cn : text
28179                 }
28180             ]
28181         };
28182         
28183         if(this.disable){
28184             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'disabled' : (cfg.cls + ' disabled');
28185         }
28186         
28187         if(this.submenu){
28188             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'dropdown-submenu' : (cfg.cls + ' dropdown-submenu');
28189             
28190             if(this.pos == 'left'){
28191                 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'pull-left' : (cfg.cls + ' pull-left');
28192             }
28193         }
28194         
28195         return cfg;
28196     },
28197     
28198     initEvents : function() 
28199     {
28200         this.el.on('mouseover', this.onMouseOver, this);
28201         this.el.on('mouseout', this.onMouseOut, this);
28202         
28203         this.el.select('a', true).first().on('click', this.onClick, this);
28204         
28205     },
28206     
28207     onClick : function(e)
28208     {
28209         if(this.preventDefault){
28210             e.preventDefault();
28211         }
28212         
28213         this.fireEvent("click", this, e);
28214     },
28215     
28216     onMouseOver : function(e)
28217     {
28218         if(this.submenu && this.pos == 'left'){
28219             this.el.select('ul.dropdown-menu', true).first().setLeft(this.el.select('ul.dropdown-menu', true).first().getWidth() * -1);
28220         }
28221         
28222         this.fireEvent("mouseover", this, e);
28223     },
28224     
28225     onMouseOut : function(e)
28226     {
28227         this.fireEvent("mouseout", this, e);
28228     }
28229 });
28230
28231  
28232
28233  /*
28234  * - LGPL
28235  *
28236  * menu separator
28237  * 
28238  */
28239 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
28240
28241 /**
28242  * @class Roo.bootstrap.menu.Separator
28243  * @extends Roo.bootstrap.Component
28244  * Bootstrap Separator class
28245  * 
28246  * @constructor
28247  * Create a new Separator
28248  * @param {Object} config The config object
28249  */
28250
28251
28252 Roo.bootstrap.menu.Separator = function(config){
28253     Roo.bootstrap.menu.Separator.superclass.constructor.call(this, config);
28254 };
28255
28256 Roo.extend(Roo.bootstrap.menu.Separator, Roo.bootstrap.Component,  {
28257     
28258     getAutoCreate : function(){
28259         var cfg = {
28260             tag : 'li',
28261             cls: 'divider'
28262         };
28263         
28264         return cfg;
28265     }
28266    
28267 });
28268
28269  
28270
28271  /*
28272  * - LGPL
28273  *
28274  * Tooltip
28275  * 
28276  */
28277
28278 /**
28279  * @class Roo.bootstrap.Tooltip
28280  * Bootstrap Tooltip class
28281  * This is basic at present - all componets support it by default, however they should add tooltipEl() method
28282  * to determine which dom element triggers the tooltip.
28283  * 
28284  * It needs to add support for additional attributes like tooltip-position
28285  * 
28286  * @constructor
28287  * Create a new Toolti
28288  * @param {Object} config The config object
28289  */
28290
28291 Roo.bootstrap.Tooltip = function(config){
28292     Roo.bootstrap.Tooltip.superclass.constructor.call(this, config);
28293     
28294     this.alignment = Roo.bootstrap.Tooltip.alignment;
28295     
28296     if(typeof(config) != 'undefined' && typeof(config.alignment) != 'undefined'){
28297         this.alignment = config.alignment;
28298     }
28299     
28300 };
28301
28302 Roo.apply(Roo.bootstrap.Tooltip, {
28303     /**
28304      * @function init initialize tooltip monitoring.
28305      * @static
28306      */
28307     currentEl : false,
28308     currentTip : false,
28309     currentRegion : false,
28310     
28311     //  init : delay?
28312     
28313     init : function()
28314     {
28315         Roo.get(document).on('mouseover', this.enter ,this);
28316         Roo.get(document).on('mouseout', this.leave, this);
28317          
28318         
28319         this.currentTip = new Roo.bootstrap.Tooltip();
28320     },
28321     
28322     enter : function(ev)
28323     {
28324         var dom = ev.getTarget();
28325         
28326         //Roo.log(['enter',dom]);
28327         var el = Roo.fly(dom);
28328         if (this.currentEl) {
28329             //Roo.log(dom);
28330             //Roo.log(this.currentEl);
28331             //Roo.log(this.currentEl.contains(dom));
28332             if (this.currentEl == el) {
28333                 return;
28334             }
28335             if (dom != this.currentEl.dom && this.currentEl.contains(dom)) {
28336                 return;
28337             }
28338
28339         }
28340         
28341         if (this.currentTip.el) {
28342             this.currentTip.el.setVisibilityMode(Roo.Element.DISPLAY).hide(); // force hiding...
28343         }    
28344         //Roo.log(ev);
28345         
28346         if(!el || el.dom == document){
28347             return;
28348         }
28349         
28350         var bindEl = el;
28351         
28352         // you can not look for children, as if el is the body.. then everythign is the child..
28353         if (!el.attr('tooltip')) { //
28354             if (!el.select("[tooltip]").elements.length) {
28355                 return;
28356             }
28357             // is the mouse over this child...?
28358             bindEl = el.select("[tooltip]").first();
28359             var xy = ev.getXY();
28360             if (!bindEl.getRegion().contains( { top : xy[1] ,right : xy[0] , bottom : xy[1], left : xy[0]})) {
28361                 //Roo.log("not in region.");
28362                 return;
28363             }
28364             //Roo.log("child element over..");
28365             
28366         }
28367         this.currentEl = bindEl;
28368         this.currentTip.bind(bindEl);
28369         this.currentRegion = Roo.lib.Region.getRegion(dom);
28370         this.currentTip.enter();
28371         
28372     },
28373     leave : function(ev)
28374     {
28375         var dom = ev.getTarget();
28376         //Roo.log(['leave',dom]);
28377         if (!this.currentEl) {
28378             return;
28379         }
28380         
28381         
28382         if (dom != this.currentEl.dom) {
28383             return;
28384         }
28385         var xy = ev.getXY();
28386         if (this.currentRegion.contains( new Roo.lib.Region( xy[1], xy[0] ,xy[1], xy[0]  ))) {
28387             return;
28388         }
28389         // only activate leave if mouse cursor is outside... bounding box..
28390         
28391         
28392         
28393         
28394         if (this.currentTip) {
28395             this.currentTip.leave();
28396         }
28397         //Roo.log('clear currentEl');
28398         this.currentEl = false;
28399         
28400         
28401     },
28402     alignment : {
28403         'left' : ['r-l', [-2,0], 'right'],
28404         'right' : ['l-r', [2,0], 'left'],
28405         'bottom' : ['t-b', [0,2], 'top'],
28406         'top' : [ 'b-t', [0,-2], 'bottom']
28407     }
28408     
28409 });
28410
28411
28412 Roo.extend(Roo.bootstrap.Tooltip, Roo.bootstrap.Component,  {
28413     
28414     
28415     bindEl : false,
28416     
28417     delay : null, // can be { show : 300 , hide: 500}
28418     
28419     timeout : null,
28420     
28421     hoverState : null, //???
28422     
28423     placement : 'bottom', 
28424     
28425     alignment : false,
28426     
28427     getAutoCreate : function(){
28428     
28429         var cfg = {
28430            cls : 'tooltip',   
28431            role : 'tooltip',
28432            cn : [
28433                 {
28434                     cls : 'tooltip-arrow arrow'
28435                 },
28436                 {
28437                     cls : 'tooltip-inner'
28438                 }
28439            ]
28440         };
28441         
28442         return cfg;
28443     },
28444     bind : function(el)
28445     {
28446         this.bindEl = el;
28447     },
28448     
28449     initEvents : function()
28450     {
28451         this.arrowEl = this.el.select('.arrow', true).first();
28452         this.innerEl = this.el.select('.tooltip-inner', true).first();
28453     },
28454     
28455     enter : function () {
28456        
28457         if (this.timeout != null) {
28458             clearTimeout(this.timeout);
28459         }
28460         
28461         this.hoverState = 'in';
28462          //Roo.log("enter - show");
28463         if (!this.delay || !this.delay.show) {
28464             this.show();
28465             return;
28466         }
28467         var _t = this;
28468         this.timeout = setTimeout(function () {
28469             if (_t.hoverState == 'in') {
28470                 _t.show();
28471             }
28472         }, this.delay.show);
28473     },
28474     leave : function()
28475     {
28476         clearTimeout(this.timeout);
28477     
28478         this.hoverState = 'out';
28479          if (!this.delay || !this.delay.hide) {
28480             this.hide();
28481             return;
28482         }
28483        
28484         var _t = this;
28485         this.timeout = setTimeout(function () {
28486             //Roo.log("leave - timeout");
28487             
28488             if (_t.hoverState == 'out') {
28489                 _t.hide();
28490                 Roo.bootstrap.Tooltip.currentEl = false;
28491             }
28492         }, delay);
28493     },
28494     
28495     show : function (msg)
28496     {
28497         if (!this.el) {
28498             this.render(document.body);
28499         }
28500         // set content.
28501         //Roo.log([this.bindEl, this.bindEl.attr('tooltip')]);
28502         
28503         var tip = msg || this.bindEl.attr('tooltip') || this.bindEl.select("[tooltip]").first().attr('tooltip');
28504         
28505         this.el.select('.tooltip-inner',true).first().dom.innerHTML = tip;
28506         
28507         this.el.removeClass(['fade','top','bottom', 'left', 'right','in',
28508                              'bs-tooltip-top','bs-tooltip-bottom', 'bs-tooltip-left', 'bs-tooltip-right']);
28509         
28510         var placement = typeof this.placement == 'function' ?
28511             this.placement.call(this, this.el, on_el) :
28512             this.placement;
28513             
28514         var autoToken = /\s?auto?\s?/i;
28515         var autoPlace = autoToken.test(placement);
28516         if (autoPlace) {
28517             placement = placement.replace(autoToken, '') || 'top';
28518         }
28519         
28520         //this.el.detach()
28521         //this.el.setXY([0,0]);
28522         this.el.show();
28523         //this.el.dom.style.display='block';
28524         
28525         //this.el.appendTo(on_el);
28526         
28527         var p = this.getPosition();
28528         var box = this.el.getBox();
28529         
28530         if (autoPlace) {
28531             // fixme..
28532         }
28533         
28534         var align = this.alignment[placement];
28535         
28536         var xy = this.el.getAlignToXY(this.bindEl, align[0], align[1]);
28537         
28538         if(placement == 'top' || placement == 'bottom'){
28539             if(xy[0] < 0){
28540                 placement = 'right';
28541             }
28542             
28543             if(xy[0] + this.el.getWidth() > Roo.lib.Dom.getViewWidth()){
28544                 placement = 'left';
28545             }
28546             
28547             var scroll = Roo.select('body', true).first().getScroll();
28548             
28549             if(xy[1] > Roo.lib.Dom.getViewHeight() + scroll.top - this.el.getHeight()){
28550                 placement = 'top';
28551             }
28552             
28553             align = this.alignment[placement];
28554             
28555             this.arrowEl.setLeft((this.innerEl.getWidth()/2) - 5);
28556             
28557         }
28558         
28559         this.el.alignTo(this.bindEl, align[0],align[1]);
28560         //var arrow = this.el.select('.arrow',true).first();
28561         //arrow.set(align[2], 
28562         
28563         this.el.addClass(placement);
28564         this.el.addClass("bs-tooltip-"+ placement);
28565         
28566         this.el.addClass('in fade show');
28567         
28568         this.hoverState = null;
28569         
28570         if (this.el.hasClass('fade')) {
28571             // fade it?
28572         }
28573         
28574         
28575         
28576         
28577         
28578     },
28579     hide : function()
28580     {
28581          
28582         if (!this.el) {
28583             return;
28584         }
28585         //this.el.setXY([0,0]);
28586         this.el.removeClass(['show', 'in']);
28587         //this.el.hide();
28588         
28589     }
28590     
28591 });
28592  
28593
28594  /*
28595  * - LGPL
28596  *
28597  * Location Picker
28598  * 
28599  */
28600
28601 /**
28602  * @class Roo.bootstrap.LocationPicker
28603  * @extends Roo.bootstrap.Component
28604  * Bootstrap LocationPicker class
28605  * @cfg {Number} latitude Position when init default 0
28606  * @cfg {Number} longitude Position when init default 0
28607  * @cfg {Number} zoom default 15
28608  * @cfg {String} mapTypeId default google.maps.MapTypeId.ROADMAP
28609  * @cfg {Boolean} mapTypeControl default false
28610  * @cfg {Boolean} disableDoubleClickZoom default false
28611  * @cfg {Boolean} scrollwheel default true
28612  * @cfg {Boolean} streetViewControl default false
28613  * @cfg {Number} radius default 0
28614  * @cfg {String} locationName
28615  * @cfg {Boolean} draggable default true
28616  * @cfg {Boolean} enableAutocomplete default false
28617  * @cfg {Boolean} enableReverseGeocode default true
28618  * @cfg {String} markerTitle
28619  * 
28620  * @constructor
28621  * Create a new LocationPicker
28622  * @param {Object} config The config object
28623  */
28624
28625
28626 Roo.bootstrap.LocationPicker = function(config){
28627     
28628     Roo.bootstrap.LocationPicker.superclass.constructor.call(this, config);
28629     
28630     this.addEvents({
28631         /**
28632          * @event initial
28633          * Fires when the picker initialized.
28634          * @param {Roo.bootstrap.LocationPicker} this
28635          * @param {Google Location} location
28636          */
28637         initial : true,
28638         /**
28639          * @event positionchanged
28640          * Fires when the picker position changed.
28641          * @param {Roo.bootstrap.LocationPicker} this
28642          * @param {Google Location} location
28643          */
28644         positionchanged : true,
28645         /**
28646          * @event resize
28647          * Fires when the map resize.
28648          * @param {Roo.bootstrap.LocationPicker} this
28649          */
28650         resize : true,
28651         /**
28652          * @event show
28653          * Fires when the map show.
28654          * @param {Roo.bootstrap.LocationPicker} this
28655          */
28656         show : true,
28657         /**
28658          * @event hide
28659          * Fires when the map hide.
28660          * @param {Roo.bootstrap.LocationPicker} this
28661          */
28662         hide : true,
28663         /**
28664          * @event mapClick
28665          * Fires when click the map.
28666          * @param {Roo.bootstrap.LocationPicker} this
28667          * @param {Map event} e
28668          */
28669         mapClick : true,
28670         /**
28671          * @event mapRightClick
28672          * Fires when right click the map.
28673          * @param {Roo.bootstrap.LocationPicker} this
28674          * @param {Map event} e
28675          */
28676         mapRightClick : true,
28677         /**
28678          * @event markerClick
28679          * Fires when click the marker.
28680          * @param {Roo.bootstrap.LocationPicker} this
28681          * @param {Map event} e
28682          */
28683         markerClick : true,
28684         /**
28685          * @event markerRightClick
28686          * Fires when right click the marker.
28687          * @param {Roo.bootstrap.LocationPicker} this
28688          * @param {Map event} e
28689          */
28690         markerRightClick : true,
28691         /**
28692          * @event OverlayViewDraw
28693          * Fires when OverlayView Draw
28694          * @param {Roo.bootstrap.LocationPicker} this
28695          */
28696         OverlayViewDraw : true,
28697         /**
28698          * @event OverlayViewOnAdd
28699          * Fires when OverlayView Draw
28700          * @param {Roo.bootstrap.LocationPicker} this
28701          */
28702         OverlayViewOnAdd : true,
28703         /**
28704          * @event OverlayViewOnRemove
28705          * Fires when OverlayView Draw
28706          * @param {Roo.bootstrap.LocationPicker} this
28707          */
28708         OverlayViewOnRemove : true,
28709         /**
28710          * @event OverlayViewShow
28711          * Fires when OverlayView Draw
28712          * @param {Roo.bootstrap.LocationPicker} this
28713          * @param {Pixel} cpx
28714          */
28715         OverlayViewShow : true,
28716         /**
28717          * @event OverlayViewHide
28718          * Fires when OverlayView Draw
28719          * @param {Roo.bootstrap.LocationPicker} this
28720          */
28721         OverlayViewHide : true,
28722         /**
28723          * @event loadexception
28724          * Fires when load google lib failed.
28725          * @param {Roo.bootstrap.LocationPicker} this
28726          */
28727         loadexception : true
28728     });
28729         
28730 };
28731
28732 Roo.extend(Roo.bootstrap.LocationPicker, Roo.bootstrap.Component,  {
28733     
28734     gMapContext: false,
28735     
28736     latitude: 0,
28737     longitude: 0,
28738     zoom: 15,
28739     mapTypeId: false,
28740     mapTypeControl: false,
28741     disableDoubleClickZoom: false,
28742     scrollwheel: true,
28743     streetViewControl: false,
28744     radius: 0,
28745     locationName: '',
28746     draggable: true,
28747     enableAutocomplete: false,
28748     enableReverseGeocode: true,
28749     markerTitle: '',
28750     
28751     getAutoCreate: function()
28752     {
28753
28754         var cfg = {
28755             tag: 'div',
28756             cls: 'roo-location-picker'
28757         };
28758         
28759         return cfg
28760     },
28761     
28762     initEvents: function(ct, position)
28763     {       
28764         if(!this.el.getWidth() || this.isApplied()){
28765             return;
28766         }
28767         
28768         this.el.setVisibilityMode(Roo.Element.DISPLAY);
28769         
28770         this.initial();
28771     },
28772     
28773     initial: function()
28774     {
28775         if(typeof(google) == 'undefined' || typeof(google.maps) == 'undefined'){
28776             this.fireEvent('loadexception', this);
28777             return;
28778         }
28779         
28780         if(!this.mapTypeId){
28781             this.mapTypeId = google.maps.MapTypeId.ROADMAP;
28782         }
28783         
28784         this.gMapContext = this.GMapContext();
28785         
28786         this.initOverlayView();
28787         
28788         this.OverlayView = new Roo.bootstrap.LocationPicker.OverlayView(this.gMapContext.map);
28789         
28790         var _this = this;
28791                 
28792         google.maps.event.addListener(this.gMapContext.marker, "dragend", function(event) {
28793             _this.setPosition(_this.gMapContext.marker.position);
28794         });
28795         
28796         google.maps.event.addListener(this.gMapContext.map, 'click', function(event){
28797             _this.fireEvent('mapClick', this, event);
28798             
28799         });
28800
28801         google.maps.event.addListener(this.gMapContext.map, 'rightclick', function(event){
28802             _this.fireEvent('mapRightClick', this, event);
28803             
28804         });
28805         
28806         google.maps.event.addListener(this.gMapContext.marker, 'click', function(event){
28807             _this.fireEvent('markerClick', this, event);
28808             
28809         });
28810
28811         google.maps.event.addListener(this.gMapContext.marker, 'rightclick', function(event){
28812             _this.fireEvent('markerRightClick', this, event);
28813             
28814         });
28815         
28816         this.setPosition(this.gMapContext.location);
28817         
28818         this.fireEvent('initial', this, this.gMapContext.location);
28819     },
28820     
28821     initOverlayView: function()
28822     {
28823         var _this = this;
28824         
28825         Roo.bootstrap.LocationPicker.OverlayView.prototype = Roo.apply(new google.maps.OverlayView(), {
28826             
28827             draw: function()
28828             {
28829                 _this.fireEvent('OverlayViewDraw', _this);
28830             },
28831             
28832             onAdd: function()
28833             {
28834                 _this.fireEvent('OverlayViewOnAdd', _this);
28835             },
28836             
28837             onRemove: function()
28838             {
28839                 _this.fireEvent('OverlayViewOnRemove', _this);
28840             },
28841             
28842             show: function(cpx)
28843             {
28844                 _this.fireEvent('OverlayViewShow', _this, cpx);
28845             },
28846             
28847             hide: function()
28848             {
28849                 _this.fireEvent('OverlayViewHide', _this);
28850             }
28851             
28852         });
28853     },
28854     
28855     fromLatLngToContainerPixel: function(event)
28856     {
28857         return this.OverlayView.getProjection().fromLatLngToContainerPixel(event.latLng);
28858     },
28859     
28860     isApplied: function() 
28861     {
28862         return this.getGmapContext() == false ? false : true;
28863     },
28864     
28865     getGmapContext: function() 
28866     {
28867         return (typeof(this.gMapContext) == 'undefined') ? false : this.gMapContext;
28868     },
28869     
28870     GMapContext: function() 
28871     {
28872         var position = new google.maps.LatLng(this.latitude, this.longitude);
28873         
28874         var _map = new google.maps.Map(this.el.dom, {
28875             center: position,
28876             zoom: this.zoom,
28877             mapTypeId: this.mapTypeId,
28878             mapTypeControl: this.mapTypeControl,
28879             disableDoubleClickZoom: this.disableDoubleClickZoom,
28880             scrollwheel: this.scrollwheel,
28881             streetViewControl: this.streetViewControl,
28882             locationName: this.locationName,
28883             draggable: this.draggable,
28884             enableAutocomplete: this.enableAutocomplete,
28885             enableReverseGeocode: this.enableReverseGeocode
28886         });
28887         
28888         var _marker = new google.maps.Marker({
28889             position: position,
28890             map: _map,
28891             title: this.markerTitle,
28892             draggable: this.draggable
28893         });
28894         
28895         return {
28896             map: _map,
28897             marker: _marker,
28898             circle: null,
28899             location: position,
28900             radius: this.radius,
28901             locationName: this.locationName,
28902             addressComponents: {
28903                 formatted_address: null,
28904                 addressLine1: null,
28905                 addressLine2: null,
28906                 streetName: null,
28907                 streetNumber: null,
28908                 city: null,
28909                 district: null,
28910                 state: null,
28911                 stateOrProvince: null
28912             },
28913             settings: this,
28914             domContainer: this.el.dom,
28915             geodecoder: new google.maps.Geocoder()
28916         };
28917     },
28918     
28919     drawCircle: function(center, radius, options) 
28920     {
28921         if (this.gMapContext.circle != null) {
28922             this.gMapContext.circle.setMap(null);
28923         }
28924         if (radius > 0) {
28925             radius *= 1;
28926             options = Roo.apply({}, options, {
28927                 strokeColor: "#0000FF",
28928                 strokeOpacity: .35,
28929                 strokeWeight: 2,
28930                 fillColor: "#0000FF",
28931                 fillOpacity: .2
28932             });
28933             
28934             options.map = this.gMapContext.map;
28935             options.radius = radius;
28936             options.center = center;
28937             this.gMapContext.circle = new google.maps.Circle(options);
28938             return this.gMapContext.circle;
28939         }
28940         
28941         return null;
28942     },
28943     
28944     setPosition: function(location) 
28945     {
28946         this.gMapContext.location = location;
28947         this.gMapContext.marker.setPosition(location);
28948         this.gMapContext.map.panTo(location);
28949         this.drawCircle(location, this.gMapContext.radius, {});
28950         
28951         var _this = this;
28952         
28953         if (this.gMapContext.settings.enableReverseGeocode) {
28954             this.gMapContext.geodecoder.geocode({
28955                 latLng: this.gMapContext.location
28956             }, function(results, status) {
28957                 
28958                 if (status == google.maps.GeocoderStatus.OK && results.length > 0) {
28959                     _this.gMapContext.locationName = results[0].formatted_address;
28960                     _this.gMapContext.addressComponents = _this.address_component_from_google_geocode(results[0].address_components);
28961                     
28962                     _this.fireEvent('positionchanged', this, location);
28963                 }
28964             });
28965             
28966             return;
28967         }
28968         
28969         this.fireEvent('positionchanged', this, location);
28970     },
28971     
28972     resize: function()
28973     {
28974         google.maps.event.trigger(this.gMapContext.map, "resize");
28975         
28976         this.gMapContext.map.setCenter(this.gMapContext.marker.position);
28977         
28978         this.fireEvent('resize', this);
28979     },
28980     
28981     setPositionByLatLng: function(latitude, longitude)
28982     {
28983         this.setPosition(new google.maps.LatLng(latitude, longitude));
28984     },
28985     
28986     getCurrentPosition: function() 
28987     {
28988         return {
28989             latitude: this.gMapContext.location.lat(),
28990             longitude: this.gMapContext.location.lng()
28991         };
28992     },
28993     
28994     getAddressName: function() 
28995     {
28996         return this.gMapContext.locationName;
28997     },
28998     
28999     getAddressComponents: function() 
29000     {
29001         return this.gMapContext.addressComponents;
29002     },
29003     
29004     address_component_from_google_geocode: function(address_components) 
29005     {
29006         var result = {};
29007         
29008         for (var i = 0; i < address_components.length; i++) {
29009             var component = address_components[i];
29010             if (component.types.indexOf("postal_code") >= 0) {
29011                 result.postalCode = component.short_name;
29012             } else if (component.types.indexOf("street_number") >= 0) {
29013                 result.streetNumber = component.short_name;
29014             } else if (component.types.indexOf("route") >= 0) {
29015                 result.streetName = component.short_name;
29016             } else if (component.types.indexOf("neighborhood") >= 0) {
29017                 result.city = component.short_name;
29018             } else if (component.types.indexOf("locality") >= 0) {
29019                 result.city = component.short_name;
29020             } else if (component.types.indexOf("sublocality") >= 0) {
29021                 result.district = component.short_name;
29022             } else if (component.types.indexOf("administrative_area_level_1") >= 0) {
29023                 result.stateOrProvince = component.short_name;
29024             } else if (component.types.indexOf("country") >= 0) {
29025                 result.country = component.short_name;
29026             }
29027         }
29028         
29029         result.addressLine1 = [ result.streetNumber, result.streetName ].join(" ").trim();
29030         result.addressLine2 = "";
29031         return result;
29032     },
29033     
29034     setZoomLevel: function(zoom)
29035     {
29036         this.gMapContext.map.setZoom(zoom);
29037     },
29038     
29039     show: function()
29040     {
29041         if(!this.el){
29042             return;
29043         }
29044         
29045         this.el.show();
29046         
29047         this.resize();
29048         
29049         this.fireEvent('show', this);
29050     },
29051     
29052     hide: function()
29053     {
29054         if(!this.el){
29055             return;
29056         }
29057         
29058         this.el.hide();
29059         
29060         this.fireEvent('hide', this);
29061     }
29062     
29063 });
29064
29065 Roo.apply(Roo.bootstrap.LocationPicker, {
29066     
29067     OverlayView : function(map, options)
29068     {
29069         options = options || {};
29070         
29071         this.setMap(map);
29072     }
29073     
29074     
29075 });/**
29076  * @class Roo.bootstrap.Alert
29077  * @extends Roo.bootstrap.Component
29078  * Bootstrap Alert class - shows an alert area box
29079  * eg
29080  * <div class="alert alert-danger" role="alert"><span class="fa fa-exclamation-triangle"></span><span class="sr-only">Error:</span>
29081   Enter a valid email address
29082 </div>
29083  * @licence LGPL
29084  * @cfg {String} title The title of alert
29085  * @cfg {String} html The content of alert
29086  * @cfg {String} weight (  success | info | warning | danger )
29087  * @cfg {String} faicon font-awesomeicon
29088  * 
29089  * @constructor
29090  * Create a new alert
29091  * @param {Object} config The config object
29092  */
29093
29094
29095 Roo.bootstrap.Alert = function(config){
29096     Roo.bootstrap.Alert.superclass.constructor.call(this, config);
29097     
29098 };
29099
29100 Roo.extend(Roo.bootstrap.Alert, Roo.bootstrap.Component,  {
29101     
29102     title: '',
29103     html: '',
29104     weight: false,
29105     faicon: false,
29106     
29107     getAutoCreate : function()
29108     {
29109         
29110         var cfg = {
29111             tag : 'div',
29112             cls : 'alert',
29113             cn : [
29114                 {
29115                     tag : 'i',
29116                     cls : 'roo-alert-icon'
29117                     
29118                 },
29119                 {
29120                     tag : 'b',
29121                     cls : 'roo-alert-title',
29122                     html : this.title
29123                 },
29124                 {
29125                     tag : 'span',
29126                     cls : 'roo-alert-text',
29127                     html : this.html
29128                 }
29129             ]
29130         };
29131         
29132         if(this.faicon){
29133             cfg.cn[0].cls += ' fa ' + this.faicon;
29134         }
29135         
29136         if(this.weight){
29137             cfg.cls += ' alert-' + this.weight;
29138         }
29139         
29140         return cfg;
29141     },
29142     
29143     initEvents: function() 
29144     {
29145         this.el.setVisibilityMode(Roo.Element.DISPLAY);
29146     },
29147     
29148     setTitle : function(str)
29149     {
29150         this.el.select('.roo-alert-title',true).first().dom.innerHTML = str;
29151     },
29152     
29153     setText : function(str)
29154     {
29155         this.el.select('.roo-alert-text',true).first().dom.innerHTML = str;
29156     },
29157     
29158     setWeight : function(weight)
29159     {
29160         if(this.weight){
29161             this.el.select('.alert',true).first().removeClass('alert-' + this.weight);
29162         }
29163         
29164         this.weight = weight;
29165         
29166         this.el.select('.alert',true).first().addClass('alert-' + this.weight);
29167     },
29168     
29169     setIcon : function(icon)
29170     {
29171         if(this.faicon){
29172             this.el.select('.roo-alert-icon',true).first().removeClass(['fa', 'fa-' + this.faicon]);
29173         }
29174         
29175         this.faicon = icon;
29176         
29177         this.el.select('.roo-alert-icon',true).first().addClass(['fa', 'fa-' + this.faicon]);
29178     },
29179     
29180     hide: function() 
29181     {
29182         this.el.hide();   
29183     },
29184     
29185     show: function() 
29186     {  
29187         this.el.show();   
29188     }
29189     
29190 });
29191
29192  
29193 /*
29194 * Licence: LGPL
29195 */
29196
29197 /**
29198  * @class Roo.bootstrap.UploadCropbox
29199  * @extends Roo.bootstrap.Component
29200  * Bootstrap UploadCropbox class
29201  * @cfg {String} emptyText show when image has been loaded
29202  * @cfg {String} rotateNotify show when image too small to rotate
29203  * @cfg {Number} errorTimeout default 3000
29204  * @cfg {Number} minWidth default 300
29205  * @cfg {Number} minHeight default 300
29206  * @cfg {Array} buttons default ['rotateLeft', 'pictureBtn', 'rotateRight']
29207  * @cfg {Boolean} isDocument (true|false) default false
29208  * @cfg {String} url action url
29209  * @cfg {String} paramName default 'imageUpload'
29210  * @cfg {String} method default POST
29211  * @cfg {Boolean} loadMask (true|false) default true
29212  * @cfg {Boolean} loadingText default 'Loading...'
29213  * 
29214  * @constructor
29215  * Create a new UploadCropbox
29216  * @param {Object} config The config object
29217  */
29218
29219 Roo.bootstrap.UploadCropbox = function(config){
29220     Roo.bootstrap.UploadCropbox.superclass.constructor.call(this, config);
29221     
29222     this.addEvents({
29223         /**
29224          * @event beforeselectfile
29225          * Fire before select file
29226          * @param {Roo.bootstrap.UploadCropbox} this
29227          */
29228         "beforeselectfile" : true,
29229         /**
29230          * @event initial
29231          * Fire after initEvent
29232          * @param {Roo.bootstrap.UploadCropbox} this
29233          */
29234         "initial" : true,
29235         /**
29236          * @event crop
29237          * Fire after initEvent
29238          * @param {Roo.bootstrap.UploadCropbox} this
29239          * @param {String} data
29240          */
29241         "crop" : true,
29242         /**
29243          * @event prepare
29244          * Fire when preparing the file data
29245          * @param {Roo.bootstrap.UploadCropbox} this
29246          * @param {Object} file
29247          */
29248         "prepare" : true,
29249         /**
29250          * @event exception
29251          * Fire when get exception
29252          * @param {Roo.bootstrap.UploadCropbox} this
29253          * @param {XMLHttpRequest} xhr
29254          */
29255         "exception" : true,
29256         /**
29257          * @event beforeloadcanvas
29258          * Fire before load the canvas
29259          * @param {Roo.bootstrap.UploadCropbox} this
29260          * @param {String} src
29261          */
29262         "beforeloadcanvas" : true,
29263         /**
29264          * @event trash
29265          * Fire when trash image
29266          * @param {Roo.bootstrap.UploadCropbox} this
29267          */
29268         "trash" : true,
29269         /**
29270          * @event download
29271          * Fire when download the image
29272          * @param {Roo.bootstrap.UploadCropbox} this
29273          */
29274         "download" : true,
29275         /**
29276          * @event footerbuttonclick
29277          * Fire when footerbuttonclick
29278          * @param {Roo.bootstrap.UploadCropbox} this
29279          * @param {String} type
29280          */
29281         "footerbuttonclick" : true,
29282         /**
29283          * @event resize
29284          * Fire when resize
29285          * @param {Roo.bootstrap.UploadCropbox} this
29286          */
29287         "resize" : true,
29288         /**
29289          * @event rotate
29290          * Fire when rotate the image
29291          * @param {Roo.bootstrap.UploadCropbox} this
29292          * @param {String} pos
29293          */
29294         "rotate" : true,
29295         /**
29296          * @event inspect
29297          * Fire when inspect the file
29298          * @param {Roo.bootstrap.UploadCropbox} this
29299          * @param {Object} file
29300          */
29301         "inspect" : true,
29302         /**
29303          * @event upload
29304          * Fire when xhr upload the file
29305          * @param {Roo.bootstrap.UploadCropbox} this
29306          * @param {Object} data
29307          */
29308         "upload" : true,
29309         /**
29310          * @event arrange
29311          * Fire when arrange the file data
29312          * @param {Roo.bootstrap.UploadCropbox} this
29313          * @param {Object} formData
29314          */
29315         "arrange" : true
29316     });
29317     
29318     this.buttons = this.buttons || Roo.bootstrap.UploadCropbox.footer.STANDARD;
29319 };
29320
29321 Roo.extend(Roo.bootstrap.UploadCropbox, Roo.bootstrap.Component,  {
29322     
29323     emptyText : 'Click to upload image',
29324     rotateNotify : 'Image is too small to rotate',
29325     errorTimeout : 3000,
29326     scale : 0,
29327     baseScale : 1,
29328     rotate : 0,
29329     dragable : false,
29330     pinching : false,
29331     mouseX : 0,
29332     mouseY : 0,
29333     cropData : false,
29334     minWidth : 300,
29335     minHeight : 300,
29336     file : false,
29337     exif : {},
29338     baseRotate : 1,
29339     cropType : 'image/jpeg',
29340     buttons : false,
29341     canvasLoaded : false,
29342     isDocument : false,
29343     method : 'POST',
29344     paramName : 'imageUpload',
29345     loadMask : true,
29346     loadingText : 'Loading...',
29347     maskEl : false,
29348     
29349     getAutoCreate : function()
29350     {
29351         var cfg = {
29352             tag : 'div',
29353             cls : 'roo-upload-cropbox',
29354             cn : [
29355                 {
29356                     tag : 'input',
29357                     cls : 'roo-upload-cropbox-selector',
29358                     type : 'file'
29359                 },
29360                 {
29361                     tag : 'div',
29362                     cls : 'roo-upload-cropbox-body',
29363                     style : 'cursor:pointer',
29364                     cn : [
29365                         {
29366                             tag : 'div',
29367                             cls : 'roo-upload-cropbox-preview'
29368                         },
29369                         {
29370                             tag : 'div',
29371                             cls : 'roo-upload-cropbox-thumb'
29372                         },
29373                         {
29374                             tag : 'div',
29375                             cls : 'roo-upload-cropbox-empty-notify',
29376                             html : this.emptyText
29377                         },
29378                         {
29379                             tag : 'div',
29380                             cls : 'roo-upload-cropbox-error-notify alert alert-danger',
29381                             html : this.rotateNotify
29382                         }
29383                     ]
29384                 },
29385                 {
29386                     tag : 'div',
29387                     cls : 'roo-upload-cropbox-footer',
29388                     cn : {
29389                         tag : 'div',
29390                         cls : 'btn-group btn-group-justified roo-upload-cropbox-btn-group',
29391                         cn : []
29392                     }
29393                 }
29394             ]
29395         };
29396         
29397         return cfg;
29398     },
29399     
29400     onRender : function(ct, position)
29401     {
29402         Roo.bootstrap.UploadCropbox.superclass.onRender.call(this, ct, position);
29403         
29404         if (this.buttons.length) {
29405             
29406             Roo.each(this.buttons, function(bb) {
29407                 
29408                 var btn = this.el.select('.roo-upload-cropbox-footer div.roo-upload-cropbox-btn-group').first().createChild(bb);
29409                 
29410                 btn.on('click', this.onFooterButtonClick.createDelegate(this, [bb.action], true));
29411                 
29412             }, this);
29413         }
29414         
29415         if(this.loadMask){
29416             this.maskEl = this.el;
29417         }
29418     },
29419     
29420     initEvents : function()
29421     {
29422         this.urlAPI = (window.createObjectURL && window) || 
29423                                 (window.URL && URL.revokeObjectURL && URL) || 
29424                                 (window.webkitURL && webkitURL);
29425                         
29426         this.bodyEl = this.el.select('.roo-upload-cropbox-body', true).first();
29427         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29428         
29429         this.selectorEl = this.el.select('.roo-upload-cropbox-selector', true).first();
29430         this.selectorEl.hide();
29431         
29432         this.previewEl = this.el.select('.roo-upload-cropbox-preview', true).first();
29433         this.previewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29434         
29435         this.thumbEl = this.el.select('.roo-upload-cropbox-thumb', true).first();
29436         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29437         this.thumbEl.hide();
29438         
29439         this.notifyEl = this.el.select('.roo-upload-cropbox-empty-notify', true).first();
29440         this.notifyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29441         
29442         this.errorEl = this.el.select('.roo-upload-cropbox-error-notify', true).first();
29443         this.errorEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29444         this.errorEl.hide();
29445         
29446         this.footerEl = this.el.select('.roo-upload-cropbox-footer', true).first();
29447         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29448         this.footerEl.hide();
29449         
29450         this.setThumbBoxSize();
29451         
29452         this.bind();
29453         
29454         this.resize();
29455         
29456         this.fireEvent('initial', this);
29457     },
29458
29459     bind : function()
29460     {
29461         var _this = this;
29462         
29463         window.addEventListener("resize", function() { _this.resize(); } );
29464         
29465         this.bodyEl.on('click', this.beforeSelectFile, this);
29466         
29467         if(Roo.isTouch){
29468             this.bodyEl.on('touchstart', this.onTouchStart, this);
29469             this.bodyEl.on('touchmove', this.onTouchMove, this);
29470             this.bodyEl.on('touchend', this.onTouchEnd, this);
29471         }
29472         
29473         if(!Roo.isTouch){
29474             this.bodyEl.on('mousedown', this.onMouseDown, this);
29475             this.bodyEl.on('mousemove', this.onMouseMove, this);
29476             var mousewheel = (/Firefox/i.test(navigator.userAgent))? 'DOMMouseScroll' : 'mousewheel';
29477             this.bodyEl.on(mousewheel, this.onMouseWheel, this);
29478             Roo.get(document).on('mouseup', this.onMouseUp, this);
29479         }
29480         
29481         this.selectorEl.on('change', this.onFileSelected, this);
29482     },
29483     
29484     reset : function()
29485     {    
29486         this.scale = 0;
29487         this.baseScale = 1;
29488         this.rotate = 0;
29489         this.baseRotate = 1;
29490         this.dragable = false;
29491         this.pinching = false;
29492         this.mouseX = 0;
29493         this.mouseY = 0;
29494         this.cropData = false;
29495         this.notifyEl.dom.innerHTML = this.emptyText;
29496         
29497         this.selectorEl.dom.value = '';
29498         
29499     },
29500     
29501     resize : function()
29502     {
29503         if(this.fireEvent('resize', this) != false){
29504             this.setThumbBoxPosition();
29505             this.setCanvasPosition();
29506         }
29507     },
29508     
29509     onFooterButtonClick : function(e, el, o, type)
29510     {
29511         switch (type) {
29512             case 'rotate-left' :
29513                 this.onRotateLeft(e);
29514                 break;
29515             case 'rotate-right' :
29516                 this.onRotateRight(e);
29517                 break;
29518             case 'picture' :
29519                 this.beforeSelectFile(e);
29520                 break;
29521             case 'trash' :
29522                 this.trash(e);
29523                 break;
29524             case 'crop' :
29525                 this.crop(e);
29526                 break;
29527             case 'download' :
29528                 this.download(e);
29529                 break;
29530             default :
29531                 break;
29532         }
29533         
29534         this.fireEvent('footerbuttonclick', this, type);
29535     },
29536     
29537     beforeSelectFile : function(e)
29538     {
29539         e.preventDefault();
29540         
29541         if(this.fireEvent('beforeselectfile', this) != false){
29542             this.selectorEl.dom.click();
29543         }
29544     },
29545     
29546     onFileSelected : function(e)
29547     {
29548         e.preventDefault();
29549         
29550         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
29551             return;
29552         }
29553         
29554         var file = this.selectorEl.dom.files[0];
29555         
29556         if(this.fireEvent('inspect', this, file) != false){
29557             this.prepare(file);
29558         }
29559         
29560     },
29561     
29562     trash : function(e)
29563     {
29564         this.fireEvent('trash', this);
29565     },
29566     
29567     download : function(e)
29568     {
29569         this.fireEvent('download', this);
29570     },
29571     
29572     loadCanvas : function(src)
29573     {   
29574         if(this.fireEvent('beforeloadcanvas', this, src) != false){
29575             
29576             this.reset();
29577             
29578             this.imageEl = document.createElement('img');
29579             
29580             var _this = this;
29581             
29582             this.imageEl.addEventListener("load", function(){ _this.onLoadCanvas(); });
29583             
29584             this.imageEl.src = src;
29585         }
29586     },
29587     
29588     onLoadCanvas : function()
29589     {   
29590         this.imageEl.OriginWidth = this.imageEl.naturalWidth || this.imageEl.width;
29591         this.imageEl.OriginHeight = this.imageEl.naturalHeight || this.imageEl.height;
29592         
29593         this.bodyEl.un('click', this.beforeSelectFile, this);
29594         
29595         this.notifyEl.hide();
29596         this.thumbEl.show();
29597         this.footerEl.show();
29598         
29599         this.baseRotateLevel();
29600         
29601         if(this.isDocument){
29602             this.setThumbBoxSize();
29603         }
29604         
29605         this.setThumbBoxPosition();
29606         
29607         this.baseScaleLevel();
29608         
29609         this.draw();
29610         
29611         this.resize();
29612         
29613         this.canvasLoaded = true;
29614         
29615         if(this.loadMask){
29616             this.maskEl.unmask();
29617         }
29618         
29619     },
29620     
29621     setCanvasPosition : function()
29622     {   
29623         if(!this.canvasEl){
29624             return;
29625         }
29626         
29627         var pw = Math.ceil((this.bodyEl.getWidth() - this.canvasEl.width) / 2);
29628         var ph = Math.ceil((this.bodyEl.getHeight() - this.canvasEl.height) / 2);
29629         
29630         this.previewEl.setLeft(pw);
29631         this.previewEl.setTop(ph);
29632         
29633     },
29634     
29635     onMouseDown : function(e)
29636     {   
29637         e.stopEvent();
29638         
29639         this.dragable = true;
29640         this.pinching = false;
29641         
29642         if(this.isDocument && (this.canvasEl.width < this.thumbEl.getWidth() || this.canvasEl.height < this.thumbEl.getHeight())){
29643             this.dragable = false;
29644             return;
29645         }
29646         
29647         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
29648         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
29649         
29650     },
29651     
29652     onMouseMove : function(e)
29653     {   
29654         e.stopEvent();
29655         
29656         if(!this.canvasLoaded){
29657             return;
29658         }
29659         
29660         if (!this.dragable){
29661             return;
29662         }
29663         
29664         var minX = Math.ceil(this.thumbEl.getLeft(true));
29665         var minY = Math.ceil(this.thumbEl.getTop(true));
29666         
29667         var maxX = Math.ceil(minX + this.thumbEl.getWidth() - this.canvasEl.width);
29668         var maxY = Math.ceil(minY + this.thumbEl.getHeight() - this.canvasEl.height);
29669         
29670         var x = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
29671         var y = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
29672         
29673         x = x - this.mouseX;
29674         y = y - this.mouseY;
29675         
29676         var bgX = Math.ceil(x + this.previewEl.getLeft(true));
29677         var bgY = Math.ceil(y + this.previewEl.getTop(true));
29678         
29679         bgX = (minX < bgX) ? minX : ((maxX > bgX) ? maxX : bgX);
29680         bgY = (minY < bgY) ? minY : ((maxY > bgY) ? maxY : bgY);
29681         
29682         this.previewEl.setLeft(bgX);
29683         this.previewEl.setTop(bgY);
29684         
29685         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
29686         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
29687     },
29688     
29689     onMouseUp : function(e)
29690     {   
29691         e.stopEvent();
29692         
29693         this.dragable = false;
29694     },
29695     
29696     onMouseWheel : function(e)
29697     {   
29698         e.stopEvent();
29699         
29700         this.startScale = this.scale;
29701         
29702         this.scale = (e.getWheelDelta() == 1) ? (this.scale + 1) : (this.scale - 1);
29703         
29704         if(!this.zoomable()){
29705             this.scale = this.startScale;
29706             return;
29707         }
29708         
29709         this.draw();
29710         
29711         return;
29712     },
29713     
29714     zoomable : function()
29715     {
29716         var minScale = this.thumbEl.getWidth() / this.minWidth;
29717         
29718         if(this.minWidth < this.minHeight){
29719             minScale = this.thumbEl.getHeight() / this.minHeight;
29720         }
29721         
29722         var width = Math.ceil(this.imageEl.OriginWidth * this.getScaleLevel() / minScale);
29723         var height = Math.ceil(this.imageEl.OriginHeight * this.getScaleLevel() / minScale);
29724         
29725         if(
29726                 this.isDocument &&
29727                 (this.rotate == 0 || this.rotate == 180) && 
29728                 (
29729                     width > this.imageEl.OriginWidth || 
29730                     height > this.imageEl.OriginHeight ||
29731                     (width < this.minWidth && height < this.minHeight)
29732                 )
29733         ){
29734             return false;
29735         }
29736         
29737         if(
29738                 this.isDocument &&
29739                 (this.rotate == 90 || this.rotate == 270) && 
29740                 (
29741                     width > this.imageEl.OriginWidth || 
29742                     height > this.imageEl.OriginHeight ||
29743                     (width < this.minHeight && height < this.minWidth)
29744                 )
29745         ){
29746             return false;
29747         }
29748         
29749         if(
29750                 !this.isDocument &&
29751                 (this.rotate == 0 || this.rotate == 180) && 
29752                 (
29753                     width < this.minWidth || 
29754                     width > this.imageEl.OriginWidth || 
29755                     height < this.minHeight || 
29756                     height > this.imageEl.OriginHeight
29757                 )
29758         ){
29759             return false;
29760         }
29761         
29762         if(
29763                 !this.isDocument &&
29764                 (this.rotate == 90 || this.rotate == 270) && 
29765                 (
29766                     width < this.minHeight || 
29767                     width > this.imageEl.OriginWidth || 
29768                     height < this.minWidth || 
29769                     height > this.imageEl.OriginHeight
29770                 )
29771         ){
29772             return false;
29773         }
29774         
29775         return true;
29776         
29777     },
29778     
29779     onRotateLeft : function(e)
29780     {   
29781         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
29782             
29783             var minScale = this.thumbEl.getWidth() / this.minWidth;
29784             
29785             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
29786             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
29787             
29788             this.startScale = this.scale;
29789             
29790             while (this.getScaleLevel() < minScale){
29791             
29792                 this.scale = this.scale + 1;
29793                 
29794                 if(!this.zoomable()){
29795                     break;
29796                 }
29797                 
29798                 if(
29799                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
29800                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
29801                 ){
29802                     continue;
29803                 }
29804                 
29805                 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
29806
29807                 this.draw();
29808                 
29809                 return;
29810             }
29811             
29812             this.scale = this.startScale;
29813             
29814             this.onRotateFail();
29815             
29816             return false;
29817         }
29818         
29819         this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
29820
29821         if(this.isDocument){
29822             this.setThumbBoxSize();
29823             this.setThumbBoxPosition();
29824             this.setCanvasPosition();
29825         }
29826         
29827         this.draw();
29828         
29829         this.fireEvent('rotate', this, 'left');
29830         
29831     },
29832     
29833     onRotateRight : function(e)
29834     {
29835         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
29836             
29837             var minScale = this.thumbEl.getWidth() / this.minWidth;
29838         
29839             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
29840             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
29841             
29842             this.startScale = this.scale;
29843             
29844             while (this.getScaleLevel() < minScale){
29845             
29846                 this.scale = this.scale + 1;
29847                 
29848                 if(!this.zoomable()){
29849                     break;
29850                 }
29851                 
29852                 if(
29853                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
29854                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
29855                 ){
29856                     continue;
29857                 }
29858                 
29859                 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
29860
29861                 this.draw();
29862                 
29863                 return;
29864             }
29865             
29866             this.scale = this.startScale;
29867             
29868             this.onRotateFail();
29869             
29870             return false;
29871         }
29872         
29873         this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
29874
29875         if(this.isDocument){
29876             this.setThumbBoxSize();
29877             this.setThumbBoxPosition();
29878             this.setCanvasPosition();
29879         }
29880         
29881         this.draw();
29882         
29883         this.fireEvent('rotate', this, 'right');
29884     },
29885     
29886     onRotateFail : function()
29887     {
29888         this.errorEl.show(true);
29889         
29890         var _this = this;
29891         
29892         (function() { _this.errorEl.hide(true); }).defer(this.errorTimeout);
29893     },
29894     
29895     draw : function()
29896     {
29897         this.previewEl.dom.innerHTML = '';
29898         
29899         var canvasEl = document.createElement("canvas");
29900         
29901         var contextEl = canvasEl.getContext("2d");
29902         
29903         canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
29904         canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
29905         var center = this.imageEl.OriginWidth / 2;
29906         
29907         if(this.imageEl.OriginWidth < this.imageEl.OriginHeight){
29908             canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
29909             canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
29910             center = this.imageEl.OriginHeight / 2;
29911         }
29912         
29913         contextEl.scale(this.getScaleLevel(), this.getScaleLevel());
29914         
29915         contextEl.translate(center, center);
29916         contextEl.rotate(this.rotate * Math.PI / 180);
29917
29918         contextEl.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
29919         
29920         this.canvasEl = document.createElement("canvas");
29921         
29922         this.contextEl = this.canvasEl.getContext("2d");
29923         
29924         switch (this.rotate) {
29925             case 0 :
29926                 
29927                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
29928                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
29929                 
29930                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
29931                 
29932                 break;
29933             case 90 : 
29934                 
29935                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
29936                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
29937                 
29938                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
29939                     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);
29940                     break;
29941                 }
29942                 
29943                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
29944                 
29945                 break;
29946             case 180 :
29947                 
29948                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
29949                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
29950                 
29951                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
29952                     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);
29953                     break;
29954                 }
29955                 
29956                 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);
29957                 
29958                 break;
29959             case 270 :
29960                 
29961                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
29962                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
29963         
29964                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
29965                     this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
29966                     break;
29967                 }
29968                 
29969                 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);
29970                 
29971                 break;
29972             default : 
29973                 break;
29974         }
29975         
29976         this.previewEl.appendChild(this.canvasEl);
29977         
29978         this.setCanvasPosition();
29979     },
29980     
29981     crop : function()
29982     {
29983         if(!this.canvasLoaded){
29984             return;
29985         }
29986         
29987         var imageCanvas = document.createElement("canvas");
29988         
29989         var imageContext = imageCanvas.getContext("2d");
29990         
29991         imageCanvas.width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
29992         imageCanvas.height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
29993         
29994         var center = imageCanvas.width / 2;
29995         
29996         imageContext.translate(center, center);
29997         
29998         imageContext.rotate(this.rotate * Math.PI / 180);
29999         
30000         imageContext.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
30001         
30002         var canvas = document.createElement("canvas");
30003         
30004         var context = canvas.getContext("2d");
30005                 
30006         canvas.width = this.minWidth;
30007         canvas.height = this.minHeight;
30008
30009         switch (this.rotate) {
30010             case 0 :
30011                 
30012                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
30013                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
30014                 
30015                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
30016                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
30017                 
30018                 var targetWidth = this.minWidth - 2 * x;
30019                 var targetHeight = this.minHeight - 2 * y;
30020                 
30021                 var scale = 1;
30022                 
30023                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
30024                     scale = targetWidth / width;
30025                 }
30026                 
30027                 if(x > 0 && y == 0){
30028                     scale = targetHeight / height;
30029                 }
30030                 
30031                 if(x > 0 && y > 0){
30032                     scale = targetWidth / width;
30033                     
30034                     if(width < height){
30035                         scale = targetHeight / height;
30036                     }
30037                 }
30038                 
30039                 context.scale(scale, scale);
30040                 
30041                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
30042                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
30043
30044                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
30045                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
30046
30047                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
30048                 
30049                 break;
30050             case 90 : 
30051                 
30052                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
30053                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
30054                 
30055                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
30056                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
30057                 
30058                 var targetWidth = this.minWidth - 2 * x;
30059                 var targetHeight = this.minHeight - 2 * y;
30060                 
30061                 var scale = 1;
30062                 
30063                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
30064                     scale = targetWidth / width;
30065                 }
30066                 
30067                 if(x > 0 && y == 0){
30068                     scale = targetHeight / height;
30069                 }
30070                 
30071                 if(x > 0 && y > 0){
30072                     scale = targetWidth / width;
30073                     
30074                     if(width < height){
30075                         scale = targetHeight / height;
30076                     }
30077                 }
30078                 
30079                 context.scale(scale, scale);
30080                 
30081                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
30082                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
30083
30084                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
30085                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
30086                 
30087                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
30088                 
30089                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
30090                 
30091                 break;
30092             case 180 :
30093                 
30094                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
30095                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
30096                 
30097                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
30098                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
30099                 
30100                 var targetWidth = this.minWidth - 2 * x;
30101                 var targetHeight = this.minHeight - 2 * y;
30102                 
30103                 var scale = 1;
30104                 
30105                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
30106                     scale = targetWidth / width;
30107                 }
30108                 
30109                 if(x > 0 && y == 0){
30110                     scale = targetHeight / height;
30111                 }
30112                 
30113                 if(x > 0 && y > 0){
30114                     scale = targetWidth / width;
30115                     
30116                     if(width < height){
30117                         scale = targetHeight / height;
30118                     }
30119                 }
30120                 
30121                 context.scale(scale, scale);
30122                 
30123                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
30124                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
30125
30126                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
30127                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
30128
30129                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
30130                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
30131                 
30132                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
30133                 
30134                 break;
30135             case 270 :
30136                 
30137                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
30138                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
30139                 
30140                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
30141                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
30142                 
30143                 var targetWidth = this.minWidth - 2 * x;
30144                 var targetHeight = this.minHeight - 2 * y;
30145                 
30146                 var scale = 1;
30147                 
30148                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
30149                     scale = targetWidth / width;
30150                 }
30151                 
30152                 if(x > 0 && y == 0){
30153                     scale = targetHeight / height;
30154                 }
30155                 
30156                 if(x > 0 && y > 0){
30157                     scale = targetWidth / width;
30158                     
30159                     if(width < height){
30160                         scale = targetHeight / height;
30161                     }
30162                 }
30163                 
30164                 context.scale(scale, scale);
30165                 
30166                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
30167                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
30168
30169                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
30170                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
30171                 
30172                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
30173                 
30174                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
30175                 
30176                 break;
30177             default : 
30178                 break;
30179         }
30180         
30181         this.cropData = canvas.toDataURL(this.cropType);
30182         
30183         if(this.fireEvent('crop', this, this.cropData) !== false){
30184             this.process(this.file, this.cropData);
30185         }
30186         
30187         return;
30188         
30189     },
30190     
30191     setThumbBoxSize : function()
30192     {
30193         var width, height;
30194         
30195         if(this.isDocument && typeof(this.imageEl) != 'undefined'){
30196             width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.max(this.minWidth, this.minHeight) : Math.min(this.minWidth, this.minHeight);
30197             height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.min(this.minWidth, this.minHeight) : Math.max(this.minWidth, this.minHeight);
30198             
30199             this.minWidth = width;
30200             this.minHeight = height;
30201             
30202             if(this.rotate == 90 || this.rotate == 270){
30203                 this.minWidth = height;
30204                 this.minHeight = width;
30205             }
30206         }
30207         
30208         height = 300;
30209         width = Math.ceil(this.minWidth * height / this.minHeight);
30210         
30211         if(this.minWidth > this.minHeight){
30212             width = 300;
30213             height = Math.ceil(this.minHeight * width / this.minWidth);
30214         }
30215         
30216         this.thumbEl.setStyle({
30217             width : width + 'px',
30218             height : height + 'px'
30219         });
30220
30221         return;
30222             
30223     },
30224     
30225     setThumbBoxPosition : function()
30226     {
30227         var x = Math.ceil((this.bodyEl.getWidth() - this.thumbEl.getWidth()) / 2 );
30228         var y = Math.ceil((this.bodyEl.getHeight() - this.thumbEl.getHeight()) / 2);
30229         
30230         this.thumbEl.setLeft(x);
30231         this.thumbEl.setTop(y);
30232         
30233     },
30234     
30235     baseRotateLevel : function()
30236     {
30237         this.baseRotate = 1;
30238         
30239         if(
30240                 typeof(this.exif) != 'undefined' &&
30241                 typeof(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != 'undefined' &&
30242                 [1, 3, 6, 8].indexOf(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != -1
30243         ){
30244             this.baseRotate = this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']];
30245         }
30246         
30247         this.rotate = Roo.bootstrap.UploadCropbox['Orientation'][this.baseRotate];
30248         
30249     },
30250     
30251     baseScaleLevel : function()
30252     {
30253         var width, height;
30254         
30255         if(this.isDocument){
30256             
30257             if(this.baseRotate == 6 || this.baseRotate == 8){
30258             
30259                 height = this.thumbEl.getHeight();
30260                 this.baseScale = height / this.imageEl.OriginWidth;
30261
30262                 if(this.imageEl.OriginHeight * this.baseScale > this.thumbEl.getWidth()){
30263                     width = this.thumbEl.getWidth();
30264                     this.baseScale = width / this.imageEl.OriginHeight;
30265                 }
30266
30267                 return;
30268             }
30269
30270             height = this.thumbEl.getHeight();
30271             this.baseScale = height / this.imageEl.OriginHeight;
30272
30273             if(this.imageEl.OriginWidth * this.baseScale > this.thumbEl.getWidth()){
30274                 width = this.thumbEl.getWidth();
30275                 this.baseScale = width / this.imageEl.OriginWidth;
30276             }
30277
30278             return;
30279         }
30280         
30281         if(this.baseRotate == 6 || this.baseRotate == 8){
30282             
30283             width = this.thumbEl.getHeight();
30284             this.baseScale = width / this.imageEl.OriginHeight;
30285             
30286             if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getWidth()){
30287                 height = this.thumbEl.getWidth();
30288                 this.baseScale = height / this.imageEl.OriginHeight;
30289             }
30290             
30291             if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
30292                 height = this.thumbEl.getWidth();
30293                 this.baseScale = height / this.imageEl.OriginHeight;
30294                 
30295                 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getHeight()){
30296                     width = this.thumbEl.getHeight();
30297                     this.baseScale = width / this.imageEl.OriginWidth;
30298                 }
30299             }
30300             
30301             return;
30302         }
30303         
30304         width = this.thumbEl.getWidth();
30305         this.baseScale = width / this.imageEl.OriginWidth;
30306         
30307         if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getHeight()){
30308             height = this.thumbEl.getHeight();
30309             this.baseScale = height / this.imageEl.OriginHeight;
30310         }
30311         
30312         if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
30313             
30314             height = this.thumbEl.getHeight();
30315             this.baseScale = height / this.imageEl.OriginHeight;
30316             
30317             if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getWidth()){
30318                 width = this.thumbEl.getWidth();
30319                 this.baseScale = width / this.imageEl.OriginWidth;
30320             }
30321             
30322         }
30323         
30324         return;
30325     },
30326     
30327     getScaleLevel : function()
30328     {
30329         return this.baseScale * Math.pow(1.1, this.scale);
30330     },
30331     
30332     onTouchStart : function(e)
30333     {
30334         if(!this.canvasLoaded){
30335             this.beforeSelectFile(e);
30336             return;
30337         }
30338         
30339         var touches = e.browserEvent.touches;
30340         
30341         if(!touches){
30342             return;
30343         }
30344         
30345         if(touches.length == 1){
30346             this.onMouseDown(e);
30347             return;
30348         }
30349         
30350         if(touches.length != 2){
30351             return;
30352         }
30353         
30354         var coords = [];
30355         
30356         for(var i = 0, finger; finger = touches[i]; i++){
30357             coords.push(finger.pageX, finger.pageY);
30358         }
30359         
30360         var x = Math.pow(coords[0] - coords[2], 2);
30361         var y = Math.pow(coords[1] - coords[3], 2);
30362         
30363         this.startDistance = Math.sqrt(x + y);
30364         
30365         this.startScale = this.scale;
30366         
30367         this.pinching = true;
30368         this.dragable = false;
30369         
30370     },
30371     
30372     onTouchMove : function(e)
30373     {
30374         if(!this.pinching && !this.dragable){
30375             return;
30376         }
30377         
30378         var touches = e.browserEvent.touches;
30379         
30380         if(!touches){
30381             return;
30382         }
30383         
30384         if(this.dragable){
30385             this.onMouseMove(e);
30386             return;
30387         }
30388         
30389         var coords = [];
30390         
30391         for(var i = 0, finger; finger = touches[i]; i++){
30392             coords.push(finger.pageX, finger.pageY);
30393         }
30394         
30395         var x = Math.pow(coords[0] - coords[2], 2);
30396         var y = Math.pow(coords[1] - coords[3], 2);
30397         
30398         this.endDistance = Math.sqrt(x + y);
30399         
30400         this.scale = this.startScale + Math.floor(Math.log(this.endDistance / this.startDistance) / Math.log(1.1));
30401         
30402         if(!this.zoomable()){
30403             this.scale = this.startScale;
30404             return;
30405         }
30406         
30407         this.draw();
30408         
30409     },
30410     
30411     onTouchEnd : function(e)
30412     {
30413         this.pinching = false;
30414         this.dragable = false;
30415         
30416     },
30417     
30418     process : function(file, crop)
30419     {
30420         if(this.loadMask){
30421             this.maskEl.mask(this.loadingText);
30422         }
30423         
30424         this.xhr = new XMLHttpRequest();
30425         
30426         file.xhr = this.xhr;
30427
30428         this.xhr.open(this.method, this.url, true);
30429         
30430         var headers = {
30431             "Accept": "application/json",
30432             "Cache-Control": "no-cache",
30433             "X-Requested-With": "XMLHttpRequest"
30434         };
30435         
30436         for (var headerName in headers) {
30437             var headerValue = headers[headerName];
30438             if (headerValue) {
30439                 this.xhr.setRequestHeader(headerName, headerValue);
30440             }
30441         }
30442         
30443         var _this = this;
30444         
30445         this.xhr.onload = function()
30446         {
30447             _this.xhrOnLoad(_this.xhr);
30448         }
30449         
30450         this.xhr.onerror = function()
30451         {
30452             _this.xhrOnError(_this.xhr);
30453         }
30454         
30455         var formData = new FormData();
30456
30457         formData.append('returnHTML', 'NO');
30458         
30459         if(crop){
30460             formData.append('crop', crop);
30461         }
30462         
30463         if(typeof(file) != 'undefined' && (typeof(file.id) == 'undefined' || file.id * 1 < 1)){
30464             formData.append(this.paramName, file, file.name);
30465         }
30466         
30467         if(typeof(file.filename) != 'undefined'){
30468             formData.append('filename', file.filename);
30469         }
30470         
30471         if(typeof(file.mimetype) != 'undefined'){
30472             formData.append('mimetype', file.mimetype);
30473         }
30474         
30475         if(this.fireEvent('arrange', this, formData) != false){
30476             this.xhr.send(formData);
30477         };
30478     },
30479     
30480     xhrOnLoad : function(xhr)
30481     {
30482         if(this.loadMask){
30483             this.maskEl.unmask();
30484         }
30485         
30486         if (xhr.readyState !== 4) {
30487             this.fireEvent('exception', this, xhr);
30488             return;
30489         }
30490
30491         var response = Roo.decode(xhr.responseText);
30492         
30493         if(!response.success){
30494             this.fireEvent('exception', this, xhr);
30495             return;
30496         }
30497         
30498         var response = Roo.decode(xhr.responseText);
30499         
30500         this.fireEvent('upload', this, response);
30501         
30502     },
30503     
30504     xhrOnError : function()
30505     {
30506         if(this.loadMask){
30507             this.maskEl.unmask();
30508         }
30509         
30510         Roo.log('xhr on error');
30511         
30512         var response = Roo.decode(xhr.responseText);
30513           
30514         Roo.log(response);
30515         
30516     },
30517     
30518     prepare : function(file)
30519     {   
30520         if(this.loadMask){
30521             this.maskEl.mask(this.loadingText);
30522         }
30523         
30524         this.file = false;
30525         this.exif = {};
30526         
30527         if(typeof(file) === 'string'){
30528             this.loadCanvas(file);
30529             return;
30530         }
30531         
30532         if(!file || !this.urlAPI){
30533             return;
30534         }
30535         
30536         this.file = file;
30537         this.cropType = file.type;
30538         
30539         var _this = this;
30540         
30541         if(this.fireEvent('prepare', this, this.file) != false){
30542             
30543             var reader = new FileReader();
30544             
30545             reader.onload = function (e) {
30546                 if (e.target.error) {
30547                     Roo.log(e.target.error);
30548                     return;
30549                 }
30550                 
30551                 var buffer = e.target.result,
30552                     dataView = new DataView(buffer),
30553                     offset = 2,
30554                     maxOffset = dataView.byteLength - 4,
30555                     markerBytes,
30556                     markerLength;
30557                 
30558                 if (dataView.getUint16(0) === 0xffd8) {
30559                     while (offset < maxOffset) {
30560                         markerBytes = dataView.getUint16(offset);
30561                         
30562                         if ((markerBytes >= 0xffe0 && markerBytes <= 0xffef) || markerBytes === 0xfffe) {
30563                             markerLength = dataView.getUint16(offset + 2) + 2;
30564                             if (offset + markerLength > dataView.byteLength) {
30565                                 Roo.log('Invalid meta data: Invalid segment size.');
30566                                 break;
30567                             }
30568                             
30569                             if(markerBytes == 0xffe1){
30570                                 _this.parseExifData(
30571                                     dataView,
30572                                     offset,
30573                                     markerLength
30574                                 );
30575                             }
30576                             
30577                             offset += markerLength;
30578                             
30579                             continue;
30580                         }
30581                         
30582                         break;
30583                     }
30584                     
30585                 }
30586                 
30587                 var url = _this.urlAPI.createObjectURL(_this.file);
30588                 
30589                 _this.loadCanvas(url);
30590                 
30591                 return;
30592             }
30593             
30594             reader.readAsArrayBuffer(this.file);
30595             
30596         }
30597         
30598     },
30599     
30600     parseExifData : function(dataView, offset, length)
30601     {
30602         var tiffOffset = offset + 10,
30603             littleEndian,
30604             dirOffset;
30605     
30606         if (dataView.getUint32(offset + 4) !== 0x45786966) {
30607             // No Exif data, might be XMP data instead
30608             return;
30609         }
30610         
30611         // Check for the ASCII code for "Exif" (0x45786966):
30612         if (dataView.getUint32(offset + 4) !== 0x45786966) {
30613             // No Exif data, might be XMP data instead
30614             return;
30615         }
30616         if (tiffOffset + 8 > dataView.byteLength) {
30617             Roo.log('Invalid Exif data: Invalid segment size.');
30618             return;
30619         }
30620         // Check for the two null bytes:
30621         if (dataView.getUint16(offset + 8) !== 0x0000) {
30622             Roo.log('Invalid Exif data: Missing byte alignment offset.');
30623             return;
30624         }
30625         // Check the byte alignment:
30626         switch (dataView.getUint16(tiffOffset)) {
30627         case 0x4949:
30628             littleEndian = true;
30629             break;
30630         case 0x4D4D:
30631             littleEndian = false;
30632             break;
30633         default:
30634             Roo.log('Invalid Exif data: Invalid byte alignment marker.');
30635             return;
30636         }
30637         // Check for the TIFF tag marker (0x002A):
30638         if (dataView.getUint16(tiffOffset + 2, littleEndian) !== 0x002A) {
30639             Roo.log('Invalid Exif data: Missing TIFF marker.');
30640             return;
30641         }
30642         // Retrieve the directory offset bytes, usually 0x00000008 or 8 decimal:
30643         dirOffset = dataView.getUint32(tiffOffset + 4, littleEndian);
30644         
30645         this.parseExifTags(
30646             dataView,
30647             tiffOffset,
30648             tiffOffset + dirOffset,
30649             littleEndian
30650         );
30651     },
30652     
30653     parseExifTags : function(dataView, tiffOffset, dirOffset, littleEndian)
30654     {
30655         var tagsNumber,
30656             dirEndOffset,
30657             i;
30658         if (dirOffset + 6 > dataView.byteLength) {
30659             Roo.log('Invalid Exif data: Invalid directory offset.');
30660             return;
30661         }
30662         tagsNumber = dataView.getUint16(dirOffset, littleEndian);
30663         dirEndOffset = dirOffset + 2 + 12 * tagsNumber;
30664         if (dirEndOffset + 4 > dataView.byteLength) {
30665             Roo.log('Invalid Exif data: Invalid directory size.');
30666             return;
30667         }
30668         for (i = 0; i < tagsNumber; i += 1) {
30669             this.parseExifTag(
30670                 dataView,
30671                 tiffOffset,
30672                 dirOffset + 2 + 12 * i, // tag offset
30673                 littleEndian
30674             );
30675         }
30676         // Return the offset to the next directory:
30677         return dataView.getUint32(dirEndOffset, littleEndian);
30678     },
30679     
30680     parseExifTag : function (dataView, tiffOffset, offset, littleEndian) 
30681     {
30682         var tag = dataView.getUint16(offset, littleEndian);
30683         
30684         this.exif[tag] = this.getExifValue(
30685             dataView,
30686             tiffOffset,
30687             offset,
30688             dataView.getUint16(offset + 2, littleEndian), // tag type
30689             dataView.getUint32(offset + 4, littleEndian), // tag length
30690             littleEndian
30691         );
30692     },
30693     
30694     getExifValue : function (dataView, tiffOffset, offset, type, length, littleEndian)
30695     {
30696         var tagType = Roo.bootstrap.UploadCropbox.exifTagTypes[type],
30697             tagSize,
30698             dataOffset,
30699             values,
30700             i,
30701             str,
30702             c;
30703     
30704         if (!tagType) {
30705             Roo.log('Invalid Exif data: Invalid tag type.');
30706             return;
30707         }
30708         
30709         tagSize = tagType.size * length;
30710         // Determine if the value is contained in the dataOffset bytes,
30711         // or if the value at the dataOffset is a pointer to the actual data:
30712         dataOffset = tagSize > 4 ?
30713                 tiffOffset + dataView.getUint32(offset + 8, littleEndian) : (offset + 8);
30714         if (dataOffset + tagSize > dataView.byteLength) {
30715             Roo.log('Invalid Exif data: Invalid data offset.');
30716             return;
30717         }
30718         if (length === 1) {
30719             return tagType.getValue(dataView, dataOffset, littleEndian);
30720         }
30721         values = [];
30722         for (i = 0; i < length; i += 1) {
30723             values[i] = tagType.getValue(dataView, dataOffset + i * tagType.size, littleEndian);
30724         }
30725         
30726         if (tagType.ascii) {
30727             str = '';
30728             // Concatenate the chars:
30729             for (i = 0; i < values.length; i += 1) {
30730                 c = values[i];
30731                 // Ignore the terminating NULL byte(s):
30732                 if (c === '\u0000') {
30733                     break;
30734                 }
30735                 str += c;
30736             }
30737             return str;
30738         }
30739         return values;
30740     }
30741     
30742 });
30743
30744 Roo.apply(Roo.bootstrap.UploadCropbox, {
30745     tags : {
30746         'Orientation': 0x0112
30747     },
30748     
30749     Orientation: {
30750             1: 0, //'top-left',
30751 //            2: 'top-right',
30752             3: 180, //'bottom-right',
30753 //            4: 'bottom-left',
30754 //            5: 'left-top',
30755             6: 90, //'right-top',
30756 //            7: 'right-bottom',
30757             8: 270 //'left-bottom'
30758     },
30759     
30760     exifTagTypes : {
30761         // byte, 8-bit unsigned int:
30762         1: {
30763             getValue: function (dataView, dataOffset) {
30764                 return dataView.getUint8(dataOffset);
30765             },
30766             size: 1
30767         },
30768         // ascii, 8-bit byte:
30769         2: {
30770             getValue: function (dataView, dataOffset) {
30771                 return String.fromCharCode(dataView.getUint8(dataOffset));
30772             },
30773             size: 1,
30774             ascii: true
30775         },
30776         // short, 16 bit int:
30777         3: {
30778             getValue: function (dataView, dataOffset, littleEndian) {
30779                 return dataView.getUint16(dataOffset, littleEndian);
30780             },
30781             size: 2
30782         },
30783         // long, 32 bit int:
30784         4: {
30785             getValue: function (dataView, dataOffset, littleEndian) {
30786                 return dataView.getUint32(dataOffset, littleEndian);
30787             },
30788             size: 4
30789         },
30790         // rational = two long values, first is numerator, second is denominator:
30791         5: {
30792             getValue: function (dataView, dataOffset, littleEndian) {
30793                 return dataView.getUint32(dataOffset, littleEndian) /
30794                     dataView.getUint32(dataOffset + 4, littleEndian);
30795             },
30796             size: 8
30797         },
30798         // slong, 32 bit signed int:
30799         9: {
30800             getValue: function (dataView, dataOffset, littleEndian) {
30801                 return dataView.getInt32(dataOffset, littleEndian);
30802             },
30803             size: 4
30804         },
30805         // srational, two slongs, first is numerator, second is denominator:
30806         10: {
30807             getValue: function (dataView, dataOffset, littleEndian) {
30808                 return dataView.getInt32(dataOffset, littleEndian) /
30809                     dataView.getInt32(dataOffset + 4, littleEndian);
30810             },
30811             size: 8
30812         }
30813     },
30814     
30815     footer : {
30816         STANDARD : [
30817             {
30818                 tag : 'div',
30819                 cls : 'btn-group roo-upload-cropbox-rotate-left',
30820                 action : 'rotate-left',
30821                 cn : [
30822                     {
30823                         tag : 'button',
30824                         cls : 'btn btn-default',
30825                         html : '<i class="fa fa-undo"></i>'
30826                     }
30827                 ]
30828             },
30829             {
30830                 tag : 'div',
30831                 cls : 'btn-group roo-upload-cropbox-picture',
30832                 action : 'picture',
30833                 cn : [
30834                     {
30835                         tag : 'button',
30836                         cls : 'btn btn-default',
30837                         html : '<i class="fa fa-picture-o"></i>'
30838                     }
30839                 ]
30840             },
30841             {
30842                 tag : 'div',
30843                 cls : 'btn-group roo-upload-cropbox-rotate-right',
30844                 action : 'rotate-right',
30845                 cn : [
30846                     {
30847                         tag : 'button',
30848                         cls : 'btn btn-default',
30849                         html : '<i class="fa fa-repeat"></i>'
30850                     }
30851                 ]
30852             }
30853         ],
30854         DOCUMENT : [
30855             {
30856                 tag : 'div',
30857                 cls : 'btn-group roo-upload-cropbox-rotate-left',
30858                 action : 'rotate-left',
30859                 cn : [
30860                     {
30861                         tag : 'button',
30862                         cls : 'btn btn-default',
30863                         html : '<i class="fa fa-undo"></i>'
30864                     }
30865                 ]
30866             },
30867             {
30868                 tag : 'div',
30869                 cls : 'btn-group roo-upload-cropbox-download',
30870                 action : 'download',
30871                 cn : [
30872                     {
30873                         tag : 'button',
30874                         cls : 'btn btn-default',
30875                         html : '<i class="fa fa-download"></i>'
30876                     }
30877                 ]
30878             },
30879             {
30880                 tag : 'div',
30881                 cls : 'btn-group roo-upload-cropbox-crop',
30882                 action : 'crop',
30883                 cn : [
30884                     {
30885                         tag : 'button',
30886                         cls : 'btn btn-default',
30887                         html : '<i class="fa fa-crop"></i>'
30888                     }
30889                 ]
30890             },
30891             {
30892                 tag : 'div',
30893                 cls : 'btn-group roo-upload-cropbox-trash',
30894                 action : 'trash',
30895                 cn : [
30896                     {
30897                         tag : 'button',
30898                         cls : 'btn btn-default',
30899                         html : '<i class="fa fa-trash"></i>'
30900                     }
30901                 ]
30902             },
30903             {
30904                 tag : 'div',
30905                 cls : 'btn-group roo-upload-cropbox-rotate-right',
30906                 action : 'rotate-right',
30907                 cn : [
30908                     {
30909                         tag : 'button',
30910                         cls : 'btn btn-default',
30911                         html : '<i class="fa fa-repeat"></i>'
30912                     }
30913                 ]
30914             }
30915         ],
30916         ROTATOR : [
30917             {
30918                 tag : 'div',
30919                 cls : 'btn-group roo-upload-cropbox-rotate-left',
30920                 action : 'rotate-left',
30921                 cn : [
30922                     {
30923                         tag : 'button',
30924                         cls : 'btn btn-default',
30925                         html : '<i class="fa fa-undo"></i>'
30926                     }
30927                 ]
30928             },
30929             {
30930                 tag : 'div',
30931                 cls : 'btn-group roo-upload-cropbox-rotate-right',
30932                 action : 'rotate-right',
30933                 cn : [
30934                     {
30935                         tag : 'button',
30936                         cls : 'btn btn-default',
30937                         html : '<i class="fa fa-repeat"></i>'
30938                     }
30939                 ]
30940             }
30941         ]
30942     }
30943 });
30944
30945 /*
30946 * Licence: LGPL
30947 */
30948
30949 /**
30950  * @class Roo.bootstrap.DocumentManager
30951  * @extends Roo.bootstrap.Component
30952  * Bootstrap DocumentManager class
30953  * @cfg {String} paramName default 'imageUpload'
30954  * @cfg {String} toolTipName default 'filename'
30955  * @cfg {String} method default POST
30956  * @cfg {String} url action url
30957  * @cfg {Number} boxes number of boxes, 0 is no limit.. default 0
30958  * @cfg {Boolean} multiple multiple upload default true
30959  * @cfg {Number} thumbSize default 300
30960  * @cfg {String} fieldLabel
30961  * @cfg {Number} labelWidth default 4
30962  * @cfg {String} labelAlign (left|top) default left
30963  * @cfg {Boolean} editable (true|false) allow edit when upload a image default true
30964 * @cfg {Number} labellg set the width of label (1-12)
30965  * @cfg {Number} labelmd set the width of label (1-12)
30966  * @cfg {Number} labelsm set the width of label (1-12)
30967  * @cfg {Number} labelxs set the width of label (1-12)
30968  * 
30969  * @constructor
30970  * Create a new DocumentManager
30971  * @param {Object} config The config object
30972  */
30973
30974 Roo.bootstrap.DocumentManager = function(config){
30975     Roo.bootstrap.DocumentManager.superclass.constructor.call(this, config);
30976     
30977     this.files = [];
30978     this.delegates = [];
30979     
30980     this.addEvents({
30981         /**
30982          * @event initial
30983          * Fire when initial the DocumentManager
30984          * @param {Roo.bootstrap.DocumentManager} this
30985          */
30986         "initial" : true,
30987         /**
30988          * @event inspect
30989          * inspect selected file
30990          * @param {Roo.bootstrap.DocumentManager} this
30991          * @param {File} file
30992          */
30993         "inspect" : true,
30994         /**
30995          * @event exception
30996          * Fire when xhr load exception
30997          * @param {Roo.bootstrap.DocumentManager} this
30998          * @param {XMLHttpRequest} xhr
30999          */
31000         "exception" : true,
31001         /**
31002          * @event afterupload
31003          * Fire when xhr load exception
31004          * @param {Roo.bootstrap.DocumentManager} this
31005          * @param {XMLHttpRequest} xhr
31006          */
31007         "afterupload" : true,
31008         /**
31009          * @event prepare
31010          * prepare the form data
31011          * @param {Roo.bootstrap.DocumentManager} this
31012          * @param {Object} formData
31013          */
31014         "prepare" : true,
31015         /**
31016          * @event remove
31017          * Fire when remove the file
31018          * @param {Roo.bootstrap.DocumentManager} this
31019          * @param {Object} file
31020          */
31021         "remove" : true,
31022         /**
31023          * @event refresh
31024          * Fire after refresh the file
31025          * @param {Roo.bootstrap.DocumentManager} this
31026          */
31027         "refresh" : true,
31028         /**
31029          * @event click
31030          * Fire after click the image
31031          * @param {Roo.bootstrap.DocumentManager} this
31032          * @param {Object} file
31033          */
31034         "click" : true,
31035         /**
31036          * @event edit
31037          * Fire when upload a image and editable set to true
31038          * @param {Roo.bootstrap.DocumentManager} this
31039          * @param {Object} file
31040          */
31041         "edit" : true,
31042         /**
31043          * @event beforeselectfile
31044          * Fire before select file
31045          * @param {Roo.bootstrap.DocumentManager} this
31046          */
31047         "beforeselectfile" : true,
31048         /**
31049          * @event process
31050          * Fire before process file
31051          * @param {Roo.bootstrap.DocumentManager} this
31052          * @param {Object} file
31053          */
31054         "process" : true,
31055         /**
31056          * @event previewrendered
31057          * Fire when preview rendered
31058          * @param {Roo.bootstrap.DocumentManager} this
31059          * @param {Object} file
31060          */
31061         "previewrendered" : true,
31062         /**
31063          */
31064         "previewResize" : true
31065         
31066     });
31067 };
31068
31069 Roo.extend(Roo.bootstrap.DocumentManager, Roo.bootstrap.Component,  {
31070     
31071     boxes : 0,
31072     inputName : '',
31073     thumbSize : 300,
31074     multiple : true,
31075     files : false,
31076     method : 'POST',
31077     url : '',
31078     paramName : 'imageUpload',
31079     toolTipName : 'filename',
31080     fieldLabel : '',
31081     labelWidth : 4,
31082     labelAlign : 'left',
31083     editable : true,
31084     delegates : false,
31085     xhr : false, 
31086     
31087     labellg : 0,
31088     labelmd : 0,
31089     labelsm : 0,
31090     labelxs : 0,
31091     
31092     getAutoCreate : function()
31093     {   
31094         var managerWidget = {
31095             tag : 'div',
31096             cls : 'roo-document-manager',
31097             cn : [
31098                 {
31099                     tag : 'input',
31100                     cls : 'roo-document-manager-selector',
31101                     type : 'file'
31102                 },
31103                 {
31104                     tag : 'div',
31105                     cls : 'roo-document-manager-uploader',
31106                     cn : [
31107                         {
31108                             tag : 'div',
31109                             cls : 'roo-document-manager-upload-btn',
31110                             html : '<i class="fa fa-plus"></i>'
31111                         }
31112                     ]
31113                     
31114                 }
31115             ]
31116         };
31117         
31118         var content = [
31119             {
31120                 tag : 'div',
31121                 cls : 'column col-md-12',
31122                 cn : managerWidget
31123             }
31124         ];
31125         
31126         if(this.fieldLabel.length){
31127             
31128             content = [
31129                 {
31130                     tag : 'div',
31131                     cls : 'column col-md-12',
31132                     html : this.fieldLabel
31133                 },
31134                 {
31135                     tag : 'div',
31136                     cls : 'column col-md-12',
31137                     cn : managerWidget
31138                 }
31139             ];
31140
31141             if(this.labelAlign == 'left'){
31142                 content = [
31143                     {
31144                         tag : 'div',
31145                         cls : 'column',
31146                         html : this.fieldLabel
31147                     },
31148                     {
31149                         tag : 'div',
31150                         cls : 'column',
31151                         cn : managerWidget
31152                     }
31153                 ];
31154                 
31155                 if(this.labelWidth > 12){
31156                     content[0].style = "width: " + this.labelWidth + 'px';
31157                 }
31158
31159                 if(this.labelWidth < 13 && this.labelmd == 0){
31160                     this.labelmd = this.labelWidth;
31161                 }
31162
31163                 if(this.labellg > 0){
31164                     content[0].cls += ' col-lg-' + this.labellg;
31165                     content[1].cls += ' col-lg-' + (12 - this.labellg);
31166                 }
31167
31168                 if(this.labelmd > 0){
31169                     content[0].cls += ' col-md-' + this.labelmd;
31170                     content[1].cls += ' col-md-' + (12 - this.labelmd);
31171                 }
31172
31173                 if(this.labelsm > 0){
31174                     content[0].cls += ' col-sm-' + this.labelsm;
31175                     content[1].cls += ' col-sm-' + (12 - this.labelsm);
31176                 }
31177
31178                 if(this.labelxs > 0){
31179                     content[0].cls += ' col-xs-' + this.labelxs;
31180                     content[1].cls += ' col-xs-' + (12 - this.labelxs);
31181                 }
31182                 
31183             }
31184         }
31185         
31186         var cfg = {
31187             tag : 'div',
31188             cls : 'row clearfix',
31189             cn : content
31190         };
31191         
31192         return cfg;
31193         
31194     },
31195     
31196     initEvents : function()
31197     {
31198         this.managerEl = this.el.select('.roo-document-manager', true).first();
31199         this.managerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
31200         
31201         this.selectorEl = this.el.select('.roo-document-manager-selector', true).first();
31202         this.selectorEl.hide();
31203         
31204         if(this.multiple){
31205             this.selectorEl.attr('multiple', 'multiple');
31206         }
31207         
31208         this.selectorEl.on('change', this.onFileSelected, this);
31209         
31210         this.uploader = this.el.select('.roo-document-manager-uploader', true).first();
31211         this.uploader.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
31212         
31213         this.uploader.on('click', this.onUploaderClick, this);
31214         
31215         this.renderProgressDialog();
31216         
31217         var _this = this;
31218         
31219         window.addEventListener("resize", function() { _this.refresh(); } );
31220         
31221         this.fireEvent('initial', this);
31222     },
31223     
31224     renderProgressDialog : function()
31225     {
31226         var _this = this;
31227         
31228         this.progressDialog = new Roo.bootstrap.Modal({
31229             cls : 'roo-document-manager-progress-dialog',
31230             allow_close : false,
31231             animate : false,
31232             title : '',
31233             buttons : [
31234                 {
31235                     name  :'cancel',
31236                     weight : 'danger',
31237                     html : 'Cancel'
31238                 }
31239             ], 
31240             listeners : { 
31241                 btnclick : function() {
31242                     _this.uploadCancel();
31243                     this.hide();
31244                 }
31245             }
31246         });
31247          
31248         this.progressDialog.render(Roo.get(document.body));
31249          
31250         this.progress = new Roo.bootstrap.Progress({
31251             cls : 'roo-document-manager-progress',
31252             active : true,
31253             striped : true
31254         });
31255         
31256         this.progress.render(this.progressDialog.getChildContainer());
31257         
31258         this.progressBar = new Roo.bootstrap.ProgressBar({
31259             cls : 'roo-document-manager-progress-bar',
31260             aria_valuenow : 0,
31261             aria_valuemin : 0,
31262             aria_valuemax : 12,
31263             panel : 'success'
31264         });
31265         
31266         this.progressBar.render(this.progress.getChildContainer());
31267     },
31268     
31269     onUploaderClick : function(e)
31270     {
31271         e.preventDefault();
31272      
31273         if(this.fireEvent('beforeselectfile', this) != false){
31274             this.selectorEl.dom.click();
31275         }
31276         
31277     },
31278     
31279     onFileSelected : function(e)
31280     {
31281         e.preventDefault();
31282         
31283         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
31284             return;
31285         }
31286         
31287         Roo.each(this.selectorEl.dom.files, function(file){
31288             if(this.fireEvent('inspect', this, file) != false){
31289                 this.files.push(file);
31290             }
31291         }, this);
31292         
31293         this.queue();
31294         
31295     },
31296     
31297     queue : function()
31298     {
31299         this.selectorEl.dom.value = '';
31300         
31301         if(!this.files || !this.files.length){
31302             return;
31303         }
31304         
31305         if(this.boxes > 0 && this.files.length > this.boxes){
31306             this.files = this.files.slice(0, this.boxes);
31307         }
31308         
31309         this.uploader.show();
31310         
31311         if(this.boxes > 0 && this.files.length > this.boxes - 1){
31312             this.uploader.hide();
31313         }
31314         
31315         var _this = this;
31316         
31317         var files = [];
31318         
31319         var docs = [];
31320         
31321         Roo.each(this.files, function(file){
31322             
31323             if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
31324                 var f = this.renderPreview(file);
31325                 files.push(f);
31326                 return;
31327             }
31328             
31329             if(file.type.indexOf('image') != -1){
31330                 this.delegates.push(
31331                     (function(){
31332                         _this.process(file);
31333                     }).createDelegate(this)
31334                 );
31335         
31336                 return;
31337             }
31338             
31339             docs.push(
31340                 (function(){
31341                     _this.process(file);
31342                 }).createDelegate(this)
31343             );
31344             
31345         }, this);
31346         
31347         this.files = files;
31348         
31349         this.delegates = this.delegates.concat(docs);
31350         
31351         if(!this.delegates.length){
31352             this.refresh();
31353             return;
31354         }
31355         
31356         this.progressBar.aria_valuemax = this.delegates.length;
31357         
31358         this.arrange();
31359         
31360         return;
31361     },
31362     
31363     arrange : function()
31364     {
31365         if(!this.delegates.length){
31366             this.progressDialog.hide();
31367             this.refresh();
31368             return;
31369         }
31370         
31371         var delegate = this.delegates.shift();
31372         
31373         this.progressDialog.show();
31374         
31375         this.progressDialog.setTitle((this.progressBar.aria_valuemax - this.delegates.length) + ' / ' + this.progressBar.aria_valuemax);
31376         
31377         this.progressBar.update(this.progressBar.aria_valuemax - this.delegates.length);
31378         
31379         delegate();
31380     },
31381     
31382     refresh : function()
31383     {
31384         this.uploader.show();
31385         
31386         if(this.boxes > 0 && this.files.length > this.boxes - 1){
31387             this.uploader.hide();
31388         }
31389         
31390         Roo.isTouch ? this.closable(false) : this.closable(true);
31391         
31392         this.fireEvent('refresh', this);
31393     },
31394     
31395     onRemove : function(e, el, o)
31396     {
31397         e.preventDefault();
31398         
31399         this.fireEvent('remove', this, o);
31400         
31401     },
31402     
31403     remove : function(o)
31404     {
31405         var files = [];
31406         
31407         Roo.each(this.files, function(file){
31408             if(typeof(file.id) == 'undefined' || file.id * 1 < 1 || file.id != o.id){
31409                 files.push(file);
31410                 return;
31411             }
31412
31413             o.target.remove();
31414
31415         }, this);
31416         
31417         this.files = files;
31418         
31419         this.refresh();
31420     },
31421     
31422     clear : function()
31423     {
31424         Roo.each(this.files, function(file){
31425             if(!file.target){
31426                 return;
31427             }
31428             
31429             file.target.remove();
31430
31431         }, this);
31432         
31433         this.files = [];
31434         
31435         this.refresh();
31436     },
31437     
31438     onClick : function(e, el, o)
31439     {
31440         e.preventDefault();
31441         
31442         this.fireEvent('click', this, o);
31443         
31444     },
31445     
31446     closable : function(closable)
31447     {
31448         Roo.each(this.managerEl.select('.roo-document-manager-preview > button.close', true).elements, function(el){
31449             
31450             el.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
31451             
31452             if(closable){
31453                 el.show();
31454                 return;
31455             }
31456             
31457             el.hide();
31458             
31459         }, this);
31460     },
31461     
31462     xhrOnLoad : function(xhr)
31463     {
31464         Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
31465             el.remove();
31466         }, this);
31467         
31468         if (xhr.readyState !== 4) {
31469             this.arrange();
31470             this.fireEvent('exception', this, xhr);
31471             return;
31472         }
31473
31474         var response = Roo.decode(xhr.responseText);
31475         
31476         if(!response.success){
31477             this.arrange();
31478             this.fireEvent('exception', this, xhr);
31479             return;
31480         }
31481         
31482         var file = this.renderPreview(response.data);
31483         
31484         this.files.push(file);
31485         
31486         this.arrange();
31487         
31488         this.fireEvent('afterupload', this, xhr);
31489         
31490     },
31491     
31492     xhrOnError : function(xhr)
31493     {
31494         Roo.log('xhr on error');
31495         
31496         var response = Roo.decode(xhr.responseText);
31497           
31498         Roo.log(response);
31499         
31500         this.arrange();
31501     },
31502     
31503     process : function(file)
31504     {
31505         if(this.fireEvent('process', this, file) !== false){
31506             if(this.editable && file.type.indexOf('image') != -1){
31507                 this.fireEvent('edit', this, file);
31508                 return;
31509             }
31510
31511             this.uploadStart(file, false);
31512
31513             return;
31514         }
31515         
31516     },
31517     
31518     uploadStart : function(file, crop)
31519     {
31520         this.xhr = new XMLHttpRequest();
31521         
31522         if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
31523             this.arrange();
31524             return;
31525         }
31526         
31527         file.xhr = this.xhr;
31528             
31529         this.managerEl.createChild({
31530             tag : 'div',
31531             cls : 'roo-document-manager-loading',
31532             cn : [
31533                 {
31534                     tag : 'div',
31535                     tooltip : file.name,
31536                     cls : 'roo-document-manager-thumb',
31537                     html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
31538                 }
31539             ]
31540
31541         });
31542
31543         this.xhr.open(this.method, this.url, true);
31544         
31545         var headers = {
31546             "Accept": "application/json",
31547             "Cache-Control": "no-cache",
31548             "X-Requested-With": "XMLHttpRequest"
31549         };
31550         
31551         for (var headerName in headers) {
31552             var headerValue = headers[headerName];
31553             if (headerValue) {
31554                 this.xhr.setRequestHeader(headerName, headerValue);
31555             }
31556         }
31557         
31558         var _this = this;
31559         
31560         this.xhr.onload = function()
31561         {
31562             _this.xhrOnLoad(_this.xhr);
31563         }
31564         
31565         this.xhr.onerror = function()
31566         {
31567             _this.xhrOnError(_this.xhr);
31568         }
31569         
31570         var formData = new FormData();
31571
31572         formData.append('returnHTML', 'NO');
31573         
31574         if(crop){
31575             formData.append('crop', crop);
31576         }
31577         
31578         formData.append(this.paramName, file, file.name);
31579         
31580         var options = {
31581             file : file, 
31582             manually : false
31583         };
31584         
31585         if(this.fireEvent('prepare', this, formData, options) != false){
31586             
31587             if(options.manually){
31588                 return;
31589             }
31590             
31591             this.xhr.send(formData);
31592             return;
31593         };
31594         
31595         this.uploadCancel();
31596     },
31597     
31598     uploadCancel : function()
31599     {
31600         if (this.xhr) {
31601             this.xhr.abort();
31602         }
31603         
31604         this.delegates = [];
31605         
31606         Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
31607             el.remove();
31608         }, this);
31609         
31610         this.arrange();
31611     },
31612     
31613     renderPreview : function(file)
31614     {
31615         if(typeof(file.target) != 'undefined' && file.target){
31616             return file;
31617         }
31618         
31619         var img_src = encodeURI(baseURL +'/Images/Thumb/' + this.thumbSize + '/' + file.id + '/' + file.filename);
31620         
31621         var previewEl = this.managerEl.createChild({
31622             tag : 'div',
31623             cls : 'roo-document-manager-preview',
31624             cn : [
31625                 {
31626                     tag : 'div',
31627                     tooltip : file[this.toolTipName],
31628                     cls : 'roo-document-manager-thumb',
31629                     html : '<img tooltip="' + file[this.toolTipName] + '" src="' + img_src + '">'
31630                 },
31631                 {
31632                     tag : 'button',
31633                     cls : 'close',
31634                     html : '<i class="fa fa-times-circle"></i>'
31635                 }
31636             ]
31637         });
31638
31639         var close = previewEl.select('button.close', true).first();
31640
31641         close.on('click', this.onRemove, this, file);
31642
31643         file.target = previewEl;
31644
31645         var image = previewEl.select('img', true).first();
31646         
31647         var _this = this;
31648         
31649         image.dom.addEventListener("load", function(){ _this.onPreviewLoad(file, image); });
31650         
31651         image.on('click', this.onClick, this, file);
31652         
31653         this.fireEvent('previewrendered', this, file);
31654         
31655         return file;
31656         
31657     },
31658     
31659     onPreviewLoad : function(file, image)
31660     {
31661         if(typeof(file.target) == 'undefined' || !file.target){
31662             return;
31663         }
31664         
31665         var width = image.dom.naturalWidth || image.dom.width;
31666         var height = image.dom.naturalHeight || image.dom.height;
31667         
31668         if(!this.previewResize) {
31669             return;
31670         }
31671         
31672         if(width > height){
31673             file.target.addClass('wide');
31674             return;
31675         }
31676         
31677         file.target.addClass('tall');
31678         return;
31679         
31680     },
31681     
31682     uploadFromSource : function(file, crop)
31683     {
31684         this.xhr = new XMLHttpRequest();
31685         
31686         this.managerEl.createChild({
31687             tag : 'div',
31688             cls : 'roo-document-manager-loading',
31689             cn : [
31690                 {
31691                     tag : 'div',
31692                     tooltip : file.name,
31693                     cls : 'roo-document-manager-thumb',
31694                     html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
31695                 }
31696             ]
31697
31698         });
31699
31700         this.xhr.open(this.method, this.url, true);
31701         
31702         var headers = {
31703             "Accept": "application/json",
31704             "Cache-Control": "no-cache",
31705             "X-Requested-With": "XMLHttpRequest"
31706         };
31707         
31708         for (var headerName in headers) {
31709             var headerValue = headers[headerName];
31710             if (headerValue) {
31711                 this.xhr.setRequestHeader(headerName, headerValue);
31712             }
31713         }
31714         
31715         var _this = this;
31716         
31717         this.xhr.onload = function()
31718         {
31719             _this.xhrOnLoad(_this.xhr);
31720         }
31721         
31722         this.xhr.onerror = function()
31723         {
31724             _this.xhrOnError(_this.xhr);
31725         }
31726         
31727         var formData = new FormData();
31728
31729         formData.append('returnHTML', 'NO');
31730         
31731         formData.append('crop', crop);
31732         
31733         if(typeof(file.filename) != 'undefined'){
31734             formData.append('filename', file.filename);
31735         }
31736         
31737         if(typeof(file.mimetype) != 'undefined'){
31738             formData.append('mimetype', file.mimetype);
31739         }
31740         
31741         Roo.log(formData);
31742         
31743         if(this.fireEvent('prepare', this, formData) != false){
31744             this.xhr.send(formData);
31745         };
31746     }
31747 });
31748
31749 /*
31750 * Licence: LGPL
31751 */
31752
31753 /**
31754  * @class Roo.bootstrap.DocumentViewer
31755  * @extends Roo.bootstrap.Component
31756  * Bootstrap DocumentViewer class
31757  * @cfg {Boolean} showDownload (true|false) show download button (default true)
31758  * @cfg {Boolean} showTrash (true|false) show trash button (default true)
31759  * 
31760  * @constructor
31761  * Create a new DocumentViewer
31762  * @param {Object} config The config object
31763  */
31764
31765 Roo.bootstrap.DocumentViewer = function(config){
31766     Roo.bootstrap.DocumentViewer.superclass.constructor.call(this, config);
31767     
31768     this.addEvents({
31769         /**
31770          * @event initial
31771          * Fire after initEvent
31772          * @param {Roo.bootstrap.DocumentViewer} this
31773          */
31774         "initial" : true,
31775         /**
31776          * @event click
31777          * Fire after click
31778          * @param {Roo.bootstrap.DocumentViewer} this
31779          */
31780         "click" : true,
31781         /**
31782          * @event download
31783          * Fire after download button
31784          * @param {Roo.bootstrap.DocumentViewer} this
31785          */
31786         "download" : true,
31787         /**
31788          * @event trash
31789          * Fire after trash button
31790          * @param {Roo.bootstrap.DocumentViewer} this
31791          */
31792         "trash" : true
31793         
31794     });
31795 };
31796
31797 Roo.extend(Roo.bootstrap.DocumentViewer, Roo.bootstrap.Component,  {
31798     
31799     showDownload : true,
31800     
31801     showTrash : true,
31802     
31803     getAutoCreate : function()
31804     {
31805         var cfg = {
31806             tag : 'div',
31807             cls : 'roo-document-viewer',
31808             cn : [
31809                 {
31810                     tag : 'div',
31811                     cls : 'roo-document-viewer-body',
31812                     cn : [
31813                         {
31814                             tag : 'div',
31815                             cls : 'roo-document-viewer-thumb',
31816                             cn : [
31817                                 {
31818                                     tag : 'img',
31819                                     cls : 'roo-document-viewer-image'
31820                                 }
31821                             ]
31822                         }
31823                     ]
31824                 },
31825                 {
31826                     tag : 'div',
31827                     cls : 'roo-document-viewer-footer',
31828                     cn : {
31829                         tag : 'div',
31830                         cls : 'btn-group btn-group-justified roo-document-viewer-btn-group',
31831                         cn : [
31832                             {
31833                                 tag : 'div',
31834                                 cls : 'btn-group roo-document-viewer-download',
31835                                 cn : [
31836                                     {
31837                                         tag : 'button',
31838                                         cls : 'btn btn-default',
31839                                         html : '<i class="fa fa-download"></i>'
31840                                     }
31841                                 ]
31842                             },
31843                             {
31844                                 tag : 'div',
31845                                 cls : 'btn-group roo-document-viewer-trash',
31846                                 cn : [
31847                                     {
31848                                         tag : 'button',
31849                                         cls : 'btn btn-default',
31850                                         html : '<i class="fa fa-trash"></i>'
31851                                     }
31852                                 ]
31853                             }
31854                         ]
31855                     }
31856                 }
31857             ]
31858         };
31859         
31860         return cfg;
31861     },
31862     
31863     initEvents : function()
31864     {
31865         this.bodyEl = this.el.select('.roo-document-viewer-body', true).first();
31866         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
31867         
31868         this.thumbEl = this.el.select('.roo-document-viewer-thumb', true).first();
31869         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
31870         
31871         this.imageEl = this.el.select('.roo-document-viewer-image', true).first();
31872         this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
31873         
31874         this.footerEl = this.el.select('.roo-document-viewer-footer', true).first();
31875         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY);
31876         
31877         this.downloadBtn = this.el.select('.roo-document-viewer-download', true).first();
31878         this.downloadBtn.setVisibilityMode(Roo.Element.DISPLAY);
31879         
31880         this.trashBtn = this.el.select('.roo-document-viewer-trash', true).first();
31881         this.trashBtn.setVisibilityMode(Roo.Element.DISPLAY);
31882         
31883         this.bodyEl.on('click', this.onClick, this);
31884         this.downloadBtn.on('click', this.onDownload, this);
31885         this.trashBtn.on('click', this.onTrash, this);
31886         
31887         this.downloadBtn.hide();
31888         this.trashBtn.hide();
31889         
31890         if(this.showDownload){
31891             this.downloadBtn.show();
31892         }
31893         
31894         if(this.showTrash){
31895             this.trashBtn.show();
31896         }
31897         
31898         if(!this.showDownload && !this.showTrash) {
31899             this.footerEl.hide();
31900         }
31901         
31902     },
31903     
31904     initial : function()
31905     {
31906         this.fireEvent('initial', this);
31907         
31908     },
31909     
31910     onClick : function(e)
31911     {
31912         e.preventDefault();
31913         
31914         this.fireEvent('click', this);
31915     },
31916     
31917     onDownload : function(e)
31918     {
31919         e.preventDefault();
31920         
31921         this.fireEvent('download', this);
31922     },
31923     
31924     onTrash : function(e)
31925     {
31926         e.preventDefault();
31927         
31928         this.fireEvent('trash', this);
31929     }
31930     
31931 });
31932 /*
31933  * - LGPL
31934  *
31935  * nav progress bar
31936  * 
31937  */
31938
31939 /**
31940  * @class Roo.bootstrap.NavProgressBar
31941  * @extends Roo.bootstrap.Component
31942  * Bootstrap NavProgressBar class
31943  * 
31944  * @constructor
31945  * Create a new nav progress bar
31946  * @param {Object} config The config object
31947  */
31948
31949 Roo.bootstrap.NavProgressBar = function(config){
31950     Roo.bootstrap.NavProgressBar.superclass.constructor.call(this, config);
31951
31952     this.bullets = this.bullets || [];
31953    
31954 //    Roo.bootstrap.NavProgressBar.register(this);
31955      this.addEvents({
31956         /**
31957              * @event changed
31958              * Fires when the active item changes
31959              * @param {Roo.bootstrap.NavProgressBar} this
31960              * @param {Roo.bootstrap.NavProgressItem} selected The item selected
31961              * @param {Roo.bootstrap.NavProgressItem} prev The previously selected item 
31962          */
31963         'changed': true
31964      });
31965     
31966 };
31967
31968 Roo.extend(Roo.bootstrap.NavProgressBar, Roo.bootstrap.Component,  {
31969     
31970     bullets : [],
31971     barItems : [],
31972     
31973     getAutoCreate : function()
31974     {
31975         var cfg = Roo.apply({}, Roo.bootstrap.NavProgressBar.superclass.getAutoCreate.call(this));
31976         
31977         cfg = {
31978             tag : 'div',
31979             cls : 'roo-navigation-bar-group',
31980             cn : [
31981                 {
31982                     tag : 'div',
31983                     cls : 'roo-navigation-top-bar'
31984                 },
31985                 {
31986                     tag : 'div',
31987                     cls : 'roo-navigation-bullets-bar',
31988                     cn : [
31989                         {
31990                             tag : 'ul',
31991                             cls : 'roo-navigation-bar'
31992                         }
31993                     ]
31994                 },
31995                 
31996                 {
31997                     tag : 'div',
31998                     cls : 'roo-navigation-bottom-bar'
31999                 }
32000             ]
32001             
32002         };
32003         
32004         return cfg;
32005         
32006     },
32007     
32008     initEvents: function() 
32009     {
32010         
32011     },
32012     
32013     onRender : function(ct, position) 
32014     {
32015         Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
32016         
32017         if(this.bullets.length){
32018             Roo.each(this.bullets, function(b){
32019                this.addItem(b);
32020             }, this);
32021         }
32022         
32023         this.format();
32024         
32025     },
32026     
32027     addItem : function(cfg)
32028     {
32029         var item = new Roo.bootstrap.NavProgressItem(cfg);
32030         
32031         item.parentId = this.id;
32032         item.render(this.el.select('.roo-navigation-bar', true).first(), null);
32033         
32034         if(cfg.html){
32035             var top = new Roo.bootstrap.Element({
32036                 tag : 'div',
32037                 cls : 'roo-navigation-bar-text'
32038             });
32039             
32040             var bottom = new Roo.bootstrap.Element({
32041                 tag : 'div',
32042                 cls : 'roo-navigation-bar-text'
32043             });
32044             
32045             top.onRender(this.el.select('.roo-navigation-top-bar', true).first(), null);
32046             bottom.onRender(this.el.select('.roo-navigation-bottom-bar', true).first(), null);
32047             
32048             var topText = new Roo.bootstrap.Element({
32049                 tag : 'span',
32050                 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? cfg.html : ''
32051             });
32052             
32053             var bottomText = new Roo.bootstrap.Element({
32054                 tag : 'span',
32055                 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? '' : cfg.html
32056             });
32057             
32058             topText.onRender(top.el, null);
32059             bottomText.onRender(bottom.el, null);
32060             
32061             item.topEl = top;
32062             item.bottomEl = bottom;
32063         }
32064         
32065         this.barItems.push(item);
32066         
32067         return item;
32068     },
32069     
32070     getActive : function()
32071     {
32072         var active = false;
32073         
32074         Roo.each(this.barItems, function(v){
32075             
32076             if (!v.isActive()) {
32077                 return;
32078             }
32079             
32080             active = v;
32081             return false;
32082             
32083         });
32084         
32085         return active;
32086     },
32087     
32088     setActiveItem : function(item)
32089     {
32090         var prev = false;
32091         
32092         Roo.each(this.barItems, function(v){
32093             if (v.rid == item.rid) {
32094                 return ;
32095             }
32096             
32097             if (v.isActive()) {
32098                 v.setActive(false);
32099                 prev = v;
32100             }
32101         });
32102
32103         item.setActive(true);
32104         
32105         this.fireEvent('changed', this, item, prev);
32106     },
32107     
32108     getBarItem: function(rid)
32109     {
32110         var ret = false;
32111         
32112         Roo.each(this.barItems, function(e) {
32113             if (e.rid != rid) {
32114                 return;
32115             }
32116             
32117             ret =  e;
32118             return false;
32119         });
32120         
32121         return ret;
32122     },
32123     
32124     indexOfItem : function(item)
32125     {
32126         var index = false;
32127         
32128         Roo.each(this.barItems, function(v, i){
32129             
32130             if (v.rid != item.rid) {
32131                 return;
32132             }
32133             
32134             index = i;
32135             return false
32136         });
32137         
32138         return index;
32139     },
32140     
32141     setActiveNext : function()
32142     {
32143         var i = this.indexOfItem(this.getActive());
32144         
32145         if (i > this.barItems.length) {
32146             return;
32147         }
32148         
32149         this.setActiveItem(this.barItems[i+1]);
32150     },
32151     
32152     setActivePrev : function()
32153     {
32154         var i = this.indexOfItem(this.getActive());
32155         
32156         if (i  < 1) {
32157             return;
32158         }
32159         
32160         this.setActiveItem(this.barItems[i-1]);
32161     },
32162     
32163     format : function()
32164     {
32165         if(!this.barItems.length){
32166             return;
32167         }
32168      
32169         var width = 100 / this.barItems.length;
32170         
32171         Roo.each(this.barItems, function(i){
32172             i.el.setStyle('width', width + '%');
32173             i.topEl.el.setStyle('width', width + '%');
32174             i.bottomEl.el.setStyle('width', width + '%');
32175         }, this);
32176         
32177     }
32178     
32179 });
32180 /*
32181  * - LGPL
32182  *
32183  * Nav Progress Item
32184  * 
32185  */
32186
32187 /**
32188  * @class Roo.bootstrap.NavProgressItem
32189  * @extends Roo.bootstrap.Component
32190  * Bootstrap NavProgressItem class
32191  * @cfg {String} rid the reference id
32192  * @cfg {Boolean} active (true|false) Is item active default false
32193  * @cfg {Boolean} disabled (true|false) Is item active default false
32194  * @cfg {String} html
32195  * @cfg {String} position (top|bottom) text position default bottom
32196  * @cfg {String} icon show icon instead of number
32197  * 
32198  * @constructor
32199  * Create a new NavProgressItem
32200  * @param {Object} config The config object
32201  */
32202 Roo.bootstrap.NavProgressItem = function(config){
32203     Roo.bootstrap.NavProgressItem.superclass.constructor.call(this, config);
32204     this.addEvents({
32205         // raw events
32206         /**
32207          * @event click
32208          * The raw click event for the entire grid.
32209          * @param {Roo.bootstrap.NavProgressItem} this
32210          * @param {Roo.EventObject} e
32211          */
32212         "click" : true
32213     });
32214    
32215 };
32216
32217 Roo.extend(Roo.bootstrap.NavProgressItem, Roo.bootstrap.Component,  {
32218     
32219     rid : '',
32220     active : false,
32221     disabled : false,
32222     html : '',
32223     position : 'bottom',
32224     icon : false,
32225     
32226     getAutoCreate : function()
32227     {
32228         var iconCls = 'roo-navigation-bar-item-icon';
32229         
32230         iconCls += ((this.icon) ? (' ' + this.icon) : (' step-number')) ;
32231         
32232         var cfg = {
32233             tag: 'li',
32234             cls: 'roo-navigation-bar-item',
32235             cn : [
32236                 {
32237                     tag : 'i',
32238                     cls : iconCls
32239                 }
32240             ]
32241         };
32242         
32243         if(this.active){
32244             cfg.cls += ' active';
32245         }
32246         if(this.disabled){
32247             cfg.cls += ' disabled';
32248         }
32249         
32250         return cfg;
32251     },
32252     
32253     disable : function()
32254     {
32255         this.setDisabled(true);
32256     },
32257     
32258     enable : function()
32259     {
32260         this.setDisabled(false);
32261     },
32262     
32263     initEvents: function() 
32264     {
32265         this.iconEl = this.el.select('.roo-navigation-bar-item-icon', true).first();
32266         
32267         this.iconEl.on('click', this.onClick, this);
32268     },
32269     
32270     onClick : function(e)
32271     {
32272         e.preventDefault();
32273         
32274         if(this.disabled){
32275             return;
32276         }
32277         
32278         if(this.fireEvent('click', this, e) === false){
32279             return;
32280         };
32281         
32282         this.parent().setActiveItem(this);
32283     },
32284     
32285     isActive: function () 
32286     {
32287         return this.active;
32288     },
32289     
32290     setActive : function(state)
32291     {
32292         if(this.active == state){
32293             return;
32294         }
32295         
32296         this.active = state;
32297         
32298         if (state) {
32299             this.el.addClass('active');
32300             return;
32301         }
32302         
32303         this.el.removeClass('active');
32304         
32305         return;
32306     },
32307     
32308     setDisabled : function(state)
32309     {
32310         if(this.disabled == state){
32311             return;
32312         }
32313         
32314         this.disabled = state;
32315         
32316         if (state) {
32317             this.el.addClass('disabled');
32318             return;
32319         }
32320         
32321         this.el.removeClass('disabled');
32322     },
32323     
32324     tooltipEl : function()
32325     {
32326         return this.el.select('.roo-navigation-bar-item-icon', true).first();;
32327     }
32328 });
32329  
32330
32331  /*
32332  * - LGPL
32333  *
32334  * FieldLabel
32335  * 
32336  */
32337
32338 /**
32339  * @class Roo.bootstrap.FieldLabel
32340  * @extends Roo.bootstrap.Component
32341  * Bootstrap FieldLabel class
32342  * @cfg {String} html contents of the element
32343  * @cfg {String} tag tag of the element default label
32344  * @cfg {String} cls class of the element
32345  * @cfg {String} target label target 
32346  * @cfg {Boolean} allowBlank (true|false) target allowBlank default true
32347  * @cfg {String} invalidClass DEPRICATED - BS4 uses is-invalid
32348  * @cfg {String} validClass DEPRICATED - BS4 uses is-valid
32349  * @cfg {String} iconTooltip default "This field is required"
32350  * @cfg {String} indicatorpos (left|right) default left
32351  * 
32352  * @constructor
32353  * Create a new FieldLabel
32354  * @param {Object} config The config object
32355  */
32356
32357 Roo.bootstrap.FieldLabel = function(config){
32358     Roo.bootstrap.Element.superclass.constructor.call(this, config);
32359     
32360     this.addEvents({
32361             /**
32362              * @event invalid
32363              * Fires after the field has been marked as invalid.
32364              * @param {Roo.form.FieldLabel} this
32365              * @param {String} msg The validation message
32366              */
32367             invalid : true,
32368             /**
32369              * @event valid
32370              * Fires after the field has been validated with no errors.
32371              * @param {Roo.form.FieldLabel} this
32372              */
32373             valid : true
32374         });
32375 };
32376
32377 Roo.extend(Roo.bootstrap.FieldLabel, Roo.bootstrap.Component,  {
32378     
32379     tag: 'label',
32380     cls: '',
32381     html: '',
32382     target: '',
32383     allowBlank : true,
32384     invalidClass : 'has-warning',
32385     validClass : 'has-success',
32386     iconTooltip : 'This field is required',
32387     indicatorpos : 'left',
32388     
32389     getAutoCreate : function(){
32390         
32391         var cls = "";
32392         if (!this.allowBlank) {
32393             cls  = "visible";
32394         }
32395         
32396         var cfg = {
32397             tag : this.tag,
32398             cls : 'roo-bootstrap-field-label ' + this.cls,
32399             for : this.target,
32400             cn : [
32401                 {
32402                     tag : 'i',
32403                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star ' + cls,
32404                     tooltip : this.iconTooltip
32405                 },
32406                 {
32407                     tag : 'span',
32408                     html : this.html
32409                 }
32410             ] 
32411         };
32412         
32413         if(this.indicatorpos == 'right'){
32414             var cfg = {
32415                 tag : this.tag,
32416                 cls : 'roo-bootstrap-field-label ' + this.cls,
32417                 for : this.target,
32418                 cn : [
32419                     {
32420                         tag : 'span',
32421                         html : this.html
32422                     },
32423                     {
32424                         tag : 'i',
32425                         cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star '+ cls,
32426                         tooltip : this.iconTooltip
32427                     }
32428                 ] 
32429             };
32430         }
32431         
32432         return cfg;
32433     },
32434     
32435     initEvents: function() 
32436     {
32437         Roo.bootstrap.Element.superclass.initEvents.call(this);
32438         
32439         this.indicator = this.indicatorEl();
32440         
32441         if(this.indicator){
32442             this.indicator.removeClass('visible');
32443             this.indicator.addClass('invisible');
32444         }
32445         
32446         Roo.bootstrap.FieldLabel.register(this);
32447     },
32448     
32449     indicatorEl : function()
32450     {
32451         var indicator = this.el.select('i.roo-required-indicator',true).first();
32452         
32453         if(!indicator){
32454             return false;
32455         }
32456         
32457         return indicator;
32458         
32459     },
32460     
32461     /**
32462      * Mark this field as valid
32463      */
32464     markValid : function()
32465     {
32466         if(this.indicator){
32467             this.indicator.removeClass('visible');
32468             this.indicator.addClass('invisible');
32469         }
32470         if (Roo.bootstrap.version == 3) {
32471             this.el.removeClass(this.invalidClass);
32472             this.el.addClass(this.validClass);
32473         } else {
32474             this.el.removeClass('is-invalid');
32475             this.el.addClass('is-valid');
32476         }
32477         
32478         
32479         this.fireEvent('valid', this);
32480     },
32481     
32482     /**
32483      * Mark this field as invalid
32484      * @param {String} msg The validation message
32485      */
32486     markInvalid : function(msg)
32487     {
32488         if(this.indicator){
32489             this.indicator.removeClass('invisible');
32490             this.indicator.addClass('visible');
32491         }
32492           if (Roo.bootstrap.version == 3) {
32493             this.el.removeClass(this.validClass);
32494             this.el.addClass(this.invalidClass);
32495         } else {
32496             this.el.removeClass('is-valid');
32497             this.el.addClass('is-invalid');
32498         }
32499         
32500         
32501         this.fireEvent('invalid', this, msg);
32502     }
32503     
32504    
32505 });
32506
32507 Roo.apply(Roo.bootstrap.FieldLabel, {
32508     
32509     groups: {},
32510     
32511      /**
32512     * register a FieldLabel Group
32513     * @param {Roo.bootstrap.FieldLabel} the FieldLabel to add
32514     */
32515     register : function(label)
32516     {
32517         if(this.groups.hasOwnProperty(label.target)){
32518             return;
32519         }
32520      
32521         this.groups[label.target] = label;
32522         
32523     },
32524     /**
32525     * fetch a FieldLabel Group based on the target
32526     * @param {string} target
32527     * @returns {Roo.bootstrap.FieldLabel} the CheckBox group
32528     */
32529     get: function(target) {
32530         if (typeof(this.groups[target]) == 'undefined') {
32531             return false;
32532         }
32533         
32534         return this.groups[target] ;
32535     }
32536 });
32537
32538  
32539
32540  /*
32541  * - LGPL
32542  *
32543  * page DateSplitField.
32544  * 
32545  */
32546
32547
32548 /**
32549  * @class Roo.bootstrap.DateSplitField
32550  * @extends Roo.bootstrap.Component
32551  * Bootstrap DateSplitField class
32552  * @cfg {string} fieldLabel - the label associated
32553  * @cfg {Number} labelWidth set the width of label (0-12)
32554  * @cfg {String} labelAlign (top|left)
32555  * @cfg {Boolean} dayAllowBlank (true|false) default false
32556  * @cfg {Boolean} monthAllowBlank (true|false) default false
32557  * @cfg {Boolean} yearAllowBlank (true|false) default false
32558  * @cfg {string} dayPlaceholder 
32559  * @cfg {string} monthPlaceholder
32560  * @cfg {string} yearPlaceholder
32561  * @cfg {string} dayFormat default 'd'
32562  * @cfg {string} monthFormat default 'm'
32563  * @cfg {string} yearFormat default 'Y'
32564  * @cfg {Number} labellg set the width of label (1-12)
32565  * @cfg {Number} labelmd set the width of label (1-12)
32566  * @cfg {Number} labelsm set the width of label (1-12)
32567  * @cfg {Number} labelxs set the width of label (1-12)
32568
32569  *     
32570  * @constructor
32571  * Create a new DateSplitField
32572  * @param {Object} config The config object
32573  */
32574
32575 Roo.bootstrap.DateSplitField = function(config){
32576     Roo.bootstrap.DateSplitField.superclass.constructor.call(this, config);
32577     
32578     this.addEvents({
32579         // raw events
32580          /**
32581          * @event years
32582          * getting the data of years
32583          * @param {Roo.bootstrap.DateSplitField} this
32584          * @param {Object} years
32585          */
32586         "years" : true,
32587         /**
32588          * @event days
32589          * getting the data of days
32590          * @param {Roo.bootstrap.DateSplitField} this
32591          * @param {Object} days
32592          */
32593         "days" : true,
32594         /**
32595          * @event invalid
32596          * Fires after the field has been marked as invalid.
32597          * @param {Roo.form.Field} this
32598          * @param {String} msg The validation message
32599          */
32600         invalid : true,
32601        /**
32602          * @event valid
32603          * Fires after the field has been validated with no errors.
32604          * @param {Roo.form.Field} this
32605          */
32606         valid : true
32607     });
32608 };
32609
32610 Roo.extend(Roo.bootstrap.DateSplitField, Roo.bootstrap.Component,  {
32611     
32612     fieldLabel : '',
32613     labelAlign : 'top',
32614     labelWidth : 3,
32615     dayAllowBlank : false,
32616     monthAllowBlank : false,
32617     yearAllowBlank : false,
32618     dayPlaceholder : '',
32619     monthPlaceholder : '',
32620     yearPlaceholder : '',
32621     dayFormat : 'd',
32622     monthFormat : 'm',
32623     yearFormat : 'Y',
32624     isFormField : true,
32625     labellg : 0,
32626     labelmd : 0,
32627     labelsm : 0,
32628     labelxs : 0,
32629     
32630     getAutoCreate : function()
32631     {
32632         var cfg = {
32633             tag : 'div',
32634             cls : 'row roo-date-split-field-group',
32635             cn : [
32636                 {
32637                     tag : 'input',
32638                     type : 'hidden',
32639                     cls : 'form-hidden-field roo-date-split-field-group-value',
32640                     name : this.name
32641                 }
32642             ]
32643         };
32644         
32645         var labelCls = 'col-md-12';
32646         var contentCls = 'col-md-4';
32647         
32648         if(this.fieldLabel){
32649             
32650             var label = {
32651                 tag : 'div',
32652                 cls : 'column roo-date-split-field-label col-md-' + ((this.labelAlign == 'top') ? '12' : this.labelWidth),
32653                 cn : [
32654                     {
32655                         tag : 'label',
32656                         html : this.fieldLabel
32657                     }
32658                 ]
32659             };
32660             
32661             if(this.labelAlign == 'left'){
32662             
32663                 if(this.labelWidth > 12){
32664                     label.style = "width: " + this.labelWidth + 'px';
32665                 }
32666
32667                 if(this.labelWidth < 13 && this.labelmd == 0){
32668                     this.labelmd = this.labelWidth;
32669                 }
32670
32671                 if(this.labellg > 0){
32672                     labelCls = ' col-lg-' + this.labellg;
32673                     contentCls = ' col-lg-' + ((12 - this.labellg) / 3);
32674                 }
32675
32676                 if(this.labelmd > 0){
32677                     labelCls = ' col-md-' + this.labelmd;
32678                     contentCls = ' col-md-' + ((12 - this.labelmd) / 3);
32679                 }
32680
32681                 if(this.labelsm > 0){
32682                     labelCls = ' col-sm-' + this.labelsm;
32683                     contentCls = ' col-sm-' + ((12 - this.labelsm) / 3);
32684                 }
32685
32686                 if(this.labelxs > 0){
32687                     labelCls = ' col-xs-' + this.labelxs;
32688                     contentCls = ' col-xs-' + ((12 - this.labelxs) / 3);
32689                 }
32690             }
32691             
32692             label.cls += ' ' + labelCls;
32693             
32694             cfg.cn.push(label);
32695         }
32696         
32697         Roo.each(['day', 'month', 'year'], function(t){
32698             cfg.cn.push({
32699                 tag : 'div',
32700                 cls : 'column roo-date-split-field-' + t + ' ' + contentCls
32701             });
32702         }, this);
32703         
32704         return cfg;
32705     },
32706     
32707     inputEl: function ()
32708     {
32709         return this.el.select('.roo-date-split-field-group-value', true).first();
32710     },
32711     
32712     onRender : function(ct, position) 
32713     {
32714         var _this = this;
32715         
32716         Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
32717         
32718         this.inputEl = this.el.select('.roo-date-split-field-group-value', true).first();
32719         
32720         this.dayField = new Roo.bootstrap.ComboBox({
32721             allowBlank : this.dayAllowBlank,
32722             alwaysQuery : true,
32723             displayField : 'value',
32724             editable : false,
32725             fieldLabel : '',
32726             forceSelection : true,
32727             mode : 'local',
32728             placeholder : this.dayPlaceholder,
32729             selectOnFocus : true,
32730             tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
32731             triggerAction : 'all',
32732             typeAhead : true,
32733             valueField : 'value',
32734             store : new Roo.data.SimpleStore({
32735                 data : (function() {    
32736                     var days = [];
32737                     _this.fireEvent('days', _this, days);
32738                     return days;
32739                 })(),
32740                 fields : [ 'value' ]
32741             }),
32742             listeners : {
32743                 select : function (_self, record, index)
32744                 {
32745                     _this.setValue(_this.getValue());
32746                 }
32747             }
32748         });
32749
32750         this.dayField.render(this.el.select('.roo-date-split-field-day', true).first(), null);
32751         
32752         this.monthField = new Roo.bootstrap.MonthField({
32753             after : '<i class=\"fa fa-calendar\"></i>',
32754             allowBlank : this.monthAllowBlank,
32755             placeholder : this.monthPlaceholder,
32756             readOnly : true,
32757             listeners : {
32758                 render : function (_self)
32759                 {
32760                     this.el.select('span.input-group-addon', true).first().on('click', function(e){
32761                         e.preventDefault();
32762                         _self.focus();
32763                     });
32764                 },
32765                 select : function (_self, oldvalue, newvalue)
32766                 {
32767                     _this.setValue(_this.getValue());
32768                 }
32769             }
32770         });
32771         
32772         this.monthField.render(this.el.select('.roo-date-split-field-month', true).first(), null);
32773         
32774         this.yearField = new Roo.bootstrap.ComboBox({
32775             allowBlank : this.yearAllowBlank,
32776             alwaysQuery : true,
32777             displayField : 'value',
32778             editable : false,
32779             fieldLabel : '',
32780             forceSelection : true,
32781             mode : 'local',
32782             placeholder : this.yearPlaceholder,
32783             selectOnFocus : true,
32784             tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
32785             triggerAction : 'all',
32786             typeAhead : true,
32787             valueField : 'value',
32788             store : new Roo.data.SimpleStore({
32789                 data : (function() {
32790                     var years = [];
32791                     _this.fireEvent('years', _this, years);
32792                     return years;
32793                 })(),
32794                 fields : [ 'value' ]
32795             }),
32796             listeners : {
32797                 select : function (_self, record, index)
32798                 {
32799                     _this.setValue(_this.getValue());
32800                 }
32801             }
32802         });
32803
32804         this.yearField.render(this.el.select('.roo-date-split-field-year', true).first(), null);
32805     },
32806     
32807     setValue : function(v, format)
32808     {
32809         this.inputEl.dom.value = v;
32810         
32811         var f = format || (this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat);
32812         
32813         var d = Date.parseDate(v, f);
32814         
32815         if(!d){
32816             this.validate();
32817             return;
32818         }
32819         
32820         this.setDay(d.format(this.dayFormat));
32821         this.setMonth(d.format(this.monthFormat));
32822         this.setYear(d.format(this.yearFormat));
32823         
32824         this.validate();
32825         
32826         return;
32827     },
32828     
32829     setDay : function(v)
32830     {
32831         this.dayField.setValue(v);
32832         this.inputEl.dom.value = this.getValue();
32833         this.validate();
32834         return;
32835     },
32836     
32837     setMonth : function(v)
32838     {
32839         this.monthField.setValue(v, true);
32840         this.inputEl.dom.value = this.getValue();
32841         this.validate();
32842         return;
32843     },
32844     
32845     setYear : function(v)
32846     {
32847         this.yearField.setValue(v);
32848         this.inputEl.dom.value = this.getValue();
32849         this.validate();
32850         return;
32851     },
32852     
32853     getDay : function()
32854     {
32855         return this.dayField.getValue();
32856     },
32857     
32858     getMonth : function()
32859     {
32860         return this.monthField.getValue();
32861     },
32862     
32863     getYear : function()
32864     {
32865         return this.yearField.getValue();
32866     },
32867     
32868     getValue : function()
32869     {
32870         var f = this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat;
32871         
32872         var date = this.yearField.getValue() + '-' + this.monthField.getValue() + '-' + this.dayField.getValue();
32873         
32874         return date;
32875     },
32876     
32877     reset : function()
32878     {
32879         this.setDay('');
32880         this.setMonth('');
32881         this.setYear('');
32882         this.inputEl.dom.value = '';
32883         this.validate();
32884         return;
32885     },
32886     
32887     validate : function()
32888     {
32889         var d = this.dayField.validate();
32890         var m = this.monthField.validate();
32891         var y = this.yearField.validate();
32892         
32893         var valid = true;
32894         
32895         if(
32896                 (!this.dayAllowBlank && !d) ||
32897                 (!this.monthAllowBlank && !m) ||
32898                 (!this.yearAllowBlank && !y)
32899         ){
32900             valid = false;
32901         }
32902         
32903         if(this.dayAllowBlank && this.monthAllowBlank && this.yearAllowBlank){
32904             return valid;
32905         }
32906         
32907         if(valid){
32908             this.markValid();
32909             return valid;
32910         }
32911         
32912         this.markInvalid();
32913         
32914         return valid;
32915     },
32916     
32917     markValid : function()
32918     {
32919         
32920         var label = this.el.select('label', true).first();
32921         var icon = this.el.select('i.fa-star', true).first();
32922
32923         if(label && icon){
32924             icon.remove();
32925         }
32926         
32927         this.fireEvent('valid', this);
32928     },
32929     
32930      /**
32931      * Mark this field as invalid
32932      * @param {String} msg The validation message
32933      */
32934     markInvalid : function(msg)
32935     {
32936         
32937         var label = this.el.select('label', true).first();
32938         var icon = this.el.select('i.fa-star', true).first();
32939
32940         if(label && !icon){
32941             this.el.select('.roo-date-split-field-label', true).createChild({
32942                 tag : 'i',
32943                 cls : 'text-danger fa fa-lg fa-star',
32944                 tooltip : 'This field is required',
32945                 style : 'margin-right:5px;'
32946             }, label, true);
32947         }
32948         
32949         this.fireEvent('invalid', this, msg);
32950     },
32951     
32952     clearInvalid : function()
32953     {
32954         var label = this.el.select('label', true).first();
32955         var icon = this.el.select('i.fa-star', true).first();
32956
32957         if(label && icon){
32958             icon.remove();
32959         }
32960         
32961         this.fireEvent('valid', this);
32962     },
32963     
32964     getName: function()
32965     {
32966         return this.name;
32967     }
32968     
32969 });
32970
32971  /**
32972  *
32973  * This is based on 
32974  * http://masonry.desandro.com
32975  *
32976  * The idea is to render all the bricks based on vertical width...
32977  *
32978  * The original code extends 'outlayer' - we might need to use that....
32979  * 
32980  */
32981
32982
32983 /**
32984  * @class Roo.bootstrap.LayoutMasonry
32985  * @extends Roo.bootstrap.Component
32986  * Bootstrap Layout Masonry class
32987  * 
32988  * @constructor
32989  * Create a new Element
32990  * @param {Object} config The config object
32991  */
32992
32993 Roo.bootstrap.LayoutMasonry = function(config){
32994     
32995     Roo.bootstrap.LayoutMasonry.superclass.constructor.call(this, config);
32996     
32997     this.bricks = [];
32998     
32999     Roo.bootstrap.LayoutMasonry.register(this);
33000     
33001     this.addEvents({
33002         // raw events
33003         /**
33004          * @event layout
33005          * Fire after layout the items
33006          * @param {Roo.bootstrap.LayoutMasonry} this
33007          * @param {Roo.EventObject} e
33008          */
33009         "layout" : true
33010     });
33011     
33012 };
33013
33014 Roo.extend(Roo.bootstrap.LayoutMasonry, Roo.bootstrap.Component,  {
33015     
33016     /**
33017      * @cfg {Boolean} isLayoutInstant = no animation?
33018      */   
33019     isLayoutInstant : false, // needed?
33020    
33021     /**
33022      * @cfg {Number} boxWidth  width of the columns
33023      */   
33024     boxWidth : 450,
33025     
33026       /**
33027      * @cfg {Number} boxHeight  - 0 for square, or fix it at a certian height
33028      */   
33029     boxHeight : 0,
33030     
33031     /**
33032      * @cfg {Number} padWidth padding below box..
33033      */   
33034     padWidth : 10, 
33035     
33036     /**
33037      * @cfg {Number} gutter gutter width..
33038      */   
33039     gutter : 10,
33040     
33041      /**
33042      * @cfg {Number} maxCols maximum number of columns
33043      */   
33044     
33045     maxCols: 0,
33046     
33047     /**
33048      * @cfg {Boolean} isAutoInitial defalut true
33049      */   
33050     isAutoInitial : true, 
33051     
33052     containerWidth: 0,
33053     
33054     /**
33055      * @cfg {Boolean} isHorizontal defalut false
33056      */   
33057     isHorizontal : false, 
33058
33059     currentSize : null,
33060     
33061     tag: 'div',
33062     
33063     cls: '',
33064     
33065     bricks: null, //CompositeElement
33066     
33067     cols : 1,
33068     
33069     _isLayoutInited : false,
33070     
33071 //    isAlternative : false, // only use for vertical layout...
33072     
33073     /**
33074      * @cfg {Number} alternativePadWidth padding below box..
33075      */   
33076     alternativePadWidth : 50,
33077     
33078     selectedBrick : [],
33079     
33080     getAutoCreate : function(){
33081         
33082         var cfg = Roo.apply({}, Roo.bootstrap.LayoutMasonry.superclass.getAutoCreate.call(this));
33083         
33084         var cfg = {
33085             tag: this.tag,
33086             cls: 'blog-masonary-wrapper ' + this.cls,
33087             cn : {
33088                 cls : 'mas-boxes masonary'
33089             }
33090         };
33091         
33092         return cfg;
33093     },
33094     
33095     getChildContainer: function( )
33096     {
33097         if (this.boxesEl) {
33098             return this.boxesEl;
33099         }
33100         
33101         this.boxesEl = this.el.select('.mas-boxes').first();
33102         
33103         return this.boxesEl;
33104     },
33105     
33106     
33107     initEvents : function()
33108     {
33109         var _this = this;
33110         
33111         if(this.isAutoInitial){
33112             Roo.log('hook children rendered');
33113             this.on('childrenrendered', function() {
33114                 Roo.log('children rendered');
33115                 _this.initial();
33116             } ,this);
33117         }
33118     },
33119     
33120     initial : function()
33121     {
33122         this.selectedBrick = [];
33123         
33124         this.currentSize = this.el.getBox(true);
33125         
33126         Roo.EventManager.onWindowResize(this.resize, this); 
33127
33128         if(!this.isAutoInitial){
33129             this.layout();
33130             return;
33131         }
33132         
33133         this.layout();
33134         
33135         return;
33136         //this.layout.defer(500,this);
33137         
33138     },
33139     
33140     resize : function()
33141     {
33142         var cs = this.el.getBox(true);
33143         
33144         if (
33145                 this.currentSize.width == cs.width && 
33146                 this.currentSize.x == cs.x && 
33147                 this.currentSize.height == cs.height && 
33148                 this.currentSize.y == cs.y 
33149         ) {
33150             Roo.log("no change in with or X or Y");
33151             return;
33152         }
33153         
33154         this.currentSize = cs;
33155         
33156         this.layout();
33157         
33158     },
33159     
33160     layout : function()
33161     {   
33162         this._resetLayout();
33163         
33164         var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
33165         
33166         this.layoutItems( isInstant );
33167       
33168         this._isLayoutInited = true;
33169         
33170         this.fireEvent('layout', this);
33171         
33172     },
33173     
33174     _resetLayout : function()
33175     {
33176         if(this.isHorizontal){
33177             this.horizontalMeasureColumns();
33178             return;
33179         }
33180         
33181         this.verticalMeasureColumns();
33182         
33183     },
33184     
33185     verticalMeasureColumns : function()
33186     {
33187         this.getContainerWidth();
33188         
33189 //        if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
33190 //            this.colWidth = Math.floor(this.containerWidth * 0.8);
33191 //            return;
33192 //        }
33193         
33194         var boxWidth = this.boxWidth + this.padWidth;
33195         
33196         if(this.containerWidth < this.boxWidth){
33197             boxWidth = this.containerWidth
33198         }
33199         
33200         var containerWidth = this.containerWidth;
33201         
33202         var cols = Math.floor(containerWidth / boxWidth);
33203         
33204         this.cols = Math.max( cols, 1 );
33205         
33206         this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
33207         
33208         var totalBoxWidth = this.cols * boxWidth - this.padWidth;
33209         
33210         var avail = Math.floor((containerWidth - totalBoxWidth) / this.cols);
33211         
33212         this.colWidth = boxWidth + avail - this.padWidth;
33213         
33214         this.unitWidth = Math.round((this.colWidth - (this.gutter * 2)) / 3);
33215         this.unitHeight = this.boxHeight > 0 ? this.boxHeight  : this.unitWidth;
33216     },
33217     
33218     horizontalMeasureColumns : function()
33219     {
33220         this.getContainerWidth();
33221         
33222         var boxWidth = this.boxWidth;
33223         
33224         if(this.containerWidth < boxWidth){
33225             boxWidth = this.containerWidth;
33226         }
33227         
33228         this.unitWidth = Math.floor((boxWidth - (this.gutter * 2)) / 3);
33229         
33230         this.el.setHeight(boxWidth);
33231         
33232     },
33233     
33234     getContainerWidth : function()
33235     {
33236         this.containerWidth = this.el.getBox(true).width;  //maybe use getComputedWidth
33237     },
33238     
33239     layoutItems : function( isInstant )
33240     {
33241         Roo.log(this.bricks);
33242         
33243         var items = Roo.apply([], this.bricks);
33244         
33245         if(this.isHorizontal){
33246             this._horizontalLayoutItems( items , isInstant );
33247             return;
33248         }
33249         
33250 //        if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
33251 //            this._verticalAlternativeLayoutItems( items , isInstant );
33252 //            return;
33253 //        }
33254         
33255         this._verticalLayoutItems( items , isInstant );
33256         
33257     },
33258     
33259     _verticalLayoutItems : function ( items , isInstant)
33260     {
33261         if ( !items || !items.length ) {
33262             return;
33263         }
33264         
33265         var standard = [
33266             ['xs', 'xs', 'xs', 'tall'],
33267             ['xs', 'xs', 'tall'],
33268             ['xs', 'xs', 'sm'],
33269             ['xs', 'xs', 'xs'],
33270             ['xs', 'tall'],
33271             ['xs', 'sm'],
33272             ['xs', 'xs'],
33273             ['xs'],
33274             
33275             ['sm', 'xs', 'xs'],
33276             ['sm', 'xs'],
33277             ['sm'],
33278             
33279             ['tall', 'xs', 'xs', 'xs'],
33280             ['tall', 'xs', 'xs'],
33281             ['tall', 'xs'],
33282             ['tall']
33283             
33284         ];
33285         
33286         var queue = [];
33287         
33288         var boxes = [];
33289         
33290         var box = [];
33291         
33292         Roo.each(items, function(item, k){
33293             
33294             switch (item.size) {
33295                 // these layouts take up a full box,
33296                 case 'md' :
33297                 case 'md-left' :
33298                 case 'md-right' :
33299                 case 'wide' :
33300                     
33301                     if(box.length){
33302                         boxes.push(box);
33303                         box = [];
33304                     }
33305                     
33306                     boxes.push([item]);
33307                     
33308                     break;
33309                     
33310                 case 'xs' :
33311                 case 'sm' :
33312                 case 'tall' :
33313                     
33314                     box.push(item);
33315                     
33316                     break;
33317                 default :
33318                     break;
33319                     
33320             }
33321             
33322         }, this);
33323         
33324         if(box.length){
33325             boxes.push(box);
33326             box = [];
33327         }
33328         
33329         var filterPattern = function(box, length)
33330         {
33331             if(!box.length){
33332                 return;
33333             }
33334             
33335             var match = false;
33336             
33337             var pattern = box.slice(0, length);
33338             
33339             var format = [];
33340             
33341             Roo.each(pattern, function(i){
33342                 format.push(i.size);
33343             }, this);
33344             
33345             Roo.each(standard, function(s){
33346                 
33347                 if(String(s) != String(format)){
33348                     return;
33349                 }
33350                 
33351                 match = true;
33352                 return false;
33353                 
33354             }, this);
33355             
33356             if(!match && length == 1){
33357                 return;
33358             }
33359             
33360             if(!match){
33361                 filterPattern(box, length - 1);
33362                 return;
33363             }
33364                 
33365             queue.push(pattern);
33366
33367             box = box.slice(length, box.length);
33368
33369             filterPattern(box, 4);
33370
33371             return;
33372             
33373         }
33374         
33375         Roo.each(boxes, function(box, k){
33376             
33377             if(!box.length){
33378                 return;
33379             }
33380             
33381             if(box.length == 1){
33382                 queue.push(box);
33383                 return;
33384             }
33385             
33386             filterPattern(box, 4);
33387             
33388         }, this);
33389         
33390         this._processVerticalLayoutQueue( queue, isInstant );
33391         
33392     },
33393     
33394 //    _verticalAlternativeLayoutItems : function( items , isInstant )
33395 //    {
33396 //        if ( !items || !items.length ) {
33397 //            return;
33398 //        }
33399 //
33400 //        this._processVerticalAlternativeLayoutQueue( items, isInstant );
33401 //        
33402 //    },
33403     
33404     _horizontalLayoutItems : function ( items , isInstant)
33405     {
33406         if ( !items || !items.length || items.length < 3) {
33407             return;
33408         }
33409         
33410         items.reverse();
33411         
33412         var eItems = items.slice(0, 3);
33413         
33414         items = items.slice(3, items.length);
33415         
33416         var standard = [
33417             ['xs', 'xs', 'xs', 'wide'],
33418             ['xs', 'xs', 'wide'],
33419             ['xs', 'xs', 'sm'],
33420             ['xs', 'xs', 'xs'],
33421             ['xs', 'wide'],
33422             ['xs', 'sm'],
33423             ['xs', 'xs'],
33424             ['xs'],
33425             
33426             ['sm', 'xs', 'xs'],
33427             ['sm', 'xs'],
33428             ['sm'],
33429             
33430             ['wide', 'xs', 'xs', 'xs'],
33431             ['wide', 'xs', 'xs'],
33432             ['wide', 'xs'],
33433             ['wide'],
33434             
33435             ['wide-thin']
33436         ];
33437         
33438         var queue = [];
33439         
33440         var boxes = [];
33441         
33442         var box = [];
33443         
33444         Roo.each(items, function(item, k){
33445             
33446             switch (item.size) {
33447                 case 'md' :
33448                 case 'md-left' :
33449                 case 'md-right' :
33450                 case 'tall' :
33451                     
33452                     if(box.length){
33453                         boxes.push(box);
33454                         box = [];
33455                     }
33456                     
33457                     boxes.push([item]);
33458                     
33459                     break;
33460                     
33461                 case 'xs' :
33462                 case 'sm' :
33463                 case 'wide' :
33464                 case 'wide-thin' :
33465                     
33466                     box.push(item);
33467                     
33468                     break;
33469                 default :
33470                     break;
33471                     
33472             }
33473             
33474         }, this);
33475         
33476         if(box.length){
33477             boxes.push(box);
33478             box = [];
33479         }
33480         
33481         var filterPattern = function(box, length)
33482         {
33483             if(!box.length){
33484                 return;
33485             }
33486             
33487             var match = false;
33488             
33489             var pattern = box.slice(0, length);
33490             
33491             var format = [];
33492             
33493             Roo.each(pattern, function(i){
33494                 format.push(i.size);
33495             }, this);
33496             
33497             Roo.each(standard, function(s){
33498                 
33499                 if(String(s) != String(format)){
33500                     return;
33501                 }
33502                 
33503                 match = true;
33504                 return false;
33505                 
33506             }, this);
33507             
33508             if(!match && length == 1){
33509                 return;
33510             }
33511             
33512             if(!match){
33513                 filterPattern(box, length - 1);
33514                 return;
33515             }
33516                 
33517             queue.push(pattern);
33518
33519             box = box.slice(length, box.length);
33520
33521             filterPattern(box, 4);
33522
33523             return;
33524             
33525         }
33526         
33527         Roo.each(boxes, function(box, k){
33528             
33529             if(!box.length){
33530                 return;
33531             }
33532             
33533             if(box.length == 1){
33534                 queue.push(box);
33535                 return;
33536             }
33537             
33538             filterPattern(box, 4);
33539             
33540         }, this);
33541         
33542         
33543         var prune = [];
33544         
33545         var pos = this.el.getBox(true);
33546         
33547         var minX = pos.x;
33548         
33549         var maxX = pos.right - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
33550         
33551         var hit_end = false;
33552         
33553         Roo.each(queue, function(box){
33554             
33555             if(hit_end){
33556                 
33557                 Roo.each(box, function(b){
33558                 
33559                     b.el.setVisibilityMode(Roo.Element.DISPLAY);
33560                     b.el.hide();
33561
33562                 }, this);
33563
33564                 return;
33565             }
33566             
33567             var mx = 0;
33568             
33569             Roo.each(box, function(b){
33570                 
33571                 b.el.setVisibilityMode(Roo.Element.DISPLAY);
33572                 b.el.show();
33573
33574                 mx = Math.max(mx, b.x);
33575                 
33576             }, this);
33577             
33578             maxX = maxX - this.unitWidth * mx - this.gutter * (mx - 1) - this.padWidth;
33579             
33580             if(maxX < minX){
33581                 
33582                 Roo.each(box, function(b){
33583                 
33584                     b.el.setVisibilityMode(Roo.Element.DISPLAY);
33585                     b.el.hide();
33586                     
33587                 }, this);
33588                 
33589                 hit_end = true;
33590                 
33591                 return;
33592             }
33593             
33594             prune.push(box);
33595             
33596         }, this);
33597         
33598         this._processHorizontalLayoutQueue( prune, eItems, isInstant );
33599     },
33600     
33601     /** Sets position of item in DOM
33602     * @param {Element} item
33603     * @param {Number} x - horizontal position
33604     * @param {Number} y - vertical position
33605     * @param {Boolean} isInstant - disables transitions
33606     */
33607     _processVerticalLayoutQueue : function( queue, isInstant )
33608     {
33609         var pos = this.el.getBox(true);
33610         var x = pos.x;
33611         var y = pos.y;
33612         var maxY = [];
33613         
33614         for (var i = 0; i < this.cols; i++){
33615             maxY[i] = pos.y;
33616         }
33617         
33618         Roo.each(queue, function(box, k){
33619             
33620             var col = k % this.cols;
33621             
33622             Roo.each(box, function(b,kk){
33623                 
33624                 b.el.position('absolute');
33625                 
33626                 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
33627                 var height = Math.floor(this.unitHeight * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
33628                 
33629                 if(b.size == 'md-left' || b.size == 'md-right'){
33630                     width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
33631                     height = Math.floor(this.unitHeight * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
33632                 }
33633                 
33634                 b.el.setWidth(width);
33635                 b.el.setHeight(height);
33636                 // iframe?
33637                 b.el.select('iframe',true).setSize(width,height);
33638                 
33639             }, this);
33640             
33641             for (var i = 0; i < this.cols; i++){
33642                 
33643                 if(maxY[i] < maxY[col]){
33644                     col = i;
33645                     continue;
33646                 }
33647                 
33648                 col = Math.min(col, i);
33649                 
33650             }
33651             
33652             x = pos.x + col * (this.colWidth + this.padWidth);
33653             
33654             y = maxY[col];
33655             
33656             var positions = [];
33657             
33658             switch (box.length){
33659                 case 1 :
33660                     positions = this.getVerticalOneBoxColPositions(x, y, box);
33661                     break;
33662                 case 2 :
33663                     positions = this.getVerticalTwoBoxColPositions(x, y, box);
33664                     break;
33665                 case 3 :
33666                     positions = this.getVerticalThreeBoxColPositions(x, y, box);
33667                     break;
33668                 case 4 :
33669                     positions = this.getVerticalFourBoxColPositions(x, y, box);
33670                     break;
33671                 default :
33672                     break;
33673             }
33674             
33675             Roo.each(box, function(b,kk){
33676                 
33677                 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
33678                 
33679                 var sz = b.el.getSize();
33680                 
33681                 maxY[col] = Math.max(maxY[col], positions[kk].y + sz.height + this.padWidth);
33682                 
33683             }, this);
33684             
33685         }, this);
33686         
33687         var mY = 0;
33688         
33689         for (var i = 0; i < this.cols; i++){
33690             mY = Math.max(mY, maxY[i]);
33691         }
33692         
33693         this.el.setHeight(mY - pos.y);
33694         
33695     },
33696     
33697 //    _processVerticalAlternativeLayoutQueue : function( items, isInstant )
33698 //    {
33699 //        var pos = this.el.getBox(true);
33700 //        var x = pos.x;
33701 //        var y = pos.y;
33702 //        var maxX = pos.right;
33703 //        
33704 //        var maxHeight = 0;
33705 //        
33706 //        Roo.each(items, function(item, k){
33707 //            
33708 //            var c = k % 2;
33709 //            
33710 //            item.el.position('absolute');
33711 //                
33712 //            var width = Math.floor(this.colWidth + item.el.getPadding('lr'));
33713 //
33714 //            item.el.setWidth(width);
33715 //
33716 //            var height = Math.floor(this.colWidth * item.y / item.x + item.el.getPadding('tb'));
33717 //
33718 //            item.el.setHeight(height);
33719 //            
33720 //            if(c == 0){
33721 //                item.el.setXY([x, y], isInstant ? false : true);
33722 //            } else {
33723 //                item.el.setXY([maxX - width, y], isInstant ? false : true);
33724 //            }
33725 //            
33726 //            y = y + height + this.alternativePadWidth;
33727 //            
33728 //            maxHeight = maxHeight + height + this.alternativePadWidth;
33729 //            
33730 //        }, this);
33731 //        
33732 //        this.el.setHeight(maxHeight);
33733 //        
33734 //    },
33735     
33736     _processHorizontalLayoutQueue : function( queue, eItems, isInstant )
33737     {
33738         var pos = this.el.getBox(true);
33739         
33740         var minX = pos.x;
33741         var minY = pos.y;
33742         
33743         var maxX = pos.right;
33744         
33745         this._processHorizontalEndItem(eItems, maxX, minX, minY, isInstant);
33746         
33747         var maxX = maxX - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
33748         
33749         Roo.each(queue, function(box, k){
33750             
33751             Roo.each(box, function(b, kk){
33752                 
33753                 b.el.position('absolute');
33754                 
33755                 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
33756                 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
33757                 
33758                 if(b.size == 'md-left' || b.size == 'md-right'){
33759                     width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
33760                     height = Math.floor(this.unitWidth * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
33761                 }
33762                 
33763                 b.el.setWidth(width);
33764                 b.el.setHeight(height);
33765                 
33766             }, this);
33767             
33768             if(!box.length){
33769                 return;
33770             }
33771             
33772             var positions = [];
33773             
33774             switch (box.length){
33775                 case 1 :
33776                     positions = this.getHorizontalOneBoxColPositions(maxX, minY, box);
33777                     break;
33778                 case 2 :
33779                     positions = this.getHorizontalTwoBoxColPositions(maxX, minY, box);
33780                     break;
33781                 case 3 :
33782                     positions = this.getHorizontalThreeBoxColPositions(maxX, minY, box);
33783                     break;
33784                 case 4 :
33785                     positions = this.getHorizontalFourBoxColPositions(maxX, minY, box);
33786                     break;
33787                 default :
33788                     break;
33789             }
33790             
33791             Roo.each(box, function(b,kk){
33792                 
33793                 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
33794                 
33795                 maxX = Math.min(maxX, positions[kk].x - this.padWidth);
33796                 
33797             }, this);
33798             
33799         }, this);
33800         
33801     },
33802     
33803     _processHorizontalEndItem : function(eItems, maxX, minX, minY, isInstant)
33804     {
33805         Roo.each(eItems, function(b,k){
33806             
33807             b.size = (k == 0) ? 'sm' : 'xs';
33808             b.x = (k == 0) ? 2 : 1;
33809             b.y = (k == 0) ? 2 : 1;
33810             
33811             b.el.position('absolute');
33812             
33813             var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
33814                 
33815             b.el.setWidth(width);
33816             
33817             var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
33818             
33819             b.el.setHeight(height);
33820             
33821         }, this);
33822
33823         var positions = [];
33824         
33825         positions.push({
33826             x : maxX - this.unitWidth * 2 - this.gutter,
33827             y : minY
33828         });
33829         
33830         positions.push({
33831             x : maxX - this.unitWidth,
33832             y : minY + (this.unitWidth + this.gutter) * 2
33833         });
33834         
33835         positions.push({
33836             x : maxX - this.unitWidth * 3 - this.gutter * 2,
33837             y : minY
33838         });
33839         
33840         Roo.each(eItems, function(b,k){
33841             
33842             b.el.setXY([positions[k].x, positions[k].y], isInstant ? false : true);
33843
33844         }, this);
33845         
33846     },
33847     
33848     getVerticalOneBoxColPositions : function(x, y, box)
33849     {
33850         var pos = [];
33851         
33852         var rand = Math.floor(Math.random() * ((4 - box[0].x)));
33853         
33854         if(box[0].size == 'md-left'){
33855             rand = 0;
33856         }
33857         
33858         if(box[0].size == 'md-right'){
33859             rand = 1;
33860         }
33861         
33862         pos.push({
33863             x : x + (this.unitWidth + this.gutter) * rand,
33864             y : y
33865         });
33866         
33867         return pos;
33868     },
33869     
33870     getVerticalTwoBoxColPositions : function(x, y, box)
33871     {
33872         var pos = [];
33873         
33874         if(box[0].size == 'xs'){
33875             
33876             pos.push({
33877                 x : x,
33878                 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[1].y))
33879             });
33880
33881             pos.push({
33882                 x : x + (this.unitWidth + this.gutter) * (3 - box[1].x),
33883                 y : y
33884             });
33885             
33886             return pos;
33887             
33888         }
33889         
33890         pos.push({
33891             x : x,
33892             y : y
33893         });
33894
33895         pos.push({
33896             x : x + (this.unitWidth + this.gutter) * 2,
33897             y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[0].y))
33898         });
33899         
33900         return pos;
33901         
33902     },
33903     
33904     getVerticalThreeBoxColPositions : function(x, y, box)
33905     {
33906         var pos = [];
33907         
33908         if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
33909             
33910             pos.push({
33911                 x : x,
33912                 y : y
33913             });
33914
33915             pos.push({
33916                 x : x + (this.unitWidth + this.gutter) * 1,
33917                 y : y
33918             });
33919             
33920             pos.push({
33921                 x : x + (this.unitWidth + this.gutter) * 2,
33922                 y : y
33923             });
33924             
33925             return pos;
33926             
33927         }
33928         
33929         if(box[0].size == 'xs' && box[1].size == 'xs'){
33930             
33931             pos.push({
33932                 x : x,
33933                 y : y
33934             });
33935
33936             pos.push({
33937                 x : x,
33938                 y : y + ((this.unitHeight + this.gutter) * (box[2].y - 1))
33939             });
33940             
33941             pos.push({
33942                 x : x + (this.unitWidth + this.gutter) * 1,
33943                 y : y
33944             });
33945             
33946             return pos;
33947             
33948         }
33949         
33950         pos.push({
33951             x : x,
33952             y : y
33953         });
33954
33955         pos.push({
33956             x : x + (this.unitWidth + this.gutter) * 2,
33957             y : y
33958         });
33959
33960         pos.push({
33961             x : x + (this.unitWidth + this.gutter) * 2,
33962             y : y + (this.unitHeight + this.gutter) * (box[0].y - 1)
33963         });
33964             
33965         return pos;
33966         
33967     },
33968     
33969     getVerticalFourBoxColPositions : function(x, y, box)
33970     {
33971         var pos = [];
33972         
33973         if(box[0].size == 'xs'){
33974             
33975             pos.push({
33976                 x : x,
33977                 y : y
33978             });
33979
33980             pos.push({
33981                 x : x,
33982                 y : y + (this.unitHeight + this.gutter) * 1
33983             });
33984             
33985             pos.push({
33986                 x : x,
33987                 y : y + (this.unitHeight + this.gutter) * 2
33988             });
33989             
33990             pos.push({
33991                 x : x + (this.unitWidth + this.gutter) * 1,
33992                 y : y
33993             });
33994             
33995             return pos;
33996             
33997         }
33998         
33999         pos.push({
34000             x : x,
34001             y : y
34002         });
34003
34004         pos.push({
34005             x : x + (this.unitWidth + this.gutter) * 2,
34006             y : y
34007         });
34008
34009         pos.push({
34010             x : x + (this.unitHeightunitWidth + this.gutter) * 2,
34011             y : y + (this.unitHeight + this.gutter) * 1
34012         });
34013
34014         pos.push({
34015             x : x + (this.unitWidth + this.gutter) * 2,
34016             y : y + (this.unitWidth + this.gutter) * 2
34017         });
34018
34019         return pos;
34020         
34021     },
34022     
34023     getHorizontalOneBoxColPositions : function(maxX, minY, box)
34024     {
34025         var pos = [];
34026         
34027         if(box[0].size == 'md-left'){
34028             pos.push({
34029                 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
34030                 y : minY
34031             });
34032             
34033             return pos;
34034         }
34035         
34036         if(box[0].size == 'md-right'){
34037             pos.push({
34038                 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
34039                 y : minY + (this.unitWidth + this.gutter) * 1
34040             });
34041             
34042             return pos;
34043         }
34044         
34045         var rand = Math.floor(Math.random() * (4 - box[0].y));
34046         
34047         pos.push({
34048             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34049             y : minY + (this.unitWidth + this.gutter) * rand
34050         });
34051         
34052         return pos;
34053         
34054     },
34055     
34056     getHorizontalTwoBoxColPositions : function(maxX, minY, box)
34057     {
34058         var pos = [];
34059         
34060         if(box[0].size == 'xs'){
34061             
34062             pos.push({
34063                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34064                 y : minY
34065             });
34066
34067             pos.push({
34068                 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34069                 y : minY + (this.unitWidth + this.gutter) * (3 - box[1].y)
34070             });
34071             
34072             return pos;
34073             
34074         }
34075         
34076         pos.push({
34077             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34078             y : minY
34079         });
34080
34081         pos.push({
34082             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34083             y : minY + (this.unitWidth + this.gutter) * 2
34084         });
34085         
34086         return pos;
34087         
34088     },
34089     
34090     getHorizontalThreeBoxColPositions : function(maxX, minY, box)
34091     {
34092         var pos = [];
34093         
34094         if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
34095             
34096             pos.push({
34097                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34098                 y : minY
34099             });
34100
34101             pos.push({
34102                 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34103                 y : minY + (this.unitWidth + this.gutter) * 1
34104             });
34105             
34106             pos.push({
34107                 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
34108                 y : minY + (this.unitWidth + this.gutter) * 2
34109             });
34110             
34111             return pos;
34112             
34113         }
34114         
34115         if(box[0].size == 'xs' && box[1].size == 'xs'){
34116             
34117             pos.push({
34118                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34119                 y : minY
34120             });
34121
34122             pos.push({
34123                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34124                 y : minY
34125             });
34126             
34127             pos.push({
34128                 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
34129                 y : minY + (this.unitWidth + this.gutter) * 1
34130             });
34131             
34132             return pos;
34133             
34134         }
34135         
34136         pos.push({
34137             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34138             y : minY
34139         });
34140
34141         pos.push({
34142             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34143             y : minY + (this.unitWidth + this.gutter) * 2
34144         });
34145
34146         pos.push({
34147             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
34148             y : minY + (this.unitWidth + this.gutter) * 2
34149         });
34150             
34151         return pos;
34152         
34153     },
34154     
34155     getHorizontalFourBoxColPositions : function(maxX, minY, box)
34156     {
34157         var pos = [];
34158         
34159         if(box[0].size == 'xs'){
34160             
34161             pos.push({
34162                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34163                 y : minY
34164             });
34165
34166             pos.push({
34167                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34168                 y : minY
34169             });
34170             
34171             pos.push({
34172                 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),
34173                 y : minY
34174             });
34175             
34176             pos.push({
34177                 x : maxX - this.unitWidth * box[3].x - this.gutter * (box[3].x - 1),
34178                 y : minY + (this.unitWidth + this.gutter) * 1
34179             });
34180             
34181             return pos;
34182             
34183         }
34184         
34185         pos.push({
34186             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34187             y : minY
34188         });
34189         
34190         pos.push({
34191             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34192             y : minY + (this.unitWidth + this.gutter) * 2
34193         });
34194         
34195         pos.push({
34196             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
34197             y : minY + (this.unitWidth + this.gutter) * 2
34198         });
34199         
34200         pos.push({
34201             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),
34202             y : minY + (this.unitWidth + this.gutter) * 2
34203         });
34204
34205         return pos;
34206         
34207     },
34208     
34209     /**
34210     * remove a Masonry Brick
34211     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to remove
34212     */
34213     removeBrick : function(brick_id)
34214     {
34215         if (!brick_id) {
34216             return;
34217         }
34218         
34219         for (var i = 0; i<this.bricks.length; i++) {
34220             if (this.bricks[i].id == brick_id) {
34221                 this.bricks.splice(i,1);
34222                 this.el.dom.removeChild(Roo.get(brick_id).dom);
34223                 this.initial();
34224             }
34225         }
34226     },
34227     
34228     /**
34229     * adds a Masonry Brick
34230     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
34231     */
34232     addBrick : function(cfg)
34233     {
34234         var cn = new Roo.bootstrap.MasonryBrick(cfg);
34235         //this.register(cn);
34236         cn.parentId = this.id;
34237         cn.render(this.el);
34238         return cn;
34239     },
34240     
34241     /**
34242     * register a Masonry Brick
34243     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
34244     */
34245     
34246     register : function(brick)
34247     {
34248         this.bricks.push(brick);
34249         brick.masonryId = this.id;
34250     },
34251     
34252     /**
34253     * clear all the Masonry Brick
34254     */
34255     clearAll : function()
34256     {
34257         this.bricks = [];
34258         //this.getChildContainer().dom.innerHTML = "";
34259         this.el.dom.innerHTML = '';
34260     },
34261     
34262     getSelected : function()
34263     {
34264         if (!this.selectedBrick) {
34265             return false;
34266         }
34267         
34268         return this.selectedBrick;
34269     }
34270 });
34271
34272 Roo.apply(Roo.bootstrap.LayoutMasonry, {
34273     
34274     groups: {},
34275      /**
34276     * register a Masonry Layout
34277     * @param {Roo.bootstrap.LayoutMasonry} the masonry layout to add
34278     */
34279     
34280     register : function(layout)
34281     {
34282         this.groups[layout.id] = layout;
34283     },
34284     /**
34285     * fetch a  Masonry Layout based on the masonry layout ID
34286     * @param {string} the masonry layout to add
34287     * @returns {Roo.bootstrap.LayoutMasonry} the masonry layout
34288     */
34289     
34290     get: function(layout_id) {
34291         if (typeof(this.groups[layout_id]) == 'undefined') {
34292             return false;
34293         }
34294         return this.groups[layout_id] ;
34295     }
34296     
34297     
34298     
34299 });
34300
34301  
34302
34303  /**
34304  *
34305  * This is based on 
34306  * http://masonry.desandro.com
34307  *
34308  * The idea is to render all the bricks based on vertical width...
34309  *
34310  * The original code extends 'outlayer' - we might need to use that....
34311  * 
34312  */
34313
34314
34315 /**
34316  * @class Roo.bootstrap.LayoutMasonryAuto
34317  * @extends Roo.bootstrap.Component
34318  * Bootstrap Layout Masonry class
34319  * 
34320  * @constructor
34321  * Create a new Element
34322  * @param {Object} config The config object
34323  */
34324
34325 Roo.bootstrap.LayoutMasonryAuto = function(config){
34326     Roo.bootstrap.LayoutMasonryAuto.superclass.constructor.call(this, config);
34327 };
34328
34329 Roo.extend(Roo.bootstrap.LayoutMasonryAuto, Roo.bootstrap.Component,  {
34330     
34331       /**
34332      * @cfg {Boolean} isFitWidth  - resize the width..
34333      */   
34334     isFitWidth : false,  // options..
34335     /**
34336      * @cfg {Boolean} isOriginLeft = left align?
34337      */   
34338     isOriginLeft : true,
34339     /**
34340      * @cfg {Boolean} isOriginTop = top align?
34341      */   
34342     isOriginTop : false,
34343     /**
34344      * @cfg {Boolean} isLayoutInstant = no animation?
34345      */   
34346     isLayoutInstant : false, // needed?
34347     /**
34348      * @cfg {Boolean} isResizingContainer = not sure if this is used..
34349      */   
34350     isResizingContainer : true,
34351     /**
34352      * @cfg {Number} columnWidth  width of the columns 
34353      */   
34354     
34355     columnWidth : 0,
34356     
34357     /**
34358      * @cfg {Number} maxCols maximum number of columns
34359      */   
34360     
34361     maxCols: 0,
34362     /**
34363      * @cfg {Number} padHeight padding below box..
34364      */   
34365     
34366     padHeight : 10, 
34367     
34368     /**
34369      * @cfg {Boolean} isAutoInitial defalut true
34370      */   
34371     
34372     isAutoInitial : true, 
34373     
34374     // private?
34375     gutter : 0,
34376     
34377     containerWidth: 0,
34378     initialColumnWidth : 0,
34379     currentSize : null,
34380     
34381     colYs : null, // array.
34382     maxY : 0,
34383     padWidth: 10,
34384     
34385     
34386     tag: 'div',
34387     cls: '',
34388     bricks: null, //CompositeElement
34389     cols : 0, // array?
34390     // element : null, // wrapped now this.el
34391     _isLayoutInited : null, 
34392     
34393     
34394     getAutoCreate : function(){
34395         
34396         var cfg = {
34397             tag: this.tag,
34398             cls: 'blog-masonary-wrapper ' + this.cls,
34399             cn : {
34400                 cls : 'mas-boxes masonary'
34401             }
34402         };
34403         
34404         return cfg;
34405     },
34406     
34407     getChildContainer: function( )
34408     {
34409         if (this.boxesEl) {
34410             return this.boxesEl;
34411         }
34412         
34413         this.boxesEl = this.el.select('.mas-boxes').first();
34414         
34415         return this.boxesEl;
34416     },
34417     
34418     
34419     initEvents : function()
34420     {
34421         var _this = this;
34422         
34423         if(this.isAutoInitial){
34424             Roo.log('hook children rendered');
34425             this.on('childrenrendered', function() {
34426                 Roo.log('children rendered');
34427                 _this.initial();
34428             } ,this);
34429         }
34430         
34431     },
34432     
34433     initial : function()
34434     {
34435         this.reloadItems();
34436
34437         this.currentSize = this.el.getBox(true);
34438
34439         /// was window resize... - let's see if this works..
34440         Roo.EventManager.onWindowResize(this.resize, this); 
34441
34442         if(!this.isAutoInitial){
34443             this.layout();
34444             return;
34445         }
34446         
34447         this.layout.defer(500,this);
34448     },
34449     
34450     reloadItems: function()
34451     {
34452         this.bricks = this.el.select('.masonry-brick', true);
34453         
34454         this.bricks.each(function(b) {
34455             //Roo.log(b.getSize());
34456             if (!b.attr('originalwidth')) {
34457                 b.attr('originalwidth',  b.getSize().width);
34458             }
34459             
34460         });
34461         
34462         Roo.log(this.bricks.elements.length);
34463     },
34464     
34465     resize : function()
34466     {
34467         Roo.log('resize');
34468         var cs = this.el.getBox(true);
34469         
34470         if (this.currentSize.width == cs.width && this.currentSize.x == cs.x ) {
34471             Roo.log("no change in with or X");
34472             return;
34473         }
34474         this.currentSize = cs;
34475         this.layout();
34476     },
34477     
34478     layout : function()
34479     {
34480          Roo.log('layout');
34481         this._resetLayout();
34482         //this._manageStamps();
34483       
34484         // don't animate first layout
34485         var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
34486         this.layoutItems( isInstant );
34487       
34488         // flag for initalized
34489         this._isLayoutInited = true;
34490     },
34491     
34492     layoutItems : function( isInstant )
34493     {
34494         //var items = this._getItemsForLayout( this.items );
34495         // original code supports filtering layout items.. we just ignore it..
34496         
34497         this._layoutItems( this.bricks , isInstant );
34498       
34499         this._postLayout();
34500     },
34501     _layoutItems : function ( items , isInstant)
34502     {
34503        //this.fireEvent( 'layout', this, items );
34504     
34505
34506         if ( !items || !items.elements.length ) {
34507           // no items, emit event with empty array
34508             return;
34509         }
34510
34511         var queue = [];
34512         items.each(function(item) {
34513             Roo.log("layout item");
34514             Roo.log(item);
34515             // get x/y object from method
34516             var position = this._getItemLayoutPosition( item );
34517             // enqueue
34518             position.item = item;
34519             position.isInstant = isInstant; // || item.isLayoutInstant; << not set yet...
34520             queue.push( position );
34521         }, this);
34522       
34523         this._processLayoutQueue( queue );
34524     },
34525     /** Sets position of item in DOM
34526     * @param {Element} item
34527     * @param {Number} x - horizontal position
34528     * @param {Number} y - vertical position
34529     * @param {Boolean} isInstant - disables transitions
34530     */
34531     _processLayoutQueue : function( queue )
34532     {
34533         for ( var i=0, len = queue.length; i < len; i++ ) {
34534             var obj = queue[i];
34535             obj.item.position('absolute');
34536             obj.item.setXY([obj.x,obj.y], obj.isInstant ? false : true);
34537         }
34538     },
34539       
34540     
34541     /**
34542     * Any logic you want to do after each layout,
34543     * i.e. size the container
34544     */
34545     _postLayout : function()
34546     {
34547         this.resizeContainer();
34548     },
34549     
34550     resizeContainer : function()
34551     {
34552         if ( !this.isResizingContainer ) {
34553             return;
34554         }
34555         var size = this._getContainerSize();
34556         if ( size ) {
34557             this.el.setSize(size.width,size.height);
34558             this.boxesEl.setSize(size.width,size.height);
34559         }
34560     },
34561     
34562     
34563     
34564     _resetLayout : function()
34565     {
34566         //this.getSize();  // -- does not really do anything.. it probably applies left/right etc. to obuject but not used
34567         this.colWidth = this.el.getWidth();
34568         //this.gutter = this.el.getWidth(); 
34569         
34570         this.measureColumns();
34571
34572         // reset column Y
34573         var i = this.cols;
34574         this.colYs = [];
34575         while (i--) {
34576             this.colYs.push( 0 );
34577         }
34578     
34579         this.maxY = 0;
34580     },
34581
34582     measureColumns : function()
34583     {
34584         this.getContainerWidth();
34585       // if columnWidth is 0, default to outerWidth of first item
34586         if ( !this.columnWidth ) {
34587             var firstItem = this.bricks.first();
34588             Roo.log(firstItem);
34589             this.columnWidth  = this.containerWidth;
34590             if (firstItem && firstItem.attr('originalwidth') ) {
34591                 this.columnWidth = 1* (firstItem.attr('originalwidth') || firstItem.getWidth());
34592             }
34593             // columnWidth fall back to item of first element
34594             Roo.log("set column width?");
34595                         this.initialColumnWidth = this.columnWidth  ;
34596
34597             // if first elem has no width, default to size of container
34598             
34599         }
34600         
34601         
34602         if (this.initialColumnWidth) {
34603             this.columnWidth = this.initialColumnWidth;
34604         }
34605         
34606         
34607             
34608         // column width is fixed at the top - however if container width get's smaller we should
34609         // reduce it...
34610         
34611         // this bit calcs how man columns..
34612             
34613         var columnWidth = this.columnWidth += this.gutter;
34614       
34615         // calculate columns
34616         var containerWidth = this.containerWidth + this.gutter;
34617         
34618         var cols = (containerWidth - this.padWidth) / (columnWidth - this.padWidth);
34619         // fix rounding errors, typically with gutters
34620         var excess = columnWidth - containerWidth % columnWidth;
34621         
34622         
34623         // if overshoot is less than a pixel, round up, otherwise floor it
34624         var mathMethod = excess && excess < 1 ? 'round' : 'floor';
34625         cols = Math[ mathMethod ]( cols );
34626         this.cols = Math.max( cols, 1 );
34627         this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
34628         
34629          // padding positioning..
34630         var totalColWidth = this.cols * this.columnWidth;
34631         var padavail = this.containerWidth - totalColWidth;
34632         // so for 2 columns - we need 3 'pads'
34633         
34634         var padNeeded = (1+this.cols) * this.padWidth;
34635         
34636         var padExtra = Math.floor((padavail - padNeeded) / this.cols);
34637         
34638         this.columnWidth += padExtra
34639         //this.padWidth = Math.floor(padavail /  ( this.cols));
34640         
34641         // adjust colum width so that padding is fixed??
34642         
34643         // we have 3 columns ... total = width * 3
34644         // we have X left over... that should be used by 
34645         
34646         //if (this.expandC) {
34647             
34648         //}
34649         
34650         
34651         
34652     },
34653     
34654     getContainerWidth : function()
34655     {
34656        /* // container is parent if fit width
34657         var container = this.isFitWidth ? this.element.parentNode : this.element;
34658         // check that this.size and size are there
34659         // IE8 triggers resize on body size change, so they might not be
34660         
34661         var size = getSize( container );  //FIXME
34662         this.containerWidth = size && size.innerWidth; //FIXME
34663         */
34664          
34665         this.containerWidth = this.el.getBox(true).width;  //maybe use getComputedWidth
34666         
34667     },
34668     
34669     _getItemLayoutPosition : function( item )  // what is item?
34670     {
34671         // we resize the item to our columnWidth..
34672       
34673         item.setWidth(this.columnWidth);
34674         item.autoBoxAdjust  = false;
34675         
34676         var sz = item.getSize();
34677  
34678         // how many columns does this brick span
34679         var remainder = this.containerWidth % this.columnWidth;
34680         
34681         var mathMethod = remainder && remainder < 1 ? 'round' : 'ceil';
34682         // round if off by 1 pixel, otherwise use ceil
34683         var colSpan = Math[ mathMethod ]( sz.width  / this.columnWidth );
34684         colSpan = Math.min( colSpan, this.cols );
34685         
34686         // normally this should be '1' as we dont' currently allow multi width columns..
34687         
34688         var colGroup = this._getColGroup( colSpan );
34689         // get the minimum Y value from the columns
34690         var minimumY = Math.min.apply( Math, colGroup );
34691         Roo.log([ 'setHeight',  minimumY, sz.height, setHeight ]);
34692         
34693         var shortColIndex = colGroup.indexOf(  minimumY ); // broken on ie8..?? probably...
34694          
34695         // position the brick
34696         var position = {
34697             x: this.currentSize.x + (this.padWidth /2) + ((this.columnWidth + this.padWidth )* shortColIndex),
34698             y: this.currentSize.y + minimumY + this.padHeight
34699         };
34700         
34701         Roo.log(position);
34702         // apply setHeight to necessary columns
34703         var setHeight = minimumY + sz.height + this.padHeight;
34704         //Roo.log([ 'setHeight',  minimumY, sz.height, setHeight ]);
34705         
34706         var setSpan = this.cols + 1 - colGroup.length;
34707         for ( var i = 0; i < setSpan; i++ ) {
34708           this.colYs[ shortColIndex + i ] = setHeight ;
34709         }
34710       
34711         return position;
34712     },
34713     
34714     /**
34715      * @param {Number} colSpan - number of columns the element spans
34716      * @returns {Array} colGroup
34717      */
34718     _getColGroup : function( colSpan )
34719     {
34720         if ( colSpan < 2 ) {
34721           // if brick spans only one column, use all the column Ys
34722           return this.colYs;
34723         }
34724       
34725         var colGroup = [];
34726         // how many different places could this brick fit horizontally
34727         var groupCount = this.cols + 1 - colSpan;
34728         // for each group potential horizontal position
34729         for ( var i = 0; i < groupCount; i++ ) {
34730           // make an array of colY values for that one group
34731           var groupColYs = this.colYs.slice( i, i + colSpan );
34732           // and get the max value of the array
34733           colGroup[i] = Math.max.apply( Math, groupColYs );
34734         }
34735         return colGroup;
34736     },
34737     /*
34738     _manageStamp : function( stamp )
34739     {
34740         var stampSize =  stamp.getSize();
34741         var offset = stamp.getBox();
34742         // get the columns that this stamp affects
34743         var firstX = this.isOriginLeft ? offset.x : offset.right;
34744         var lastX = firstX + stampSize.width;
34745         var firstCol = Math.floor( firstX / this.columnWidth );
34746         firstCol = Math.max( 0, firstCol );
34747         
34748         var lastCol = Math.floor( lastX / this.columnWidth );
34749         // lastCol should not go over if multiple of columnWidth #425
34750         lastCol -= lastX % this.columnWidth ? 0 : 1;
34751         lastCol = Math.min( this.cols - 1, lastCol );
34752         
34753         // set colYs to bottom of the stamp
34754         var stampMaxY = ( this.isOriginTop ? offset.y : offset.bottom ) +
34755             stampSize.height;
34756             
34757         for ( var i = firstCol; i <= lastCol; i++ ) {
34758           this.colYs[i] = Math.max( stampMaxY, this.colYs[i] );
34759         }
34760     },
34761     */
34762     
34763     _getContainerSize : function()
34764     {
34765         this.maxY = Math.max.apply( Math, this.colYs );
34766         var size = {
34767             height: this.maxY
34768         };
34769       
34770         if ( this.isFitWidth ) {
34771             size.width = this._getContainerFitWidth();
34772         }
34773       
34774         return size;
34775     },
34776     
34777     _getContainerFitWidth : function()
34778     {
34779         var unusedCols = 0;
34780         // count unused columns
34781         var i = this.cols;
34782         while ( --i ) {
34783           if ( this.colYs[i] !== 0 ) {
34784             break;
34785           }
34786           unusedCols++;
34787         }
34788         // fit container to columns that have been used
34789         return ( this.cols - unusedCols ) * this.columnWidth - this.gutter;
34790     },
34791     
34792     needsResizeLayout : function()
34793     {
34794         var previousWidth = this.containerWidth;
34795         this.getContainerWidth();
34796         return previousWidth !== this.containerWidth;
34797     }
34798  
34799 });
34800
34801  
34802
34803  /*
34804  * - LGPL
34805  *
34806  * element
34807  * 
34808  */
34809
34810 /**
34811  * @class Roo.bootstrap.MasonryBrick
34812  * @extends Roo.bootstrap.Component
34813  * Bootstrap MasonryBrick class
34814  * 
34815  * @constructor
34816  * Create a new MasonryBrick
34817  * @param {Object} config The config object
34818  */
34819
34820 Roo.bootstrap.MasonryBrick = function(config){
34821     
34822     Roo.bootstrap.MasonryBrick.superclass.constructor.call(this, config);
34823     
34824     Roo.bootstrap.MasonryBrick.register(this);
34825     
34826     this.addEvents({
34827         // raw events
34828         /**
34829          * @event click
34830          * When a MasonryBrick is clcik
34831          * @param {Roo.bootstrap.MasonryBrick} this
34832          * @param {Roo.EventObject} e
34833          */
34834         "click" : true
34835     });
34836 };
34837
34838 Roo.extend(Roo.bootstrap.MasonryBrick, Roo.bootstrap.Component,  {
34839     
34840     /**
34841      * @cfg {String} title
34842      */   
34843     title : '',
34844     /**
34845      * @cfg {String} html
34846      */   
34847     html : '',
34848     /**
34849      * @cfg {String} bgimage
34850      */   
34851     bgimage : '',
34852     /**
34853      * @cfg {String} videourl
34854      */   
34855     videourl : '',
34856     /**
34857      * @cfg {String} cls
34858      */   
34859     cls : '',
34860     /**
34861      * @cfg {String} href
34862      */   
34863     href : '',
34864     /**
34865      * @cfg {String} size (xs|sm|md|md-left|md-right|tall|wide)
34866      */   
34867     size : 'xs',
34868     
34869     /**
34870      * @cfg {String} placetitle (center|bottom)
34871      */   
34872     placetitle : '',
34873     
34874     /**
34875      * @cfg {Boolean} isFitContainer defalut true
34876      */   
34877     isFitContainer : true, 
34878     
34879     /**
34880      * @cfg {Boolean} preventDefault defalut false
34881      */   
34882     preventDefault : false, 
34883     
34884     /**
34885      * @cfg {Boolean} inverse defalut false
34886      */   
34887     maskInverse : false, 
34888     
34889     getAutoCreate : function()
34890     {
34891         if(!this.isFitContainer){
34892             return this.getSplitAutoCreate();
34893         }
34894         
34895         var cls = 'masonry-brick masonry-brick-full';
34896         
34897         if(this.href.length){
34898             cls += ' masonry-brick-link';
34899         }
34900         
34901         if(this.bgimage.length){
34902             cls += ' masonry-brick-image';
34903         }
34904         
34905         if(this.maskInverse){
34906             cls += ' mask-inverse';
34907         }
34908         
34909         if(!this.html.length && !this.maskInverse && !this.videourl.length){
34910             cls += ' enable-mask';
34911         }
34912         
34913         if(this.size){
34914             cls += ' masonry-' + this.size + '-brick';
34915         }
34916         
34917         if(this.placetitle.length){
34918             
34919             switch (this.placetitle) {
34920                 case 'center' :
34921                     cls += ' masonry-center-title';
34922                     break;
34923                 case 'bottom' :
34924                     cls += ' masonry-bottom-title';
34925                     break;
34926                 default:
34927                     break;
34928             }
34929             
34930         } else {
34931             if(!this.html.length && !this.bgimage.length){
34932                 cls += ' masonry-center-title';
34933             }
34934
34935             if(!this.html.length && this.bgimage.length){
34936                 cls += ' masonry-bottom-title';
34937             }
34938         }
34939         
34940         if(this.cls){
34941             cls += ' ' + this.cls;
34942         }
34943         
34944         var cfg = {
34945             tag: (this.href.length) ? 'a' : 'div',
34946             cls: cls,
34947             cn: [
34948                 {
34949                     tag: 'div',
34950                     cls: 'masonry-brick-mask'
34951                 },
34952                 {
34953                     tag: 'div',
34954                     cls: 'masonry-brick-paragraph',
34955                     cn: []
34956                 }
34957             ]
34958         };
34959         
34960         if(this.href.length){
34961             cfg.href = this.href;
34962         }
34963         
34964         var cn = cfg.cn[1].cn;
34965         
34966         if(this.title.length){
34967             cn.push({
34968                 tag: 'h4',
34969                 cls: 'masonry-brick-title',
34970                 html: this.title
34971             });
34972         }
34973         
34974         if(this.html.length){
34975             cn.push({
34976                 tag: 'p',
34977                 cls: 'masonry-brick-text',
34978                 html: this.html
34979             });
34980         }
34981         
34982         if (!this.title.length && !this.html.length) {
34983             cfg.cn[1].cls += ' hide';
34984         }
34985         
34986         if(this.bgimage.length){
34987             cfg.cn.push({
34988                 tag: 'img',
34989                 cls: 'masonry-brick-image-view',
34990                 src: this.bgimage
34991             });
34992         }
34993         
34994         if(this.videourl.length){
34995             var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
34996             // youtube support only?
34997             cfg.cn.push({
34998                 tag: 'iframe',
34999                 cls: 'masonry-brick-image-view',
35000                 src: vurl,
35001                 frameborder : 0,
35002                 allowfullscreen : true
35003             });
35004         }
35005         
35006         return cfg;
35007         
35008     },
35009     
35010     getSplitAutoCreate : function()
35011     {
35012         var cls = 'masonry-brick masonry-brick-split';
35013         
35014         if(this.href.length){
35015             cls += ' masonry-brick-link';
35016         }
35017         
35018         if(this.bgimage.length){
35019             cls += ' masonry-brick-image';
35020         }
35021         
35022         if(this.size){
35023             cls += ' masonry-' + this.size + '-brick';
35024         }
35025         
35026         switch (this.placetitle) {
35027             case 'center' :
35028                 cls += ' masonry-center-title';
35029                 break;
35030             case 'bottom' :
35031                 cls += ' masonry-bottom-title';
35032                 break;
35033             default:
35034                 if(!this.bgimage.length){
35035                     cls += ' masonry-center-title';
35036                 }
35037
35038                 if(this.bgimage.length){
35039                     cls += ' masonry-bottom-title';
35040                 }
35041                 break;
35042         }
35043         
35044         if(this.cls){
35045             cls += ' ' + this.cls;
35046         }
35047         
35048         var cfg = {
35049             tag: (this.href.length) ? 'a' : 'div',
35050             cls: cls,
35051             cn: [
35052                 {
35053                     tag: 'div',
35054                     cls: 'masonry-brick-split-head',
35055                     cn: [
35056                         {
35057                             tag: 'div',
35058                             cls: 'masonry-brick-paragraph',
35059                             cn: []
35060                         }
35061                     ]
35062                 },
35063                 {
35064                     tag: 'div',
35065                     cls: 'masonry-brick-split-body',
35066                     cn: []
35067                 }
35068             ]
35069         };
35070         
35071         if(this.href.length){
35072             cfg.href = this.href;
35073         }
35074         
35075         if(this.title.length){
35076             cfg.cn[0].cn[0].cn.push({
35077                 tag: 'h4',
35078                 cls: 'masonry-brick-title',
35079                 html: this.title
35080             });
35081         }
35082         
35083         if(this.html.length){
35084             cfg.cn[1].cn.push({
35085                 tag: 'p',
35086                 cls: 'masonry-brick-text',
35087                 html: this.html
35088             });
35089         }
35090
35091         if(this.bgimage.length){
35092             cfg.cn[0].cn.push({
35093                 tag: 'img',
35094                 cls: 'masonry-brick-image-view',
35095                 src: this.bgimage
35096             });
35097         }
35098         
35099         if(this.videourl.length){
35100             var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
35101             // youtube support only?
35102             cfg.cn[0].cn.cn.push({
35103                 tag: 'iframe',
35104                 cls: 'masonry-brick-image-view',
35105                 src: vurl,
35106                 frameborder : 0,
35107                 allowfullscreen : true
35108             });
35109         }
35110         
35111         return cfg;
35112     },
35113     
35114     initEvents: function() 
35115     {
35116         switch (this.size) {
35117             case 'xs' :
35118                 this.x = 1;
35119                 this.y = 1;
35120                 break;
35121             case 'sm' :
35122                 this.x = 2;
35123                 this.y = 2;
35124                 break;
35125             case 'md' :
35126             case 'md-left' :
35127             case 'md-right' :
35128                 this.x = 3;
35129                 this.y = 3;
35130                 break;
35131             case 'tall' :
35132                 this.x = 2;
35133                 this.y = 3;
35134                 break;
35135             case 'wide' :
35136                 this.x = 3;
35137                 this.y = 2;
35138                 break;
35139             case 'wide-thin' :
35140                 this.x = 3;
35141                 this.y = 1;
35142                 break;
35143                         
35144             default :
35145                 break;
35146         }
35147         
35148         if(Roo.isTouch){
35149             this.el.on('touchstart', this.onTouchStart, this);
35150             this.el.on('touchmove', this.onTouchMove, this);
35151             this.el.on('touchend', this.onTouchEnd, this);
35152             this.el.on('contextmenu', this.onContextMenu, this);
35153         } else {
35154             this.el.on('mouseenter'  ,this.enter, this);
35155             this.el.on('mouseleave', this.leave, this);
35156             this.el.on('click', this.onClick, this);
35157         }
35158         
35159         if (typeof(this.parent().bricks) == 'object' && this.parent().bricks != null) {
35160             this.parent().bricks.push(this);   
35161         }
35162         
35163     },
35164     
35165     onClick: function(e, el)
35166     {
35167         var time = this.endTimer - this.startTimer;
35168         // Roo.log(e.preventDefault());
35169         if(Roo.isTouch){
35170             if(time > 1000){
35171                 e.preventDefault();
35172                 return;
35173             }
35174         }
35175         
35176         if(!this.preventDefault){
35177             return;
35178         }
35179         
35180         e.preventDefault();
35181         
35182         if (this.activeClass != '') {
35183             this.selectBrick();
35184         }
35185         
35186         this.fireEvent('click', this, e);
35187     },
35188     
35189     enter: function(e, el)
35190     {
35191         e.preventDefault();
35192         
35193         if(!this.isFitContainer || this.maskInverse || this.videourl.length){
35194             return;
35195         }
35196         
35197         if(this.bgimage.length && this.html.length){
35198             this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
35199         }
35200     },
35201     
35202     leave: function(e, el)
35203     {
35204         e.preventDefault();
35205         
35206         if(!this.isFitContainer || this.maskInverse  || this.videourl.length){
35207             return;
35208         }
35209         
35210         if(this.bgimage.length && this.html.length){
35211             this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
35212         }
35213     },
35214     
35215     onTouchStart: function(e, el)
35216     {
35217 //        e.preventDefault();
35218         
35219         this.touchmoved = false;
35220         
35221         if(!this.isFitContainer){
35222             return;
35223         }
35224         
35225         if(!this.bgimage.length || !this.html.length){
35226             return;
35227         }
35228         
35229         this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
35230         
35231         this.timer = new Date().getTime();
35232         
35233     },
35234     
35235     onTouchMove: function(e, el)
35236     {
35237         this.touchmoved = true;
35238     },
35239     
35240     onContextMenu : function(e,el)
35241     {
35242         e.preventDefault();
35243         e.stopPropagation();
35244         return false;
35245     },
35246     
35247     onTouchEnd: function(e, el)
35248     {
35249 //        e.preventDefault();
35250         
35251         if((new Date().getTime() - this.timer > 1000) || !this.href.length || this.touchmoved){
35252         
35253             this.leave(e,el);
35254             
35255             return;
35256         }
35257         
35258         if(!this.bgimage.length || !this.html.length){
35259             
35260             if(this.href.length){
35261                 window.location.href = this.href;
35262             }
35263             
35264             return;
35265         }
35266         
35267         if(!this.isFitContainer){
35268             return;
35269         }
35270         
35271         this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
35272         
35273         window.location.href = this.href;
35274     },
35275     
35276     //selection on single brick only
35277     selectBrick : function() {
35278         
35279         if (!this.parentId) {
35280             return;
35281         }
35282         
35283         var m = Roo.bootstrap.LayoutMasonry.get(this.parentId);
35284         var index = m.selectedBrick.indexOf(this.id);
35285         
35286         if ( index > -1) {
35287             m.selectedBrick.splice(index,1);
35288             this.el.removeClass(this.activeClass);
35289             return;
35290         }
35291         
35292         for(var i = 0; i < m.selectedBrick.length; i++) {
35293             var b = Roo.bootstrap.MasonryBrick.get(m.selectedBrick[i]);
35294             b.el.removeClass(b.activeClass);
35295         }
35296         
35297         m.selectedBrick = [];
35298         
35299         m.selectedBrick.push(this.id);
35300         this.el.addClass(this.activeClass);
35301         return;
35302     },
35303     
35304     isSelected : function(){
35305         return this.el.hasClass(this.activeClass);
35306         
35307     }
35308 });
35309
35310 Roo.apply(Roo.bootstrap.MasonryBrick, {
35311     
35312     //groups: {},
35313     groups : new Roo.util.MixedCollection(false, function(o) { return o.el.id; }),
35314      /**
35315     * register a Masonry Brick
35316     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
35317     */
35318     
35319     register : function(brick)
35320     {
35321         //this.groups[brick.id] = brick;
35322         this.groups.add(brick.id, brick);
35323     },
35324     /**
35325     * fetch a  masonry brick based on the masonry brick ID
35326     * @param {string} the masonry brick to add
35327     * @returns {Roo.bootstrap.MasonryBrick} the masonry brick
35328     */
35329     
35330     get: function(brick_id) 
35331     {
35332         // if (typeof(this.groups[brick_id]) == 'undefined') {
35333         //     return false;
35334         // }
35335         // return this.groups[brick_id] ;
35336         
35337         if(this.groups.key(brick_id)) {
35338             return this.groups.key(brick_id);
35339         }
35340         
35341         return false;
35342     }
35343     
35344     
35345     
35346 });
35347
35348  /*
35349  * - LGPL
35350  *
35351  * element
35352  * 
35353  */
35354
35355 /**
35356  * @class Roo.bootstrap.Brick
35357  * @extends Roo.bootstrap.Component
35358  * Bootstrap Brick class
35359  * 
35360  * @constructor
35361  * Create a new Brick
35362  * @param {Object} config The config object
35363  */
35364
35365 Roo.bootstrap.Brick = function(config){
35366     Roo.bootstrap.Brick.superclass.constructor.call(this, config);
35367     
35368     this.addEvents({
35369         // raw events
35370         /**
35371          * @event click
35372          * When a Brick is click
35373          * @param {Roo.bootstrap.Brick} this
35374          * @param {Roo.EventObject} e
35375          */
35376         "click" : true
35377     });
35378 };
35379
35380 Roo.extend(Roo.bootstrap.Brick, Roo.bootstrap.Component,  {
35381     
35382     /**
35383      * @cfg {String} title
35384      */   
35385     title : '',
35386     /**
35387      * @cfg {String} html
35388      */   
35389     html : '',
35390     /**
35391      * @cfg {String} bgimage
35392      */   
35393     bgimage : '',
35394     /**
35395      * @cfg {String} cls
35396      */   
35397     cls : '',
35398     /**
35399      * @cfg {String} href
35400      */   
35401     href : '',
35402     /**
35403      * @cfg {String} video
35404      */   
35405     video : '',
35406     /**
35407      * @cfg {Boolean} square
35408      */   
35409     square : true,
35410     
35411     getAutoCreate : function()
35412     {
35413         var cls = 'roo-brick';
35414         
35415         if(this.href.length){
35416             cls += ' roo-brick-link';
35417         }
35418         
35419         if(this.bgimage.length){
35420             cls += ' roo-brick-image';
35421         }
35422         
35423         if(!this.html.length && !this.bgimage.length){
35424             cls += ' roo-brick-center-title';
35425         }
35426         
35427         if(!this.html.length && this.bgimage.length){
35428             cls += ' roo-brick-bottom-title';
35429         }
35430         
35431         if(this.cls){
35432             cls += ' ' + this.cls;
35433         }
35434         
35435         var cfg = {
35436             tag: (this.href.length) ? 'a' : 'div',
35437             cls: cls,
35438             cn: [
35439                 {
35440                     tag: 'div',
35441                     cls: 'roo-brick-paragraph',
35442                     cn: []
35443                 }
35444             ]
35445         };
35446         
35447         if(this.href.length){
35448             cfg.href = this.href;
35449         }
35450         
35451         var cn = cfg.cn[0].cn;
35452         
35453         if(this.title.length){
35454             cn.push({
35455                 tag: 'h4',
35456                 cls: 'roo-brick-title',
35457                 html: this.title
35458             });
35459         }
35460         
35461         if(this.html.length){
35462             cn.push({
35463                 tag: 'p',
35464                 cls: 'roo-brick-text',
35465                 html: this.html
35466             });
35467         } else {
35468             cn.cls += ' hide';
35469         }
35470         
35471         if(this.bgimage.length){
35472             cfg.cn.push({
35473                 tag: 'img',
35474                 cls: 'roo-brick-image-view',
35475                 src: this.bgimage
35476             });
35477         }
35478         
35479         return cfg;
35480     },
35481     
35482     initEvents: function() 
35483     {
35484         if(this.title.length || this.html.length){
35485             this.el.on('mouseenter'  ,this.enter, this);
35486             this.el.on('mouseleave', this.leave, this);
35487         }
35488         
35489         Roo.EventManager.onWindowResize(this.resize, this); 
35490         
35491         if(this.bgimage.length){
35492             this.imageEl = this.el.select('.roo-brick-image-view', true).first();
35493             this.imageEl.on('load', this.onImageLoad, this);
35494             return;
35495         }
35496         
35497         this.resize();
35498     },
35499     
35500     onImageLoad : function()
35501     {
35502         this.resize();
35503     },
35504     
35505     resize : function()
35506     {
35507         var paragraph = this.el.select('.roo-brick-paragraph', true).first();
35508         
35509         paragraph.setHeight(paragraph.getWidth() + paragraph.getPadding('tb'));
35510         
35511         if(this.bgimage.length){
35512             var image = this.el.select('.roo-brick-image-view', true).first();
35513             
35514             image.setWidth(paragraph.getWidth());
35515             
35516             if(this.square){
35517                 image.setHeight(paragraph.getWidth());
35518             }
35519             
35520             this.el.setHeight(image.getHeight());
35521             paragraph.setHeight(image.getHeight());
35522             
35523         }
35524         
35525     },
35526     
35527     enter: function(e, el)
35528     {
35529         e.preventDefault();
35530         
35531         if(this.bgimage.length){
35532             this.el.select('.roo-brick-paragraph', true).first().setOpacity(0.9, true);
35533             this.el.select('.roo-brick-image-view', true).first().setOpacity(0.1, true);
35534         }
35535     },
35536     
35537     leave: function(e, el)
35538     {
35539         e.preventDefault();
35540         
35541         if(this.bgimage.length){
35542             this.el.select('.roo-brick-paragraph', true).first().setOpacity(0, true);
35543             this.el.select('.roo-brick-image-view', true).first().setOpacity(1, true);
35544         }
35545     }
35546     
35547 });
35548
35549  
35550
35551  /*
35552  * - LGPL
35553  *
35554  * Number field 
35555  */
35556
35557 /**
35558  * @class Roo.bootstrap.NumberField
35559  * @extends Roo.bootstrap.Input
35560  * Bootstrap NumberField class
35561  * 
35562  * 
35563  * 
35564  * 
35565  * @constructor
35566  * Create a new NumberField
35567  * @param {Object} config The config object
35568  */
35569
35570 Roo.bootstrap.NumberField = function(config){
35571     Roo.bootstrap.NumberField.superclass.constructor.call(this, config);
35572 };
35573
35574 Roo.extend(Roo.bootstrap.NumberField, Roo.bootstrap.Input, {
35575     
35576     /**
35577      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
35578      */
35579     allowDecimals : true,
35580     /**
35581      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
35582      */
35583     decimalSeparator : ".",
35584     /**
35585      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
35586      */
35587     decimalPrecision : 2,
35588     /**
35589      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
35590      */
35591     allowNegative : true,
35592     
35593     /**
35594      * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
35595      */
35596     allowZero: true,
35597     /**
35598      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
35599      */
35600     minValue : Number.NEGATIVE_INFINITY,
35601     /**
35602      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
35603      */
35604     maxValue : Number.MAX_VALUE,
35605     /**
35606      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
35607      */
35608     minText : "The minimum value for this field is {0}",
35609     /**
35610      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
35611      */
35612     maxText : "The maximum value for this field is {0}",
35613     /**
35614      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
35615      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
35616      */
35617     nanText : "{0} is not a valid number",
35618     /**
35619      * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
35620      */
35621     thousandsDelimiter : false,
35622     /**
35623      * @cfg {String} valueAlign alignment of value
35624      */
35625     valueAlign : "left",
35626
35627     getAutoCreate : function()
35628     {
35629         var hiddenInput = {
35630             tag: 'input',
35631             type: 'hidden',
35632             id: Roo.id(),
35633             cls: 'hidden-number-input'
35634         };
35635         
35636         if (this.name) {
35637             hiddenInput.name = this.name;
35638         }
35639         
35640         this.name = '';
35641         
35642         var cfg = Roo.bootstrap.NumberField.superclass.getAutoCreate.call(this);
35643         
35644         this.name = hiddenInput.name;
35645         
35646         if(cfg.cn.length > 0) {
35647             cfg.cn.push(hiddenInput);
35648         }
35649         
35650         return cfg;
35651     },
35652
35653     // private
35654     initEvents : function()
35655     {   
35656         Roo.bootstrap.NumberField.superclass.initEvents.call(this);
35657         
35658         var allowed = "0123456789";
35659         
35660         if(this.allowDecimals){
35661             allowed += this.decimalSeparator;
35662         }
35663         
35664         if(this.allowNegative){
35665             allowed += "-";
35666         }
35667         
35668         if(this.thousandsDelimiter) {
35669             allowed += ",";
35670         }
35671         
35672         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
35673         
35674         var keyPress = function(e){
35675             
35676             var k = e.getKey();
35677             
35678             var c = e.getCharCode();
35679             
35680             if(
35681                     (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
35682                     allowed.indexOf(String.fromCharCode(c)) === -1
35683             ){
35684                 e.stopEvent();
35685                 return;
35686             }
35687             
35688             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
35689                 return;
35690             }
35691             
35692             if(allowed.indexOf(String.fromCharCode(c)) === -1){
35693                 e.stopEvent();
35694             }
35695         };
35696         
35697         this.el.on("keypress", keyPress, this);
35698     },
35699     
35700     validateValue : function(value)
35701     {
35702         
35703         if(!Roo.bootstrap.NumberField.superclass.validateValue.call(this, value)){
35704             return false;
35705         }
35706         
35707         var num = this.parseValue(value);
35708         
35709         if(isNaN(num)){
35710             this.markInvalid(String.format(this.nanText, value));
35711             return false;
35712         }
35713         
35714         if(num < this.minValue){
35715             this.markInvalid(String.format(this.minText, this.minValue));
35716             return false;
35717         }
35718         
35719         if(num > this.maxValue){
35720             this.markInvalid(String.format(this.maxText, this.maxValue));
35721             return false;
35722         }
35723         
35724         return true;
35725     },
35726
35727     getValue : function()
35728     {
35729         var v = this.hiddenEl().getValue();
35730         
35731         return this.fixPrecision(this.parseValue(v));
35732     },
35733
35734     parseValue : function(value)
35735     {
35736         if(this.thousandsDelimiter) {
35737             value += "";
35738             r = new RegExp(",", "g");
35739             value = value.replace(r, "");
35740         }
35741         
35742         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
35743         return isNaN(value) ? '' : value;
35744     },
35745
35746     fixPrecision : function(value)
35747     {
35748         if(this.thousandsDelimiter) {
35749             value += "";
35750             r = new RegExp(",", "g");
35751             value = value.replace(r, "");
35752         }
35753         
35754         var nan = isNaN(value);
35755         
35756         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
35757             return nan ? '' : value;
35758         }
35759         return parseFloat(value).toFixed(this.decimalPrecision);
35760     },
35761
35762     setValue : function(v)
35763     {
35764         v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
35765         
35766         this.value = v;
35767         
35768         if(this.rendered){
35769             
35770             this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
35771             
35772             this.inputEl().dom.value = (v == '') ? '' :
35773                 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
35774             
35775             if(!this.allowZero && v === '0') {
35776                 this.hiddenEl().dom.value = '';
35777                 this.inputEl().dom.value = '';
35778             }
35779             
35780             this.validate();
35781         }
35782     },
35783
35784     decimalPrecisionFcn : function(v)
35785     {
35786         return Math.floor(v);
35787     },
35788
35789     beforeBlur : function()
35790     {
35791         var v = this.parseValue(this.getRawValue());
35792         
35793         if(v || v === 0 || v === ''){
35794             this.setValue(v);
35795         }
35796     },
35797     
35798     hiddenEl : function()
35799     {
35800         return this.el.select('input.hidden-number-input',true).first();
35801     }
35802     
35803 });
35804
35805  
35806
35807 /*
35808 * Licence: LGPL
35809 */
35810
35811 /**
35812  * @class Roo.bootstrap.DocumentSlider
35813  * @extends Roo.bootstrap.Component
35814  * Bootstrap DocumentSlider class
35815  * 
35816  * @constructor
35817  * Create a new DocumentViewer
35818  * @param {Object} config The config object
35819  */
35820
35821 Roo.bootstrap.DocumentSlider = function(config){
35822     Roo.bootstrap.DocumentSlider.superclass.constructor.call(this, config);
35823     
35824     this.files = [];
35825     
35826     this.addEvents({
35827         /**
35828          * @event initial
35829          * Fire after initEvent
35830          * @param {Roo.bootstrap.DocumentSlider} this
35831          */
35832         "initial" : true,
35833         /**
35834          * @event update
35835          * Fire after update
35836          * @param {Roo.bootstrap.DocumentSlider} this
35837          */
35838         "update" : true,
35839         /**
35840          * @event click
35841          * Fire after click
35842          * @param {Roo.bootstrap.DocumentSlider} this
35843          */
35844         "click" : true
35845     });
35846 };
35847
35848 Roo.extend(Roo.bootstrap.DocumentSlider, Roo.bootstrap.Component,  {
35849     
35850     files : false,
35851     
35852     indicator : 0,
35853     
35854     getAutoCreate : function()
35855     {
35856         var cfg = {
35857             tag : 'div',
35858             cls : 'roo-document-slider',
35859             cn : [
35860                 {
35861                     tag : 'div',
35862                     cls : 'roo-document-slider-header',
35863                     cn : [
35864                         {
35865                             tag : 'div',
35866                             cls : 'roo-document-slider-header-title'
35867                         }
35868                     ]
35869                 },
35870                 {
35871                     tag : 'div',
35872                     cls : 'roo-document-slider-body',
35873                     cn : [
35874                         {
35875                             tag : 'div',
35876                             cls : 'roo-document-slider-prev',
35877                             cn : [
35878                                 {
35879                                     tag : 'i',
35880                                     cls : 'fa fa-chevron-left'
35881                                 }
35882                             ]
35883                         },
35884                         {
35885                             tag : 'div',
35886                             cls : 'roo-document-slider-thumb',
35887                             cn : [
35888                                 {
35889                                     tag : 'img',
35890                                     cls : 'roo-document-slider-image'
35891                                 }
35892                             ]
35893                         },
35894                         {
35895                             tag : 'div',
35896                             cls : 'roo-document-slider-next',
35897                             cn : [
35898                                 {
35899                                     tag : 'i',
35900                                     cls : 'fa fa-chevron-right'
35901                                 }
35902                             ]
35903                         }
35904                     ]
35905                 }
35906             ]
35907         };
35908         
35909         return cfg;
35910     },
35911     
35912     initEvents : function()
35913     {
35914         this.headerEl = this.el.select('.roo-document-slider-header', true).first();
35915         this.headerEl.setVisibilityMode(Roo.Element.DISPLAY);
35916         
35917         this.titleEl = this.el.select('.roo-document-slider-header .roo-document-slider-header-title', true).first();
35918         this.titleEl.setVisibilityMode(Roo.Element.DISPLAY);
35919         
35920         this.bodyEl = this.el.select('.roo-document-slider-body', true).first();
35921         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
35922         
35923         this.thumbEl = this.el.select('.roo-document-slider-thumb', true).first();
35924         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
35925         
35926         this.imageEl = this.el.select('.roo-document-slider-image', true).first();
35927         this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
35928         
35929         this.prevIndicator = this.el.select('.roo-document-slider-prev i', true).first();
35930         this.prevIndicator.setVisibilityMode(Roo.Element.DISPLAY);
35931         
35932         this.nextIndicator = this.el.select('.roo-document-slider-next i', true).first();
35933         this.nextIndicator.setVisibilityMode(Roo.Element.DISPLAY);
35934         
35935         this.thumbEl.on('click', this.onClick, this);
35936         
35937         this.prevIndicator.on('click', this.prev, this);
35938         
35939         this.nextIndicator.on('click', this.next, this);
35940         
35941     },
35942     
35943     initial : function()
35944     {
35945         if(this.files.length){
35946             this.indicator = 1;
35947             this.update()
35948         }
35949         
35950         this.fireEvent('initial', this);
35951     },
35952     
35953     update : function()
35954     {
35955         this.imageEl.attr('src', this.files[this.indicator - 1]);
35956         
35957         this.titleEl.dom.innerHTML = String.format('{0} / {1}', this.indicator, this.files.length);
35958         
35959         this.prevIndicator.show();
35960         
35961         if(this.indicator == 1){
35962             this.prevIndicator.hide();
35963         }
35964         
35965         this.nextIndicator.show();
35966         
35967         if(this.indicator == this.files.length){
35968             this.nextIndicator.hide();
35969         }
35970         
35971         this.thumbEl.scrollTo('top');
35972         
35973         this.fireEvent('update', this);
35974     },
35975     
35976     onClick : function(e)
35977     {
35978         e.preventDefault();
35979         
35980         this.fireEvent('click', this);
35981     },
35982     
35983     prev : function(e)
35984     {
35985         e.preventDefault();
35986         
35987         this.indicator = Math.max(1, this.indicator - 1);
35988         
35989         this.update();
35990     },
35991     
35992     next : function(e)
35993     {
35994         e.preventDefault();
35995         
35996         this.indicator = Math.min(this.files.length, this.indicator + 1);
35997         
35998         this.update();
35999     }
36000 });
36001 /*
36002  * - LGPL
36003  *
36004  * RadioSet
36005  *
36006  *
36007  */
36008
36009 /**
36010  * @class Roo.bootstrap.RadioSet
36011  * @extends Roo.bootstrap.Input
36012  * Bootstrap RadioSet class
36013  * @cfg {String} indicatorpos (left|right) default left
36014  * @cfg {Boolean} inline (true|false) inline the element (default true)
36015  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the radio
36016  * @constructor
36017  * Create a new RadioSet
36018  * @param {Object} config The config object
36019  */
36020
36021 Roo.bootstrap.RadioSet = function(config){
36022     
36023     Roo.bootstrap.RadioSet.superclass.constructor.call(this, config);
36024     
36025     this.radioes = [];
36026     
36027     Roo.bootstrap.RadioSet.register(this);
36028     
36029     this.addEvents({
36030         /**
36031         * @event check
36032         * Fires when the element is checked or unchecked.
36033         * @param {Roo.bootstrap.RadioSet} this This radio
36034         * @param {Roo.bootstrap.Radio} item The checked item
36035         */
36036        check : true,
36037        /**
36038         * @event click
36039         * Fires when the element is click.
36040         * @param {Roo.bootstrap.RadioSet} this This radio set
36041         * @param {Roo.bootstrap.Radio} item The checked item
36042         * @param {Roo.EventObject} e The event object
36043         */
36044        click : true
36045     });
36046     
36047 };
36048
36049 Roo.extend(Roo.bootstrap.RadioSet, Roo.bootstrap.Input,  {
36050
36051     radioes : false,
36052     
36053     inline : true,
36054     
36055     weight : '',
36056     
36057     indicatorpos : 'left',
36058     
36059     getAutoCreate : function()
36060     {
36061         var label = {
36062             tag : 'label',
36063             cls : 'roo-radio-set-label',
36064             cn : [
36065                 {
36066                     tag : 'span',
36067                     html : this.fieldLabel
36068                 }
36069             ]
36070         };
36071         if (Roo.bootstrap.version == 3) {
36072             
36073             
36074             if(this.indicatorpos == 'left'){
36075                 label.cn.unshift({
36076                     tag : 'i',
36077                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
36078                     tooltip : 'This field is required'
36079                 });
36080             } else {
36081                 label.cn.push({
36082                     tag : 'i',
36083                     cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
36084                     tooltip : 'This field is required'
36085                 });
36086             }
36087         }
36088         var items = {
36089             tag : 'div',
36090             cls : 'roo-radio-set-items'
36091         };
36092         
36093         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
36094         
36095         if (align === 'left' && this.fieldLabel.length) {
36096             
36097             items = {
36098                 cls : "roo-radio-set-right", 
36099                 cn: [
36100                     items
36101                 ]
36102             };
36103             
36104             if(this.labelWidth > 12){
36105                 label.style = "width: " + this.labelWidth + 'px';
36106             }
36107             
36108             if(this.labelWidth < 13 && this.labelmd == 0){
36109                 this.labelmd = this.labelWidth;
36110             }
36111             
36112             if(this.labellg > 0){
36113                 label.cls += ' col-lg-' + this.labellg;
36114                 items.cls += ' col-lg-' + (12 - this.labellg);
36115             }
36116             
36117             if(this.labelmd > 0){
36118                 label.cls += ' col-md-' + this.labelmd;
36119                 items.cls += ' col-md-' + (12 - this.labelmd);
36120             }
36121             
36122             if(this.labelsm > 0){
36123                 label.cls += ' col-sm-' + this.labelsm;
36124                 items.cls += ' col-sm-' + (12 - this.labelsm);
36125             }
36126             
36127             if(this.labelxs > 0){
36128                 label.cls += ' col-xs-' + this.labelxs;
36129                 items.cls += ' col-xs-' + (12 - this.labelxs);
36130             }
36131         }
36132         
36133         var cfg = {
36134             tag : 'div',
36135             cls : 'roo-radio-set',
36136             cn : [
36137                 {
36138                     tag : 'input',
36139                     cls : 'roo-radio-set-input',
36140                     type : 'hidden',
36141                     name : this.name,
36142                     value : this.value ? this.value :  ''
36143                 },
36144                 label,
36145                 items
36146             ]
36147         };
36148         
36149         if(this.weight.length){
36150             cfg.cls += ' roo-radio-' + this.weight;
36151         }
36152         
36153         if(this.inline) {
36154             cfg.cls += ' roo-radio-set-inline';
36155         }
36156         
36157         var settings=this;
36158         ['xs','sm','md','lg'].map(function(size){
36159             if (settings[size]) {
36160                 cfg.cls += ' col-' + size + '-' + settings[size];
36161             }
36162         });
36163         
36164         return cfg;
36165         
36166     },
36167
36168     initEvents : function()
36169     {
36170         this.labelEl = this.el.select('.roo-radio-set-label', true).first();
36171         this.labelEl.setVisibilityMode(Roo.Element.DISPLAY);
36172         
36173         if(!this.fieldLabel.length){
36174             this.labelEl.hide();
36175         }
36176         
36177         this.itemsEl = this.el.select('.roo-radio-set-items', true).first();
36178         this.itemsEl.setVisibilityMode(Roo.Element.DISPLAY);
36179         
36180         this.indicator = this.indicatorEl();
36181         
36182         if(this.indicator){
36183             this.indicator.addClass('invisible');
36184         }
36185         
36186         this.originalValue = this.getValue();
36187         
36188     },
36189     
36190     inputEl: function ()
36191     {
36192         return this.el.select('.roo-radio-set-input', true).first();
36193     },
36194     
36195     getChildContainer : function()
36196     {
36197         return this.itemsEl;
36198     },
36199     
36200     register : function(item)
36201     {
36202         this.radioes.push(item);
36203         
36204     },
36205     
36206     validate : function()
36207     {   
36208         if(this.getVisibilityEl().hasClass('hidden')){
36209             return true;
36210         }
36211         
36212         var valid = false;
36213         
36214         Roo.each(this.radioes, function(i){
36215             if(!i.checked){
36216                 return;
36217             }
36218             
36219             valid = true;
36220             return false;
36221         });
36222         
36223         if(this.allowBlank) {
36224             return true;
36225         }
36226         
36227         if(this.disabled || valid){
36228             this.markValid();
36229             return true;
36230         }
36231         
36232         this.markInvalid();
36233         return false;
36234         
36235     },
36236     
36237     markValid : function()
36238     {
36239         if(this.labelEl.isVisible(true) && this.indicatorEl()){
36240             this.indicatorEl().removeClass('visible');
36241             this.indicatorEl().addClass('invisible');
36242         }
36243         
36244         
36245         if (Roo.bootstrap.version == 3) {
36246             this.el.removeClass([this.invalidClass, this.validClass]);
36247             this.el.addClass(this.validClass);
36248         } else {
36249             this.el.removeClass(['is-invalid','is-valid']);
36250             this.el.addClass(['is-valid']);
36251         }
36252         this.fireEvent('valid', this);
36253     },
36254     
36255     markInvalid : function(msg)
36256     {
36257         if(this.allowBlank || this.disabled){
36258             return;
36259         }
36260         
36261         if(this.labelEl.isVisible(true) && this.indicatorEl()){
36262             this.indicatorEl().removeClass('invisible');
36263             this.indicatorEl().addClass('visible');
36264         }
36265         if (Roo.bootstrap.version == 3) {
36266             this.el.removeClass([this.invalidClass, this.validClass]);
36267             this.el.addClass(this.invalidClass);
36268         } else {
36269             this.el.removeClass(['is-invalid','is-valid']);
36270             this.el.addClass(['is-invalid']);
36271         }
36272         
36273         this.fireEvent('invalid', this, msg);
36274         
36275     },
36276     
36277     setValue : function(v, suppressEvent)
36278     {   
36279         if(this.value === v){
36280             return;
36281         }
36282         
36283         this.value = v;
36284         
36285         if(this.rendered){
36286             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
36287         }
36288         
36289         Roo.each(this.radioes, function(i){
36290             i.checked = false;
36291             i.el.removeClass('checked');
36292         });
36293         
36294         Roo.each(this.radioes, function(i){
36295             
36296             if(i.value === v || i.value.toString() === v.toString()){
36297                 i.checked = true;
36298                 i.el.addClass('checked');
36299                 
36300                 if(suppressEvent !== true){
36301                     this.fireEvent('check', this, i);
36302                 }
36303                 
36304                 return false;
36305             }
36306             
36307         }, this);
36308         
36309         this.validate();
36310     },
36311     
36312     clearInvalid : function(){
36313         
36314         if(!this.el || this.preventMark){
36315             return;
36316         }
36317         
36318         this.el.removeClass([this.invalidClass]);
36319         
36320         this.fireEvent('valid', this);
36321     }
36322     
36323 });
36324
36325 Roo.apply(Roo.bootstrap.RadioSet, {
36326     
36327     groups: {},
36328     
36329     register : function(set)
36330     {
36331         this.groups[set.name] = set;
36332     },
36333     
36334     get: function(name) 
36335     {
36336         if (typeof(this.groups[name]) == 'undefined') {
36337             return false;
36338         }
36339         
36340         return this.groups[name] ;
36341     }
36342     
36343 });
36344 /*
36345  * Based on:
36346  * Ext JS Library 1.1.1
36347  * Copyright(c) 2006-2007, Ext JS, LLC.
36348  *
36349  * Originally Released Under LGPL - original licence link has changed is not relivant.
36350  *
36351  * Fork - LGPL
36352  * <script type="text/javascript">
36353  */
36354
36355
36356 /**
36357  * @class Roo.bootstrap.SplitBar
36358  * @extends Roo.util.Observable
36359  * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
36360  * <br><br>
36361  * Usage:
36362  * <pre><code>
36363 var split = new Roo.bootstrap.SplitBar("elementToDrag", "elementToSize",
36364                    Roo.bootstrap.SplitBar.HORIZONTAL, Roo.bootstrap.SplitBar.LEFT);
36365 split.setAdapter(new Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter("container"));
36366 split.minSize = 100;
36367 split.maxSize = 600;
36368 split.animate = true;
36369 split.on('moved', splitterMoved);
36370 </code></pre>
36371  * @constructor
36372  * Create a new SplitBar
36373  * @config {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar. 
36374  * @config {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged 
36375  * @config {Number} orientation (optional) Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
36376  * @config {Number} placement (optional) Either Roo.bootstrap.SplitBar.LEFT or Roo.bootstrap.SplitBar.RIGHT for horizontal or  
36377                         Roo.bootstrap.SplitBar.TOP or Roo.bootstrap.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
36378                         position of the SplitBar).
36379  */
36380 Roo.bootstrap.SplitBar = function(cfg){
36381     
36382     /** @private */
36383     
36384     //{
36385     //  dragElement : elm
36386     //  resizingElement: el,
36387         // optional..
36388     //    orientation : Either Roo.bootstrap.SplitBar.HORIZONTAL
36389     //    placement : Roo.bootstrap.SplitBar.LEFT  ,
36390         // existingProxy ???
36391     //}
36392     
36393     this.el = Roo.get(cfg.dragElement, true);
36394     this.el.dom.unselectable = "on";
36395     /** @private */
36396     this.resizingEl = Roo.get(cfg.resizingElement, true);
36397
36398     /**
36399      * @private
36400      * The orientation of the split. Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
36401      * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
36402      * @type Number
36403      */
36404     this.orientation = cfg.orientation || Roo.bootstrap.SplitBar.HORIZONTAL;
36405     
36406     /**
36407      * The minimum size of the resizing element. (Defaults to 0)
36408      * @type Number
36409      */
36410     this.minSize = 0;
36411     
36412     /**
36413      * The maximum size of the resizing element. (Defaults to 2000)
36414      * @type Number
36415      */
36416     this.maxSize = 2000;
36417     
36418     /**
36419      * Whether to animate the transition to the new size
36420      * @type Boolean
36421      */
36422     this.animate = false;
36423     
36424     /**
36425      * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
36426      * @type Boolean
36427      */
36428     this.useShim = false;
36429     
36430     /** @private */
36431     this.shim = null;
36432     
36433     if(!cfg.existingProxy){
36434         /** @private */
36435         this.proxy = Roo.bootstrap.SplitBar.createProxy(this.orientation);
36436     }else{
36437         this.proxy = Roo.get(cfg.existingProxy).dom;
36438     }
36439     /** @private */
36440     this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
36441     
36442     /** @private */
36443     this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
36444     
36445     /** @private */
36446     this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
36447     
36448     /** @private */
36449     this.dragSpecs = {};
36450     
36451     /**
36452      * @private The adapter to use to positon and resize elements
36453      */
36454     this.adapter = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
36455     this.adapter.init(this);
36456     
36457     if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
36458         /** @private */
36459         this.placement = cfg.placement || (this.el.getX() > this.resizingEl.getX() ? Roo.bootstrap.SplitBar.LEFT : Roo.bootstrap.SplitBar.RIGHT);
36460         this.el.addClass("roo-splitbar-h");
36461     }else{
36462         /** @private */
36463         this.placement = cfg.placement || (this.el.getY() > this.resizingEl.getY() ? Roo.bootstrap.SplitBar.TOP : Roo.bootstrap.SplitBar.BOTTOM);
36464         this.el.addClass("roo-splitbar-v");
36465     }
36466     
36467     this.addEvents({
36468         /**
36469          * @event resize
36470          * Fires when the splitter is moved (alias for {@link #event-moved})
36471          * @param {Roo.bootstrap.SplitBar} this
36472          * @param {Number} newSize the new width or height
36473          */
36474         "resize" : true,
36475         /**
36476          * @event moved
36477          * Fires when the splitter is moved
36478          * @param {Roo.bootstrap.SplitBar} this
36479          * @param {Number} newSize the new width or height
36480          */
36481         "moved" : true,
36482         /**
36483          * @event beforeresize
36484          * Fires before the splitter is dragged
36485          * @param {Roo.bootstrap.SplitBar} this
36486          */
36487         "beforeresize" : true,
36488
36489         "beforeapply" : true
36490     });
36491
36492     Roo.util.Observable.call(this);
36493 };
36494
36495 Roo.extend(Roo.bootstrap.SplitBar, Roo.util.Observable, {
36496     onStartProxyDrag : function(x, y){
36497         this.fireEvent("beforeresize", this);
36498         if(!this.overlay){
36499             var o = Roo.DomHelper.insertFirst(document.body,  {cls: "roo-drag-overlay", html: "&#160;"}, true);
36500             o.unselectable();
36501             o.enableDisplayMode("block");
36502             // all splitbars share the same overlay
36503             Roo.bootstrap.SplitBar.prototype.overlay = o;
36504         }
36505         this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
36506         this.overlay.show();
36507         Roo.get(this.proxy).setDisplayed("block");
36508         var size = this.adapter.getElementSize(this);
36509         this.activeMinSize = this.getMinimumSize();;
36510         this.activeMaxSize = this.getMaximumSize();;
36511         var c1 = size - this.activeMinSize;
36512         var c2 = Math.max(this.activeMaxSize - size, 0);
36513         if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
36514             this.dd.resetConstraints();
36515             this.dd.setXConstraint(
36516                 this.placement == Roo.bootstrap.SplitBar.LEFT ? c1 : c2, 
36517                 this.placement == Roo.bootstrap.SplitBar.LEFT ? c2 : c1
36518             );
36519             this.dd.setYConstraint(0, 0);
36520         }else{
36521             this.dd.resetConstraints();
36522             this.dd.setXConstraint(0, 0);
36523             this.dd.setYConstraint(
36524                 this.placement == Roo.bootstrap.SplitBar.TOP ? c1 : c2, 
36525                 this.placement == Roo.bootstrap.SplitBar.TOP ? c2 : c1
36526             );
36527          }
36528         this.dragSpecs.startSize = size;
36529         this.dragSpecs.startPoint = [x, y];
36530         Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
36531     },
36532     
36533     /** 
36534      * @private Called after the drag operation by the DDProxy
36535      */
36536     onEndProxyDrag : function(e){
36537         Roo.get(this.proxy).setDisplayed(false);
36538         var endPoint = Roo.lib.Event.getXY(e);
36539         if(this.overlay){
36540             this.overlay.hide();
36541         }
36542         var newSize;
36543         if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
36544             newSize = this.dragSpecs.startSize + 
36545                 (this.placement == Roo.bootstrap.SplitBar.LEFT ?
36546                     endPoint[0] - this.dragSpecs.startPoint[0] :
36547                     this.dragSpecs.startPoint[0] - endPoint[0]
36548                 );
36549         }else{
36550             newSize = this.dragSpecs.startSize + 
36551                 (this.placement == Roo.bootstrap.SplitBar.TOP ?
36552                     endPoint[1] - this.dragSpecs.startPoint[1] :
36553                     this.dragSpecs.startPoint[1] - endPoint[1]
36554                 );
36555         }
36556         newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
36557         if(newSize != this.dragSpecs.startSize){
36558             if(this.fireEvent('beforeapply', this, newSize) !== false){
36559                 this.adapter.setElementSize(this, newSize);
36560                 this.fireEvent("moved", this, newSize);
36561                 this.fireEvent("resize", this, newSize);
36562             }
36563         }
36564     },
36565     
36566     /**
36567      * Get the adapter this SplitBar uses
36568      * @return The adapter object
36569      */
36570     getAdapter : function(){
36571         return this.adapter;
36572     },
36573     
36574     /**
36575      * Set the adapter this SplitBar uses
36576      * @param {Object} adapter A SplitBar adapter object
36577      */
36578     setAdapter : function(adapter){
36579         this.adapter = adapter;
36580         this.adapter.init(this);
36581     },
36582     
36583     /**
36584      * Gets the minimum size for the resizing element
36585      * @return {Number} The minimum size
36586      */
36587     getMinimumSize : function(){
36588         return this.minSize;
36589     },
36590     
36591     /**
36592      * Sets the minimum size for the resizing element
36593      * @param {Number} minSize The minimum size
36594      */
36595     setMinimumSize : function(minSize){
36596         this.minSize = minSize;
36597     },
36598     
36599     /**
36600      * Gets the maximum size for the resizing element
36601      * @return {Number} The maximum size
36602      */
36603     getMaximumSize : function(){
36604         return this.maxSize;
36605     },
36606     
36607     /**
36608      * Sets the maximum size for the resizing element
36609      * @param {Number} maxSize The maximum size
36610      */
36611     setMaximumSize : function(maxSize){
36612         this.maxSize = maxSize;
36613     },
36614     
36615     /**
36616      * Sets the initialize size for the resizing element
36617      * @param {Number} size The initial size
36618      */
36619     setCurrentSize : function(size){
36620         var oldAnimate = this.animate;
36621         this.animate = false;
36622         this.adapter.setElementSize(this, size);
36623         this.animate = oldAnimate;
36624     },
36625     
36626     /**
36627      * Destroy this splitbar. 
36628      * @param {Boolean} removeEl True to remove the element
36629      */
36630     destroy : function(removeEl){
36631         if(this.shim){
36632             this.shim.remove();
36633         }
36634         this.dd.unreg();
36635         this.proxy.parentNode.removeChild(this.proxy);
36636         if(removeEl){
36637             this.el.remove();
36638         }
36639     }
36640 });
36641
36642 /**
36643  * @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.
36644  */
36645 Roo.bootstrap.SplitBar.createProxy = function(dir){
36646     var proxy = new Roo.Element(document.createElement("div"));
36647     proxy.unselectable();
36648     var cls = 'roo-splitbar-proxy';
36649     proxy.addClass(cls + ' ' + (dir == Roo.bootstrap.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
36650     document.body.appendChild(proxy.dom);
36651     return proxy.dom;
36652 };
36653
36654 /** 
36655  * @class Roo.bootstrap.SplitBar.BasicLayoutAdapter
36656  * Default Adapter. It assumes the splitter and resizing element are not positioned
36657  * elements and only gets/sets the width of the element. Generally used for table based layouts.
36658  */
36659 Roo.bootstrap.SplitBar.BasicLayoutAdapter = function(){
36660 };
36661
36662 Roo.bootstrap.SplitBar.BasicLayoutAdapter.prototype = {
36663     // do nothing for now
36664     init : function(s){
36665     
36666     },
36667     /**
36668      * Called before drag operations to get the current size of the resizing element. 
36669      * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
36670      */
36671      getElementSize : function(s){
36672         if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
36673             return s.resizingEl.getWidth();
36674         }else{
36675             return s.resizingEl.getHeight();
36676         }
36677     },
36678     
36679     /**
36680      * Called after drag operations to set the size of the resizing element.
36681      * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
36682      * @param {Number} newSize The new size to set
36683      * @param {Function} onComplete A function to be invoked when resizing is complete
36684      */
36685     setElementSize : function(s, newSize, onComplete){
36686         if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
36687             if(!s.animate){
36688                 s.resizingEl.setWidth(newSize);
36689                 if(onComplete){
36690                     onComplete(s, newSize);
36691                 }
36692             }else{
36693                 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
36694             }
36695         }else{
36696             
36697             if(!s.animate){
36698                 s.resizingEl.setHeight(newSize);
36699                 if(onComplete){
36700                     onComplete(s, newSize);
36701                 }
36702             }else{
36703                 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
36704             }
36705         }
36706     }
36707 };
36708
36709 /** 
36710  *@class Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter
36711  * @extends Roo.bootstrap.SplitBar.BasicLayoutAdapter
36712  * Adapter that  moves the splitter element to align with the resized sizing element. 
36713  * Used with an absolute positioned SplitBar.
36714  * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
36715  * document.body, make sure you assign an id to the body element.
36716  */
36717 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter = function(container){
36718     this.basic = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
36719     this.container = Roo.get(container);
36720 };
36721
36722 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter.prototype = {
36723     init : function(s){
36724         this.basic.init(s);
36725     },
36726     
36727     getElementSize : function(s){
36728         return this.basic.getElementSize(s);
36729     },
36730     
36731     setElementSize : function(s, newSize, onComplete){
36732         this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
36733     },
36734     
36735     moveSplitter : function(s){
36736         var yes = Roo.bootstrap.SplitBar;
36737         switch(s.placement){
36738             case yes.LEFT:
36739                 s.el.setX(s.resizingEl.getRight());
36740                 break;
36741             case yes.RIGHT:
36742                 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
36743                 break;
36744             case yes.TOP:
36745                 s.el.setY(s.resizingEl.getBottom());
36746                 break;
36747             case yes.BOTTOM:
36748                 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
36749                 break;
36750         }
36751     }
36752 };
36753
36754 /**
36755  * Orientation constant - Create a vertical SplitBar
36756  * @static
36757  * @type Number
36758  */
36759 Roo.bootstrap.SplitBar.VERTICAL = 1;
36760
36761 /**
36762  * Orientation constant - Create a horizontal SplitBar
36763  * @static
36764  * @type Number
36765  */
36766 Roo.bootstrap.SplitBar.HORIZONTAL = 2;
36767
36768 /**
36769  * Placement constant - The resizing element is to the left of the splitter element
36770  * @static
36771  * @type Number
36772  */
36773 Roo.bootstrap.SplitBar.LEFT = 1;
36774
36775 /**
36776  * Placement constant - The resizing element is to the right of the splitter element
36777  * @static
36778  * @type Number
36779  */
36780 Roo.bootstrap.SplitBar.RIGHT = 2;
36781
36782 /**
36783  * Placement constant - The resizing element is positioned above the splitter element
36784  * @static
36785  * @type Number
36786  */
36787 Roo.bootstrap.SplitBar.TOP = 3;
36788
36789 /**
36790  * Placement constant - The resizing element is positioned under splitter element
36791  * @static
36792  * @type Number
36793  */
36794 Roo.bootstrap.SplitBar.BOTTOM = 4;
36795 Roo.namespace("Roo.bootstrap.layout");/*
36796  * Based on:
36797  * Ext JS Library 1.1.1
36798  * Copyright(c) 2006-2007, Ext JS, LLC.
36799  *
36800  * Originally Released Under LGPL - original licence link has changed is not relivant.
36801  *
36802  * Fork - LGPL
36803  * <script type="text/javascript">
36804  */
36805
36806 /**
36807  * @class Roo.bootstrap.layout.Manager
36808  * @extends Roo.bootstrap.Component
36809  * Base class for layout managers.
36810  */
36811 Roo.bootstrap.layout.Manager = function(config)
36812 {
36813     Roo.bootstrap.layout.Manager.superclass.constructor.call(this,config);
36814
36815
36816
36817
36818
36819     /** false to disable window resize monitoring @type Boolean */
36820     this.monitorWindowResize = true;
36821     this.regions = {};
36822     this.addEvents({
36823         /**
36824          * @event layout
36825          * Fires when a layout is performed.
36826          * @param {Roo.LayoutManager} this
36827          */
36828         "layout" : true,
36829         /**
36830          * @event regionresized
36831          * Fires when the user resizes a region.
36832          * @param {Roo.LayoutRegion} region The resized region
36833          * @param {Number} newSize The new size (width for east/west, height for north/south)
36834          */
36835         "regionresized" : true,
36836         /**
36837          * @event regioncollapsed
36838          * Fires when a region is collapsed.
36839          * @param {Roo.LayoutRegion} region The collapsed region
36840          */
36841         "regioncollapsed" : true,
36842         /**
36843          * @event regionexpanded
36844          * Fires when a region is expanded.
36845          * @param {Roo.LayoutRegion} region The expanded region
36846          */
36847         "regionexpanded" : true
36848     });
36849     this.updating = false;
36850
36851     if (config.el) {
36852         this.el = Roo.get(config.el);
36853         this.initEvents();
36854     }
36855
36856 };
36857
36858 Roo.extend(Roo.bootstrap.layout.Manager, Roo.bootstrap.Component, {
36859
36860
36861     regions : null,
36862
36863     monitorWindowResize : true,
36864
36865
36866     updating : false,
36867
36868
36869     onRender : function(ct, position)
36870     {
36871         if(!this.el){
36872             this.el = Roo.get(ct);
36873             this.initEvents();
36874         }
36875         //this.fireEvent('render',this);
36876     },
36877
36878
36879     initEvents: function()
36880     {
36881
36882
36883         // ie scrollbar fix
36884         if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
36885             document.body.scroll = "no";
36886         }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
36887             this.el.position('relative');
36888         }
36889         this.id = this.el.id;
36890         this.el.addClass("roo-layout-container");
36891         Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
36892         if(this.el.dom != document.body ) {
36893             this.el.on('resize', this.layout,this);
36894             this.el.on('show', this.layout,this);
36895         }
36896
36897     },
36898
36899     /**
36900      * Returns true if this layout is currently being updated
36901      * @return {Boolean}
36902      */
36903     isUpdating : function(){
36904         return this.updating;
36905     },
36906
36907     /**
36908      * Suspend the LayoutManager from doing auto-layouts while
36909      * making multiple add or remove calls
36910      */
36911     beginUpdate : function(){
36912         this.updating = true;
36913     },
36914
36915     /**
36916      * Restore auto-layouts and optionally disable the manager from performing a layout
36917      * @param {Boolean} noLayout true to disable a layout update
36918      */
36919     endUpdate : function(noLayout){
36920         this.updating = false;
36921         if(!noLayout){
36922             this.layout();
36923         }
36924     },
36925
36926     layout: function(){
36927         // abstract...
36928     },
36929
36930     onRegionResized : function(region, newSize){
36931         this.fireEvent("regionresized", region, newSize);
36932         this.layout();
36933     },
36934
36935     onRegionCollapsed : function(region){
36936         this.fireEvent("regioncollapsed", region);
36937     },
36938
36939     onRegionExpanded : function(region){
36940         this.fireEvent("regionexpanded", region);
36941     },
36942
36943     /**
36944      * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
36945      * performs box-model adjustments.
36946      * @return {Object} The size as an object {width: (the width), height: (the height)}
36947      */
36948     getViewSize : function()
36949     {
36950         var size;
36951         if(this.el.dom != document.body){
36952             size = this.el.getSize();
36953         }else{
36954             size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
36955         }
36956         size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
36957         size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
36958         return size;
36959     },
36960
36961     /**
36962      * Returns the Element this layout is bound to.
36963      * @return {Roo.Element}
36964      */
36965     getEl : function(){
36966         return this.el;
36967     },
36968
36969     /**
36970      * Returns the specified region.
36971      * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
36972      * @return {Roo.LayoutRegion}
36973      */
36974     getRegion : function(target){
36975         return this.regions[target.toLowerCase()];
36976     },
36977
36978     onWindowResize : function(){
36979         if(this.monitorWindowResize){
36980             this.layout();
36981         }
36982     }
36983 });
36984 /*
36985  * Based on:
36986  * Ext JS Library 1.1.1
36987  * Copyright(c) 2006-2007, Ext JS, LLC.
36988  *
36989  * Originally Released Under LGPL - original licence link has changed is not relivant.
36990  *
36991  * Fork - LGPL
36992  * <script type="text/javascript">
36993  */
36994 /**
36995  * @class Roo.bootstrap.layout.Border
36996  * @extends Roo.bootstrap.layout.Manager
36997  * This class represents a common layout manager used in desktop applications. For screenshots and more details,
36998  * please see: examples/bootstrap/nested.html<br><br>
36999  
37000 <b>The container the layout is rendered into can be either the body element or any other element.
37001 If it is not the body element, the container needs to either be an absolute positioned element,
37002 or you will need to add "position:relative" to the css of the container.  You will also need to specify
37003 the container size if it is not the body element.</b>
37004
37005 * @constructor
37006 * Create a new Border
37007 * @param {Object} config Configuration options
37008  */
37009 Roo.bootstrap.layout.Border = function(config){
37010     config = config || {};
37011     Roo.bootstrap.layout.Border.superclass.constructor.call(this, config);
37012     
37013     
37014     
37015     Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
37016         if(config[region]){
37017             config[region].region = region;
37018             this.addRegion(config[region]);
37019         }
37020     },this);
37021     
37022 };
37023
37024 Roo.bootstrap.layout.Border.regions =  ["north","south","east","west","center"];
37025
37026 Roo.extend(Roo.bootstrap.layout.Border, Roo.bootstrap.layout.Manager, {
37027     
37028     parent : false, // this might point to a 'nest' or a ???
37029     
37030     /**
37031      * Creates and adds a new region if it doesn't already exist.
37032      * @param {String} target The target region key (north, south, east, west or center).
37033      * @param {Object} config The regions config object
37034      * @return {BorderLayoutRegion} The new region
37035      */
37036     addRegion : function(config)
37037     {
37038         if(!this.regions[config.region]){
37039             var r = this.factory(config);
37040             this.bindRegion(r);
37041         }
37042         return this.regions[config.region];
37043     },
37044
37045     // private (kinda)
37046     bindRegion : function(r){
37047         this.regions[r.config.region] = r;
37048         
37049         r.on("visibilitychange",    this.layout, this);
37050         r.on("paneladded",          this.layout, this);
37051         r.on("panelremoved",        this.layout, this);
37052         r.on("invalidated",         this.layout, this);
37053         r.on("resized",             this.onRegionResized, this);
37054         r.on("collapsed",           this.onRegionCollapsed, this);
37055         r.on("expanded",            this.onRegionExpanded, this);
37056     },
37057
37058     /**
37059      * Performs a layout update.
37060      */
37061     layout : function()
37062     {
37063         if(this.updating) {
37064             return;
37065         }
37066         
37067         // render all the rebions if they have not been done alreayd?
37068         Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
37069             if(this.regions[region] && !this.regions[region].bodyEl){
37070                 this.regions[region].onRender(this.el)
37071             }
37072         },this);
37073         
37074         var size = this.getViewSize();
37075         var w = size.width;
37076         var h = size.height;
37077         var centerW = w;
37078         var centerH = h;
37079         var centerY = 0;
37080         var centerX = 0;
37081         //var x = 0, y = 0;
37082
37083         var rs = this.regions;
37084         var north = rs["north"];
37085         var south = rs["south"]; 
37086         var west = rs["west"];
37087         var east = rs["east"];
37088         var center = rs["center"];
37089         //if(this.hideOnLayout){ // not supported anymore
37090             //c.el.setStyle("display", "none");
37091         //}
37092         if(north && north.isVisible()){
37093             var b = north.getBox();
37094             var m = north.getMargins();
37095             b.width = w - (m.left+m.right);
37096             b.x = m.left;
37097             b.y = m.top;
37098             centerY = b.height + b.y + m.bottom;
37099             centerH -= centerY;
37100             north.updateBox(this.safeBox(b));
37101         }
37102         if(south && south.isVisible()){
37103             var b = south.getBox();
37104             var m = south.getMargins();
37105             b.width = w - (m.left+m.right);
37106             b.x = m.left;
37107             var totalHeight = (b.height + m.top + m.bottom);
37108             b.y = h - totalHeight + m.top;
37109             centerH -= totalHeight;
37110             south.updateBox(this.safeBox(b));
37111         }
37112         if(west && west.isVisible()){
37113             var b = west.getBox();
37114             var m = west.getMargins();
37115             b.height = centerH - (m.top+m.bottom);
37116             b.x = m.left;
37117             b.y = centerY + m.top;
37118             var totalWidth = (b.width + m.left + m.right);
37119             centerX += totalWidth;
37120             centerW -= totalWidth;
37121             west.updateBox(this.safeBox(b));
37122         }
37123         if(east && east.isVisible()){
37124             var b = east.getBox();
37125             var m = east.getMargins();
37126             b.height = centerH - (m.top+m.bottom);
37127             var totalWidth = (b.width + m.left + m.right);
37128             b.x = w - totalWidth + m.left;
37129             b.y = centerY + m.top;
37130             centerW -= totalWidth;
37131             east.updateBox(this.safeBox(b));
37132         }
37133         if(center){
37134             var m = center.getMargins();
37135             var centerBox = {
37136                 x: centerX + m.left,
37137                 y: centerY + m.top,
37138                 width: centerW - (m.left+m.right),
37139                 height: centerH - (m.top+m.bottom)
37140             };
37141             //if(this.hideOnLayout){
37142                 //center.el.setStyle("display", "block");
37143             //}
37144             center.updateBox(this.safeBox(centerBox));
37145         }
37146         this.el.repaint();
37147         this.fireEvent("layout", this);
37148     },
37149
37150     // private
37151     safeBox : function(box){
37152         box.width = Math.max(0, box.width);
37153         box.height = Math.max(0, box.height);
37154         return box;
37155     },
37156
37157     /**
37158      * Adds a ContentPanel (or subclass) to this layout.
37159      * @param {String} target The target region key (north, south, east, west or center).
37160      * @param {Roo.ContentPanel} panel The panel to add
37161      * @return {Roo.ContentPanel} The added panel
37162      */
37163     add : function(target, panel){
37164          
37165         target = target.toLowerCase();
37166         return this.regions[target].add(panel);
37167     },
37168
37169     /**
37170      * Remove a ContentPanel (or subclass) to this layout.
37171      * @param {String} target The target region key (north, south, east, west or center).
37172      * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
37173      * @return {Roo.ContentPanel} The removed panel
37174      */
37175     remove : function(target, panel){
37176         target = target.toLowerCase();
37177         return this.regions[target].remove(panel);
37178     },
37179
37180     /**
37181      * Searches all regions for a panel with the specified id
37182      * @param {String} panelId
37183      * @return {Roo.ContentPanel} The panel or null if it wasn't found
37184      */
37185     findPanel : function(panelId){
37186         var rs = this.regions;
37187         for(var target in rs){
37188             if(typeof rs[target] != "function"){
37189                 var p = rs[target].getPanel(panelId);
37190                 if(p){
37191                     return p;
37192                 }
37193             }
37194         }
37195         return null;
37196     },
37197
37198     /**
37199      * Searches all regions for a panel with the specified id and activates (shows) it.
37200      * @param {String/ContentPanel} panelId The panels id or the panel itself
37201      * @return {Roo.ContentPanel} The shown panel or null
37202      */
37203     showPanel : function(panelId) {
37204       var rs = this.regions;
37205       for(var target in rs){
37206          var r = rs[target];
37207          if(typeof r != "function"){
37208             if(r.hasPanel(panelId)){
37209                return r.showPanel(panelId);
37210             }
37211          }
37212       }
37213       return null;
37214    },
37215
37216    /**
37217      * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
37218      * @param {Roo.state.Provider} provider (optional) An alternate state provider
37219      */
37220    /*
37221     restoreState : function(provider){
37222         if(!provider){
37223             provider = Roo.state.Manager;
37224         }
37225         var sm = new Roo.LayoutStateManager();
37226         sm.init(this, provider);
37227     },
37228 */
37229  
37230  
37231     /**
37232      * Adds a xtype elements to the layout.
37233      * <pre><code>
37234
37235 layout.addxtype({
37236        xtype : 'ContentPanel',
37237        region: 'west',
37238        items: [ .... ]
37239    }
37240 );
37241
37242 layout.addxtype({
37243         xtype : 'NestedLayoutPanel',
37244         region: 'west',
37245         layout: {
37246            center: { },
37247            west: { }   
37248         },
37249         items : [ ... list of content panels or nested layout panels.. ]
37250    }
37251 );
37252 </code></pre>
37253      * @param {Object} cfg Xtype definition of item to add.
37254      */
37255     addxtype : function(cfg)
37256     {
37257         // basically accepts a pannel...
37258         // can accept a layout region..!?!?
37259         //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
37260         
37261         
37262         // theory?  children can only be panels??
37263         
37264         //if (!cfg.xtype.match(/Panel$/)) {
37265         //    return false;
37266         //}
37267         var ret = false;
37268         
37269         if (typeof(cfg.region) == 'undefined') {
37270             Roo.log("Failed to add Panel, region was not set");
37271             Roo.log(cfg);
37272             return false;
37273         }
37274         var region = cfg.region;
37275         delete cfg.region;
37276         
37277           
37278         var xitems = [];
37279         if (cfg.items) {
37280             xitems = cfg.items;
37281             delete cfg.items;
37282         }
37283         var nb = false;
37284         
37285         if ( region == 'center') {
37286             Roo.log("Center: " + cfg.title);
37287         }
37288         
37289         
37290         switch(cfg.xtype) 
37291         {
37292             case 'Content':  // ContentPanel (el, cfg)
37293             case 'Scroll':  // ContentPanel (el, cfg)
37294             case 'View': 
37295                 cfg.autoCreate = cfg.autoCreate || true;
37296                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
37297                 //} else {
37298                 //    var el = this.el.createChild();
37299                 //    ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
37300                 //}
37301                 
37302                 this.add(region, ret);
37303                 break;
37304             
37305             /*
37306             case 'TreePanel': // our new panel!
37307                 cfg.el = this.el.createChild();
37308                 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
37309                 this.add(region, ret);
37310                 break;
37311             */
37312             
37313             case 'Nest': 
37314                 // create a new Layout (which is  a Border Layout...
37315                 
37316                 var clayout = cfg.layout;
37317                 clayout.el  = this.el.createChild();
37318                 clayout.items   = clayout.items  || [];
37319                 
37320                 delete cfg.layout;
37321                 
37322                 // replace this exitems with the clayout ones..
37323                 xitems = clayout.items;
37324                  
37325                 // force background off if it's in center...
37326                 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
37327                     cfg.background = false;
37328                 }
37329                 cfg.layout  = new Roo.bootstrap.layout.Border(clayout);
37330                 
37331                 
37332                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
37333                 //console.log('adding nested layout panel '  + cfg.toSource());
37334                 this.add(region, ret);
37335                 nb = {}; /// find first...
37336                 break;
37337             
37338             case 'Grid':
37339                 
37340                 // needs grid and region
37341                 
37342                 //var el = this.getRegion(region).el.createChild();
37343                 /*
37344                  *var el = this.el.createChild();
37345                 // create the grid first...
37346                 cfg.grid.container = el;
37347                 cfg.grid = new cfg.grid.xns[cfg.grid.xtype](cfg.grid);
37348                 */
37349                 
37350                 if (region == 'center' && this.active ) {
37351                     cfg.background = false;
37352                 }
37353                 
37354                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
37355                 
37356                 this.add(region, ret);
37357                 /*
37358                 if (cfg.background) {
37359                     // render grid on panel activation (if panel background)
37360                     ret.on('activate', function(gp) {
37361                         if (!gp.grid.rendered) {
37362                     //        gp.grid.render(el);
37363                         }
37364                     });
37365                 } else {
37366                   //  cfg.grid.render(el);
37367                 }
37368                 */
37369                 break;
37370            
37371            
37372             case 'Border': // it can get called on it'self... - might need to check if this is fixed?
37373                 // it was the old xcomponent building that caused this before.
37374                 // espeically if border is the top element in the tree.
37375                 ret = this;
37376                 break; 
37377                 
37378                     
37379                 
37380                 
37381                 
37382             default:
37383                 /*
37384                 if (typeof(Roo[cfg.xtype]) != 'undefined') {
37385                     
37386                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
37387                     this.add(region, ret);
37388                 } else {
37389                 */
37390                     Roo.log(cfg);
37391                     throw "Can not add '" + cfg.xtype + "' to Border";
37392                     return null;
37393              
37394                                 
37395              
37396         }
37397         this.beginUpdate();
37398         // add children..
37399         var region = '';
37400         var abn = {};
37401         Roo.each(xitems, function(i)  {
37402             region = nb && i.region ? i.region : false;
37403             
37404             var add = ret.addxtype(i);
37405            
37406             if (region) {
37407                 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
37408                 if (!i.background) {
37409                     abn[region] = nb[region] ;
37410                 }
37411             }
37412             
37413         });
37414         this.endUpdate();
37415
37416         // make the last non-background panel active..
37417         //if (nb) { Roo.log(abn); }
37418         if (nb) {
37419             
37420             for(var r in abn) {
37421                 region = this.getRegion(r);
37422                 if (region) {
37423                     // tried using nb[r], but it does not work..
37424                      
37425                     region.showPanel(abn[r]);
37426                    
37427                 }
37428             }
37429         }
37430         return ret;
37431         
37432     },
37433     
37434     
37435 // private
37436     factory : function(cfg)
37437     {
37438         
37439         var validRegions = Roo.bootstrap.layout.Border.regions;
37440
37441         var target = cfg.region;
37442         cfg.mgr = this;
37443         
37444         var r = Roo.bootstrap.layout;
37445         Roo.log(target);
37446         switch(target){
37447             case "north":
37448                 return new r.North(cfg);
37449             case "south":
37450                 return new r.South(cfg);
37451             case "east":
37452                 return new r.East(cfg);
37453             case "west":
37454                 return new r.West(cfg);
37455             case "center":
37456                 return new r.Center(cfg);
37457         }
37458         throw 'Layout region "'+target+'" not supported.';
37459     }
37460     
37461     
37462 });
37463  /*
37464  * Based on:
37465  * Ext JS Library 1.1.1
37466  * Copyright(c) 2006-2007, Ext JS, LLC.
37467  *
37468  * Originally Released Under LGPL - original licence link has changed is not relivant.
37469  *
37470  * Fork - LGPL
37471  * <script type="text/javascript">
37472  */
37473  
37474 /**
37475  * @class Roo.bootstrap.layout.Basic
37476  * @extends Roo.util.Observable
37477  * This class represents a lightweight region in a layout manager. This region does not move dom nodes
37478  * and does not have a titlebar, tabs or any other features. All it does is size and position 
37479  * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
37480  * @cfg {Roo.bootstrap.layout.Manager}   mgr The manager
37481  * @cfg {string}   region  the region that it inhabits..
37482  * @cfg {bool}   skipConfig skip config?
37483  * 
37484
37485  */
37486 Roo.bootstrap.layout.Basic = function(config){
37487     
37488     this.mgr = config.mgr;
37489     
37490     this.position = config.region;
37491     
37492     var skipConfig = config.skipConfig;
37493     
37494     this.events = {
37495         /**
37496          * @scope Roo.BasicLayoutRegion
37497          */
37498         
37499         /**
37500          * @event beforeremove
37501          * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
37502          * @param {Roo.LayoutRegion} this
37503          * @param {Roo.ContentPanel} panel The panel
37504          * @param {Object} e The cancel event object
37505          */
37506         "beforeremove" : true,
37507         /**
37508          * @event invalidated
37509          * Fires when the layout for this region is changed.
37510          * @param {Roo.LayoutRegion} this
37511          */
37512         "invalidated" : true,
37513         /**
37514          * @event visibilitychange
37515          * Fires when this region is shown or hidden 
37516          * @param {Roo.LayoutRegion} this
37517          * @param {Boolean} visibility true or false
37518          */
37519         "visibilitychange" : true,
37520         /**
37521          * @event paneladded
37522          * Fires when a panel is added. 
37523          * @param {Roo.LayoutRegion} this
37524          * @param {Roo.ContentPanel} panel The panel
37525          */
37526         "paneladded" : true,
37527         /**
37528          * @event panelremoved
37529          * Fires when a panel is removed. 
37530          * @param {Roo.LayoutRegion} this
37531          * @param {Roo.ContentPanel} panel The panel
37532          */
37533         "panelremoved" : true,
37534         /**
37535          * @event beforecollapse
37536          * Fires when this region before collapse.
37537          * @param {Roo.LayoutRegion} this
37538          */
37539         "beforecollapse" : true,
37540         /**
37541          * @event collapsed
37542          * Fires when this region is collapsed.
37543          * @param {Roo.LayoutRegion} this
37544          */
37545         "collapsed" : true,
37546         /**
37547          * @event expanded
37548          * Fires when this region is expanded.
37549          * @param {Roo.LayoutRegion} this
37550          */
37551         "expanded" : true,
37552         /**
37553          * @event slideshow
37554          * Fires when this region is slid into view.
37555          * @param {Roo.LayoutRegion} this
37556          */
37557         "slideshow" : true,
37558         /**
37559          * @event slidehide
37560          * Fires when this region slides out of view. 
37561          * @param {Roo.LayoutRegion} this
37562          */
37563         "slidehide" : true,
37564         /**
37565          * @event panelactivated
37566          * Fires when a panel is activated. 
37567          * @param {Roo.LayoutRegion} this
37568          * @param {Roo.ContentPanel} panel The activated panel
37569          */
37570         "panelactivated" : true,
37571         /**
37572          * @event resized
37573          * Fires when the user resizes this region. 
37574          * @param {Roo.LayoutRegion} this
37575          * @param {Number} newSize The new size (width for east/west, height for north/south)
37576          */
37577         "resized" : true
37578     };
37579     /** A collection of panels in this region. @type Roo.util.MixedCollection */
37580     this.panels = new Roo.util.MixedCollection();
37581     this.panels.getKey = this.getPanelId.createDelegate(this);
37582     this.box = null;
37583     this.activePanel = null;
37584     // ensure listeners are added...
37585     
37586     if (config.listeners || config.events) {
37587         Roo.bootstrap.layout.Basic.superclass.constructor.call(this, {
37588             listeners : config.listeners || {},
37589             events : config.events || {}
37590         });
37591     }
37592     
37593     if(skipConfig !== true){
37594         this.applyConfig(config);
37595     }
37596 };
37597
37598 Roo.extend(Roo.bootstrap.layout.Basic, Roo.util.Observable,
37599 {
37600     getPanelId : function(p){
37601         return p.getId();
37602     },
37603     
37604     applyConfig : function(config){
37605         this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
37606         this.config = config;
37607         
37608     },
37609     
37610     /**
37611      * Resizes the region to the specified size. For vertical regions (west, east) this adjusts 
37612      * the width, for horizontal (north, south) the height.
37613      * @param {Number} newSize The new width or height
37614      */
37615     resizeTo : function(newSize){
37616         var el = this.el ? this.el :
37617                  (this.activePanel ? this.activePanel.getEl() : null);
37618         if(el){
37619             switch(this.position){
37620                 case "east":
37621                 case "west":
37622                     el.setWidth(newSize);
37623                     this.fireEvent("resized", this, newSize);
37624                 break;
37625                 case "north":
37626                 case "south":
37627                     el.setHeight(newSize);
37628                     this.fireEvent("resized", this, newSize);
37629                 break;                
37630             }
37631         }
37632     },
37633     
37634     getBox : function(){
37635         return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
37636     },
37637     
37638     getMargins : function(){
37639         return this.margins;
37640     },
37641     
37642     updateBox : function(box){
37643         this.box = box;
37644         var el = this.activePanel.getEl();
37645         el.dom.style.left = box.x + "px";
37646         el.dom.style.top = box.y + "px";
37647         this.activePanel.setSize(box.width, box.height);
37648     },
37649     
37650     /**
37651      * Returns the container element for this region.
37652      * @return {Roo.Element}
37653      */
37654     getEl : function(){
37655         return this.activePanel;
37656     },
37657     
37658     /**
37659      * Returns true if this region is currently visible.
37660      * @return {Boolean}
37661      */
37662     isVisible : function(){
37663         return this.activePanel ? true : false;
37664     },
37665     
37666     setActivePanel : function(panel){
37667         panel = this.getPanel(panel);
37668         if(this.activePanel && this.activePanel != panel){
37669             this.activePanel.setActiveState(false);
37670             this.activePanel.getEl().setLeftTop(-10000,-10000);
37671         }
37672         this.activePanel = panel;
37673         panel.setActiveState(true);
37674         if(this.box){
37675             panel.setSize(this.box.width, this.box.height);
37676         }
37677         this.fireEvent("panelactivated", this, panel);
37678         this.fireEvent("invalidated");
37679     },
37680     
37681     /**
37682      * Show the specified panel.
37683      * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
37684      * @return {Roo.ContentPanel} The shown panel or null
37685      */
37686     showPanel : function(panel){
37687         panel = this.getPanel(panel);
37688         if(panel){
37689             this.setActivePanel(panel);
37690         }
37691         return panel;
37692     },
37693     
37694     /**
37695      * Get the active panel for this region.
37696      * @return {Roo.ContentPanel} The active panel or null
37697      */
37698     getActivePanel : function(){
37699         return this.activePanel;
37700     },
37701     
37702     /**
37703      * Add the passed ContentPanel(s)
37704      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
37705      * @return {Roo.ContentPanel} The panel added (if only one was added)
37706      */
37707     add : function(panel){
37708         if(arguments.length > 1){
37709             for(var i = 0, len = arguments.length; i < len; i++) {
37710                 this.add(arguments[i]);
37711             }
37712             return null;
37713         }
37714         if(this.hasPanel(panel)){
37715             this.showPanel(panel);
37716             return panel;
37717         }
37718         var el = panel.getEl();
37719         if(el.dom.parentNode != this.mgr.el.dom){
37720             this.mgr.el.dom.appendChild(el.dom);
37721         }
37722         if(panel.setRegion){
37723             panel.setRegion(this);
37724         }
37725         this.panels.add(panel);
37726         el.setStyle("position", "absolute");
37727         if(!panel.background){
37728             this.setActivePanel(panel);
37729             if(this.config.initialSize && this.panels.getCount()==1){
37730                 this.resizeTo(this.config.initialSize);
37731             }
37732         }
37733         this.fireEvent("paneladded", this, panel);
37734         return panel;
37735     },
37736     
37737     /**
37738      * Returns true if the panel is in this region.
37739      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
37740      * @return {Boolean}
37741      */
37742     hasPanel : function(panel){
37743         if(typeof panel == "object"){ // must be panel obj
37744             panel = panel.getId();
37745         }
37746         return this.getPanel(panel) ? true : false;
37747     },
37748     
37749     /**
37750      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
37751      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
37752      * @param {Boolean} preservePanel Overrides the config preservePanel option
37753      * @return {Roo.ContentPanel} The panel that was removed
37754      */
37755     remove : function(panel, preservePanel){
37756         panel = this.getPanel(panel);
37757         if(!panel){
37758             return null;
37759         }
37760         var e = {};
37761         this.fireEvent("beforeremove", this, panel, e);
37762         if(e.cancel === true){
37763             return null;
37764         }
37765         var panelId = panel.getId();
37766         this.panels.removeKey(panelId);
37767         return panel;
37768     },
37769     
37770     /**
37771      * Returns the panel specified or null if it's not in this region.
37772      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
37773      * @return {Roo.ContentPanel}
37774      */
37775     getPanel : function(id){
37776         if(typeof id == "object"){ // must be panel obj
37777             return id;
37778         }
37779         return this.panels.get(id);
37780     },
37781     
37782     /**
37783      * Returns this regions position (north/south/east/west/center).
37784      * @return {String} 
37785      */
37786     getPosition: function(){
37787         return this.position;    
37788     }
37789 });/*
37790  * Based on:
37791  * Ext JS Library 1.1.1
37792  * Copyright(c) 2006-2007, Ext JS, LLC.
37793  *
37794  * Originally Released Under LGPL - original licence link has changed is not relivant.
37795  *
37796  * Fork - LGPL
37797  * <script type="text/javascript">
37798  */
37799  
37800 /**
37801  * @class Roo.bootstrap.layout.Region
37802  * @extends Roo.bootstrap.layout.Basic
37803  * This class represents a region in a layout manager.
37804  
37805  * @cfg {Object}    margins         Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
37806  * @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})
37807  * @cfg {String}    tabPosition     (top|bottom) "top" or "bottom" (defaults to "bottom")
37808  * @cfg {Boolean}   alwaysShowTabs  True to always display tabs even when there is only 1 panel (defaults to false)
37809  * @cfg {Boolean}   autoScroll      True to enable overflow scrolling (defaults to false)
37810  * @cfg {Boolean}   titlebar        True to display a title bar (defaults to true)
37811  * @cfg {String}    title           The title for the region (overrides panel titles)
37812  * @cfg {Boolean}   animate         True to animate expand/collapse (defaults to false)
37813  * @cfg {Boolean}   autoHide        False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
37814  * @cfg {Boolean}   preservePanels  True to preserve removed panels so they can be readded later (defaults to false)
37815  * @cfg {Boolean}   closeOnTab      True to place the close icon on the tabs instead of the region titlebar (defaults to false)
37816  * @cfg {Boolean}   hideTabs        True to hide the tab strip (defaults to false)
37817  * @cfg {Boolean}   resizeTabs      True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
37818  *                      the space available, similar to FireFox 1.5 tabs (defaults to false)
37819  * @cfg {Number}    minTabWidth     The minimum tab width (defaults to 40)
37820  * @cfg {Number}    preferredTabWidth The preferred tab width (defaults to 150)
37821  * @cfg {String}    overflow       (hidden|visible) if you have menus in the region, then you need to set this to visible.
37822
37823  * @cfg {Boolean}   hidden          True to start the region hidden (defaults to false)
37824  * @cfg {Boolean}   hideWhenEmpty   True to hide the region when it has no panels
37825  * @cfg {Boolean}   disableTabTips  True to disable tab tooltips
37826  * @cfg {Number}    width           For East/West panels
37827  * @cfg {Number}    height          For North/South panels
37828  * @cfg {Boolean}   split           To show the splitter
37829  * @cfg {Boolean}   toolbar         xtype configuration for a toolbar - shows on right of tabbar
37830  * 
37831  * @cfg {string}   cls             Extra CSS classes to add to region
37832  * 
37833  * @cfg {Roo.bootstrap.layout.Manager}   mgr The manager
37834  * @cfg {string}   region  the region that it inhabits..
37835  *
37836
37837  * @xxxcfg {Boolean}   collapsible     DISABLED False to disable collapsing (defaults to true)
37838  * @xxxcfg {Boolean}   collapsed       DISABLED True to set the initial display to collapsed (defaults to false)
37839
37840  * @xxxcfg {String}    collapsedTitle  DISABLED Optional string message to display in the collapsed block of a north or south region
37841  * @xxxxcfg {Boolean}   floatable       DISABLED False to disable floating (defaults to true)
37842  * @xxxxcfg {Boolean}   showPin         True to show a pin button NOT SUPPORTED YET
37843  */
37844 Roo.bootstrap.layout.Region = function(config)
37845 {
37846     this.applyConfig(config);
37847
37848     var mgr = config.mgr;
37849     var pos = config.region;
37850     config.skipConfig = true;
37851     Roo.bootstrap.layout.Region.superclass.constructor.call(this, config);
37852     
37853     if (mgr.el) {
37854         this.onRender(mgr.el);   
37855     }
37856      
37857     this.visible = true;
37858     this.collapsed = false;
37859     this.unrendered_panels = [];
37860 };
37861
37862 Roo.extend(Roo.bootstrap.layout.Region, Roo.bootstrap.layout.Basic, {
37863
37864     position: '', // set by wrapper (eg. north/south etc..)
37865     unrendered_panels : null,  // unrendered panels.
37866     
37867     tabPosition : false,
37868     
37869     mgr: false, // points to 'Border'
37870     
37871     
37872     createBody : function(){
37873         /** This region's body element 
37874         * @type Roo.Element */
37875         this.bodyEl = this.el.createChild({
37876                 tag: "div",
37877                 cls: "roo-layout-panel-body tab-content" // bootstrap added...
37878         });
37879     },
37880
37881     onRender: function(ctr, pos)
37882     {
37883         var dh = Roo.DomHelper;
37884         /** This region's container element 
37885         * @type Roo.Element */
37886         this.el = dh.append(ctr.dom, {
37887                 tag: "div",
37888                 cls: (this.config.cls || '') + " roo-layout-region roo-layout-panel roo-layout-panel-" + this.position
37889             }, true);
37890         /** This region's title element 
37891         * @type Roo.Element */
37892     
37893         this.titleEl = dh.append(this.el.dom,  {
37894                 tag: "div",
37895                 unselectable: "on",
37896                 cls: "roo-unselectable roo-layout-panel-hd breadcrumb roo-layout-title-" + this.position,
37897                 children:[
37898                     {tag: "span", cls: "roo-unselectable roo-layout-panel-hd-text", unselectable: "on", html: "&#160;"},
37899                     {tag: "div", cls: "roo-unselectable roo-layout-panel-hd-tools", unselectable: "on"}
37900                 ]
37901             }, true);
37902         
37903         this.titleEl.enableDisplayMode();
37904         /** This region's title text element 
37905         * @type HTMLElement */
37906         this.titleTextEl = this.titleEl.dom.firstChild;
37907         this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
37908         /*
37909         this.closeBtn = this.createTool(this.tools.dom, "roo-layout-close");
37910         this.closeBtn.enableDisplayMode();
37911         this.closeBtn.on("click", this.closeClicked, this);
37912         this.closeBtn.hide();
37913     */
37914         this.createBody(this.config);
37915         if(this.config.hideWhenEmpty){
37916             this.hide();
37917             this.on("paneladded", this.validateVisibility, this);
37918             this.on("panelremoved", this.validateVisibility, this);
37919         }
37920         if(this.autoScroll){
37921             this.bodyEl.setStyle("overflow", "auto");
37922         }else{
37923             this.bodyEl.setStyle("overflow", this.config.overflow || 'hidden');
37924         }
37925         //if(c.titlebar !== false){
37926             if((!this.config.titlebar && !this.config.title) || this.config.titlebar === false){
37927                 this.titleEl.hide();
37928             }else{
37929                 this.titleEl.show();
37930                 if(this.config.title){
37931                     this.titleTextEl.innerHTML = this.config.title;
37932                 }
37933             }
37934         //}
37935         if(this.config.collapsed){
37936             this.collapse(true);
37937         }
37938         if(this.config.hidden){
37939             this.hide();
37940         }
37941         
37942         if (this.unrendered_panels && this.unrendered_panels.length) {
37943             for (var i =0;i< this.unrendered_panels.length; i++) {
37944                 this.add(this.unrendered_panels[i]);
37945             }
37946             this.unrendered_panels = null;
37947             
37948         }
37949         
37950     },
37951     
37952     applyConfig : function(c)
37953     {
37954         /*
37955          *if(c.collapsible && this.position != "center" && !this.collapsedEl){
37956             var dh = Roo.DomHelper;
37957             if(c.titlebar !== false){
37958                 this.collapseBtn = this.createTool(this.tools.dom, "roo-layout-collapse-"+this.position);
37959                 this.collapseBtn.on("click", this.collapse, this);
37960                 this.collapseBtn.enableDisplayMode();
37961                 /*
37962                 if(c.showPin === true || this.showPin){
37963                     this.stickBtn = this.createTool(this.tools.dom, "roo-layout-stick");
37964                     this.stickBtn.enableDisplayMode();
37965                     this.stickBtn.on("click", this.expand, this);
37966                     this.stickBtn.hide();
37967                 }
37968                 
37969             }
37970             */
37971             /** This region's collapsed element
37972             * @type Roo.Element */
37973             /*
37974              *
37975             this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
37976                 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
37977             ]}, true);
37978             
37979             if(c.floatable !== false){
37980                this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
37981                this.collapsedEl.on("click", this.collapseClick, this);
37982             }
37983
37984             if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
37985                 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
37986                    id: "message", unselectable: "on", style:{"float":"left"}});
37987                this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
37988              }
37989             this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
37990             this.expandBtn.on("click", this.expand, this);
37991             
37992         }
37993         
37994         if(this.collapseBtn){
37995             this.collapseBtn.setVisible(c.collapsible == true);
37996         }
37997         
37998         this.cmargins = c.cmargins || this.cmargins ||
37999                          (this.position == "west" || this.position == "east" ?
38000                              {top: 0, left: 2, right:2, bottom: 0} :
38001                              {top: 2, left: 0, right:0, bottom: 2});
38002         */
38003         this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
38004         
38005         
38006         this.tabPosition = [ 'top','bottom', 'west'].indexOf(c.tabPosition) > -1 ? c.tabPosition : "top";
38007         
38008         this.autoScroll = c.autoScroll || false;
38009         
38010         
38011        
38012         
38013         this.duration = c.duration || .30;
38014         this.slideDuration = c.slideDuration || .45;
38015         this.config = c;
38016        
38017     },
38018     /**
38019      * Returns true if this region is currently visible.
38020      * @return {Boolean}
38021      */
38022     isVisible : function(){
38023         return this.visible;
38024     },
38025
38026     /**
38027      * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
38028      * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&amp;#160;")
38029      */
38030     //setCollapsedTitle : function(title){
38031     //    title = title || "&#160;";
38032      //   if(this.collapsedTitleTextEl){
38033       //      this.collapsedTitleTextEl.innerHTML = title;
38034        // }
38035     //},
38036
38037     getBox : function(){
38038         var b;
38039       //  if(!this.collapsed){
38040             b = this.el.getBox(false, true);
38041        // }else{
38042           //  b = this.collapsedEl.getBox(false, true);
38043         //}
38044         return b;
38045     },
38046
38047     getMargins : function(){
38048         return this.margins;
38049         //return this.collapsed ? this.cmargins : this.margins;
38050     },
38051 /*
38052     highlight : function(){
38053         this.el.addClass("x-layout-panel-dragover");
38054     },
38055
38056     unhighlight : function(){
38057         this.el.removeClass("x-layout-panel-dragover");
38058     },
38059 */
38060     updateBox : function(box)
38061     {
38062         if (!this.bodyEl) {
38063             return; // not rendered yet..
38064         }
38065         
38066         this.box = box;
38067         if(!this.collapsed){
38068             this.el.dom.style.left = box.x + "px";
38069             this.el.dom.style.top = box.y + "px";
38070             this.updateBody(box.width, box.height);
38071         }else{
38072             this.collapsedEl.dom.style.left = box.x + "px";
38073             this.collapsedEl.dom.style.top = box.y + "px";
38074             this.collapsedEl.setSize(box.width, box.height);
38075         }
38076         if(this.tabs){
38077             this.tabs.autoSizeTabs();
38078         }
38079     },
38080
38081     updateBody : function(w, h)
38082     {
38083         if(w !== null){
38084             this.el.setWidth(w);
38085             w -= this.el.getBorderWidth("rl");
38086             if(this.config.adjustments){
38087                 w += this.config.adjustments[0];
38088             }
38089         }
38090         if(h !== null && h > 0){
38091             this.el.setHeight(h);
38092             h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
38093             h -= this.el.getBorderWidth("tb");
38094             if(this.config.adjustments){
38095                 h += this.config.adjustments[1];
38096             }
38097             this.bodyEl.setHeight(h);
38098             if(this.tabs){
38099                 h = this.tabs.syncHeight(h);
38100             }
38101         }
38102         if(this.panelSize){
38103             w = w !== null ? w : this.panelSize.width;
38104             h = h !== null ? h : this.panelSize.height;
38105         }
38106         if(this.activePanel){
38107             var el = this.activePanel.getEl();
38108             w = w !== null ? w : el.getWidth();
38109             h = h !== null ? h : el.getHeight();
38110             this.panelSize = {width: w, height: h};
38111             this.activePanel.setSize(w, h);
38112         }
38113         if(Roo.isIE && this.tabs){
38114             this.tabs.el.repaint();
38115         }
38116     },
38117
38118     /**
38119      * Returns the container element for this region.
38120      * @return {Roo.Element}
38121      */
38122     getEl : function(){
38123         return this.el;
38124     },
38125
38126     /**
38127      * Hides this region.
38128      */
38129     hide : function(){
38130         //if(!this.collapsed){
38131             this.el.dom.style.left = "-2000px";
38132             this.el.hide();
38133         //}else{
38134          //   this.collapsedEl.dom.style.left = "-2000px";
38135          //   this.collapsedEl.hide();
38136        // }
38137         this.visible = false;
38138         this.fireEvent("visibilitychange", this, false);
38139     },
38140
38141     /**
38142      * Shows this region if it was previously hidden.
38143      */
38144     show : function(){
38145         //if(!this.collapsed){
38146             this.el.show();
38147         //}else{
38148         //    this.collapsedEl.show();
38149        // }
38150         this.visible = true;
38151         this.fireEvent("visibilitychange", this, true);
38152     },
38153 /*
38154     closeClicked : function(){
38155         if(this.activePanel){
38156             this.remove(this.activePanel);
38157         }
38158     },
38159
38160     collapseClick : function(e){
38161         if(this.isSlid){
38162            e.stopPropagation();
38163            this.slideIn();
38164         }else{
38165            e.stopPropagation();
38166            this.slideOut();
38167         }
38168     },
38169 */
38170     /**
38171      * Collapses this region.
38172      * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
38173      */
38174     /*
38175     collapse : function(skipAnim, skipCheck = false){
38176         if(this.collapsed) {
38177             return;
38178         }
38179         
38180         if(skipCheck || this.fireEvent("beforecollapse", this) != false){
38181             
38182             this.collapsed = true;
38183             if(this.split){
38184                 this.split.el.hide();
38185             }
38186             if(this.config.animate && skipAnim !== true){
38187                 this.fireEvent("invalidated", this);
38188                 this.animateCollapse();
38189             }else{
38190                 this.el.setLocation(-20000,-20000);
38191                 this.el.hide();
38192                 this.collapsedEl.show();
38193                 this.fireEvent("collapsed", this);
38194                 this.fireEvent("invalidated", this);
38195             }
38196         }
38197         
38198     },
38199 */
38200     animateCollapse : function(){
38201         // overridden
38202     },
38203
38204     /**
38205      * Expands this region if it was previously collapsed.
38206      * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
38207      * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
38208      */
38209     /*
38210     expand : function(e, skipAnim){
38211         if(e) {
38212             e.stopPropagation();
38213         }
38214         if(!this.collapsed || this.el.hasActiveFx()) {
38215             return;
38216         }
38217         if(this.isSlid){
38218             this.afterSlideIn();
38219             skipAnim = true;
38220         }
38221         this.collapsed = false;
38222         if(this.config.animate && skipAnim !== true){
38223             this.animateExpand();
38224         }else{
38225             this.el.show();
38226             if(this.split){
38227                 this.split.el.show();
38228             }
38229             this.collapsedEl.setLocation(-2000,-2000);
38230             this.collapsedEl.hide();
38231             this.fireEvent("invalidated", this);
38232             this.fireEvent("expanded", this);
38233         }
38234     },
38235 */
38236     animateExpand : function(){
38237         // overridden
38238     },
38239
38240     initTabs : function()
38241     {
38242         //this.bodyEl.setStyle("overflow", "hidden"); -- this is set in render?
38243         
38244         var ts = new Roo.bootstrap.panel.Tabs({
38245             el: this.bodyEl.dom,
38246             region : this,
38247             tabPosition: this.tabPosition ? this.tabPosition  : 'top',
38248             disableTooltips: this.config.disableTabTips,
38249             toolbar : this.config.toolbar
38250         });
38251         
38252         if(this.config.hideTabs){
38253             ts.stripWrap.setDisplayed(false);
38254         }
38255         this.tabs = ts;
38256         ts.resizeTabs = this.config.resizeTabs === true;
38257         ts.minTabWidth = this.config.minTabWidth || 40;
38258         ts.maxTabWidth = this.config.maxTabWidth || 250;
38259         ts.preferredTabWidth = this.config.preferredTabWidth || 150;
38260         ts.monitorResize = false;
38261         //ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden"); // this is set in render?
38262         ts.bodyEl.addClass('roo-layout-tabs-body');
38263         this.panels.each(this.initPanelAsTab, this);
38264     },
38265
38266     initPanelAsTab : function(panel){
38267         var ti = this.tabs.addTab(
38268             panel.getEl().id,
38269             panel.getTitle(),
38270             null,
38271             this.config.closeOnTab && panel.isClosable(),
38272             panel.tpl
38273         );
38274         if(panel.tabTip !== undefined){
38275             ti.setTooltip(panel.tabTip);
38276         }
38277         ti.on("activate", function(){
38278               this.setActivePanel(panel);
38279         }, this);
38280         
38281         if(this.config.closeOnTab){
38282             ti.on("beforeclose", function(t, e){
38283                 e.cancel = true;
38284                 this.remove(panel);
38285             }, this);
38286         }
38287         
38288         panel.tabItem = ti;
38289         
38290         return ti;
38291     },
38292
38293     updatePanelTitle : function(panel, title)
38294     {
38295         if(this.activePanel == panel){
38296             this.updateTitle(title);
38297         }
38298         if(this.tabs){
38299             var ti = this.tabs.getTab(panel.getEl().id);
38300             ti.setText(title);
38301             if(panel.tabTip !== undefined){
38302                 ti.setTooltip(panel.tabTip);
38303             }
38304         }
38305     },
38306
38307     updateTitle : function(title){
38308         if(this.titleTextEl && !this.config.title){
38309             this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : "&#160;");
38310         }
38311     },
38312
38313     setActivePanel : function(panel)
38314     {
38315         panel = this.getPanel(panel);
38316         if(this.activePanel && this.activePanel != panel){
38317             if(this.activePanel.setActiveState(false) === false){
38318                 return;
38319             }
38320         }
38321         this.activePanel = panel;
38322         panel.setActiveState(true);
38323         if(this.panelSize){
38324             panel.setSize(this.panelSize.width, this.panelSize.height);
38325         }
38326         if(this.closeBtn){
38327             this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
38328         }
38329         this.updateTitle(panel.getTitle());
38330         if(this.tabs){
38331             this.fireEvent("invalidated", this);
38332         }
38333         this.fireEvent("panelactivated", this, panel);
38334     },
38335
38336     /**
38337      * Shows the specified panel.
38338      * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
38339      * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
38340      */
38341     showPanel : function(panel)
38342     {
38343         panel = this.getPanel(panel);
38344         if(panel){
38345             if(this.tabs){
38346                 var tab = this.tabs.getTab(panel.getEl().id);
38347                 if(tab.isHidden()){
38348                     this.tabs.unhideTab(tab.id);
38349                 }
38350                 tab.activate();
38351             }else{
38352                 this.setActivePanel(panel);
38353             }
38354         }
38355         return panel;
38356     },
38357
38358     /**
38359      * Get the active panel for this region.
38360      * @return {Roo.ContentPanel} The active panel or null
38361      */
38362     getActivePanel : function(){
38363         return this.activePanel;
38364     },
38365
38366     validateVisibility : function(){
38367         if(this.panels.getCount() < 1){
38368             this.updateTitle("&#160;");
38369             this.closeBtn.hide();
38370             this.hide();
38371         }else{
38372             if(!this.isVisible()){
38373                 this.show();
38374             }
38375         }
38376     },
38377
38378     /**
38379      * Adds the passed ContentPanel(s) to this region.
38380      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
38381      * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
38382      */
38383     add : function(panel)
38384     {
38385         if(arguments.length > 1){
38386             for(var i = 0, len = arguments.length; i < len; i++) {
38387                 this.add(arguments[i]);
38388             }
38389             return null;
38390         }
38391         
38392         // if we have not been rendered yet, then we can not really do much of this..
38393         if (!this.bodyEl) {
38394             this.unrendered_panels.push(panel);
38395             return panel;
38396         }
38397         
38398         
38399         
38400         
38401         if(this.hasPanel(panel)){
38402             this.showPanel(panel);
38403             return panel;
38404         }
38405         panel.setRegion(this);
38406         this.panels.add(panel);
38407        /* if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
38408             // sinle panel - no tab...?? would it not be better to render it with the tabs,
38409             // and hide them... ???
38410             this.bodyEl.dom.appendChild(panel.getEl().dom);
38411             if(panel.background !== true){
38412                 this.setActivePanel(panel);
38413             }
38414             this.fireEvent("paneladded", this, panel);
38415             return panel;
38416         }
38417         */
38418         if(!this.tabs){
38419             this.initTabs();
38420         }else{
38421             this.initPanelAsTab(panel);
38422         }
38423         
38424         
38425         if(panel.background !== true){
38426             this.tabs.activate(panel.getEl().id);
38427         }
38428         this.fireEvent("paneladded", this, panel);
38429         return panel;
38430     },
38431
38432     /**
38433      * Hides the tab for the specified panel.
38434      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
38435      */
38436     hidePanel : function(panel){
38437         if(this.tabs && (panel = this.getPanel(panel))){
38438             this.tabs.hideTab(panel.getEl().id);
38439         }
38440     },
38441
38442     /**
38443      * Unhides the tab for a previously hidden panel.
38444      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
38445      */
38446     unhidePanel : function(panel){
38447         if(this.tabs && (panel = this.getPanel(panel))){
38448             this.tabs.unhideTab(panel.getEl().id);
38449         }
38450     },
38451
38452     clearPanels : function(){
38453         while(this.panels.getCount() > 0){
38454              this.remove(this.panels.first());
38455         }
38456     },
38457
38458     /**
38459      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
38460      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
38461      * @param {Boolean} preservePanel Overrides the config preservePanel option
38462      * @return {Roo.ContentPanel} The panel that was removed
38463      */
38464     remove : function(panel, preservePanel)
38465     {
38466         panel = this.getPanel(panel);
38467         if(!panel){
38468             return null;
38469         }
38470         var e = {};
38471         this.fireEvent("beforeremove", this, panel, e);
38472         if(e.cancel === true){
38473             return null;
38474         }
38475         preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
38476         var panelId = panel.getId();
38477         this.panels.removeKey(panelId);
38478         if(preservePanel){
38479             document.body.appendChild(panel.getEl().dom);
38480         }
38481         if(this.tabs){
38482             this.tabs.removeTab(panel.getEl().id);
38483         }else if (!preservePanel){
38484             this.bodyEl.dom.removeChild(panel.getEl().dom);
38485         }
38486         if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
38487             var p = this.panels.first();
38488             var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
38489             tempEl.appendChild(p.getEl().dom);
38490             this.bodyEl.update("");
38491             this.bodyEl.dom.appendChild(p.getEl().dom);
38492             tempEl = null;
38493             this.updateTitle(p.getTitle());
38494             this.tabs = null;
38495             this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
38496             this.setActivePanel(p);
38497         }
38498         panel.setRegion(null);
38499         if(this.activePanel == panel){
38500             this.activePanel = null;
38501         }
38502         if(this.config.autoDestroy !== false && preservePanel !== true){
38503             try{panel.destroy();}catch(e){}
38504         }
38505         this.fireEvent("panelremoved", this, panel);
38506         return panel;
38507     },
38508
38509     /**
38510      * Returns the TabPanel component used by this region
38511      * @return {Roo.TabPanel}
38512      */
38513     getTabs : function(){
38514         return this.tabs;
38515     },
38516
38517     createTool : function(parentEl, className){
38518         var btn = Roo.DomHelper.append(parentEl, {
38519             tag: "div",
38520             cls: "x-layout-tools-button",
38521             children: [ {
38522                 tag: "div",
38523                 cls: "roo-layout-tools-button-inner " + className,
38524                 html: "&#160;"
38525             }]
38526         }, true);
38527         btn.addClassOnOver("roo-layout-tools-button-over");
38528         return btn;
38529     }
38530 });/*
38531  * Based on:
38532  * Ext JS Library 1.1.1
38533  * Copyright(c) 2006-2007, Ext JS, LLC.
38534  *
38535  * Originally Released Under LGPL - original licence link has changed is not relivant.
38536  *
38537  * Fork - LGPL
38538  * <script type="text/javascript">
38539  */
38540  
38541
38542
38543 /**
38544  * @class Roo.SplitLayoutRegion
38545  * @extends Roo.LayoutRegion
38546  * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
38547  */
38548 Roo.bootstrap.layout.Split = function(config){
38549     this.cursor = config.cursor;
38550     Roo.bootstrap.layout.Split.superclass.constructor.call(this, config);
38551 };
38552
38553 Roo.extend(Roo.bootstrap.layout.Split, Roo.bootstrap.layout.Region,
38554 {
38555     splitTip : "Drag to resize.",
38556     collapsibleSplitTip : "Drag to resize. Double click to hide.",
38557     useSplitTips : false,
38558
38559     applyConfig : function(config){
38560         Roo.bootstrap.layout.Split.superclass.applyConfig.call(this, config);
38561     },
38562     
38563     onRender : function(ctr,pos) {
38564         
38565         Roo.bootstrap.layout.Split.superclass.onRender.call(this, ctr,pos);
38566         if(!this.config.split){
38567             return;
38568         }
38569         if(!this.split){
38570             
38571             var splitEl = Roo.DomHelper.append(ctr.dom,  {
38572                             tag: "div",
38573                             id: this.el.id + "-split",
38574                             cls: "roo-layout-split roo-layout-split-"+this.position,
38575                             html: "&#160;"
38576             });
38577             /** The SplitBar for this region 
38578             * @type Roo.SplitBar */
38579             // does not exist yet...
38580             Roo.log([this.position, this.orientation]);
38581             
38582             this.split = new Roo.bootstrap.SplitBar({
38583                 dragElement : splitEl,
38584                 resizingElement: this.el,
38585                 orientation : this.orientation
38586             });
38587             
38588             this.split.on("moved", this.onSplitMove, this);
38589             this.split.useShim = this.config.useShim === true;
38590             this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
38591             if(this.useSplitTips){
38592                 this.split.el.dom.title = this.config.collapsible ? this.collapsibleSplitTip : this.splitTip;
38593             }
38594             //if(config.collapsible){
38595             //    this.split.el.on("dblclick", this.collapse,  this);
38596             //}
38597         }
38598         if(typeof this.config.minSize != "undefined"){
38599             this.split.minSize = this.config.minSize;
38600         }
38601         if(typeof this.config.maxSize != "undefined"){
38602             this.split.maxSize = this.config.maxSize;
38603         }
38604         if(this.config.hideWhenEmpty || this.config.hidden || this.config.collapsed){
38605             this.hideSplitter();
38606         }
38607         
38608     },
38609
38610     getHMaxSize : function(){
38611          var cmax = this.config.maxSize || 10000;
38612          var center = this.mgr.getRegion("center");
38613          return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
38614     },
38615
38616     getVMaxSize : function(){
38617          var cmax = this.config.maxSize || 10000;
38618          var center = this.mgr.getRegion("center");
38619          return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
38620     },
38621
38622     onSplitMove : function(split, newSize){
38623         this.fireEvent("resized", this, newSize);
38624     },
38625     
38626     /** 
38627      * Returns the {@link Roo.SplitBar} for this region.
38628      * @return {Roo.SplitBar}
38629      */
38630     getSplitBar : function(){
38631         return this.split;
38632     },
38633     
38634     hide : function(){
38635         this.hideSplitter();
38636         Roo.bootstrap.layout.Split.superclass.hide.call(this);
38637     },
38638
38639     hideSplitter : function(){
38640         if(this.split){
38641             this.split.el.setLocation(-2000,-2000);
38642             this.split.el.hide();
38643         }
38644     },
38645
38646     show : function(){
38647         if(this.split){
38648             this.split.el.show();
38649         }
38650         Roo.bootstrap.layout.Split.superclass.show.call(this);
38651     },
38652     
38653     beforeSlide: function(){
38654         if(Roo.isGecko){// firefox overflow auto bug workaround
38655             this.bodyEl.clip();
38656             if(this.tabs) {
38657                 this.tabs.bodyEl.clip();
38658             }
38659             if(this.activePanel){
38660                 this.activePanel.getEl().clip();
38661                 
38662                 if(this.activePanel.beforeSlide){
38663                     this.activePanel.beforeSlide();
38664                 }
38665             }
38666         }
38667     },
38668     
38669     afterSlide : function(){
38670         if(Roo.isGecko){// firefox overflow auto bug workaround
38671             this.bodyEl.unclip();
38672             if(this.tabs) {
38673                 this.tabs.bodyEl.unclip();
38674             }
38675             if(this.activePanel){
38676                 this.activePanel.getEl().unclip();
38677                 if(this.activePanel.afterSlide){
38678                     this.activePanel.afterSlide();
38679                 }
38680             }
38681         }
38682     },
38683
38684     initAutoHide : function(){
38685         if(this.autoHide !== false){
38686             if(!this.autoHideHd){
38687                 var st = new Roo.util.DelayedTask(this.slideIn, this);
38688                 this.autoHideHd = {
38689                     "mouseout": function(e){
38690                         if(!e.within(this.el, true)){
38691                             st.delay(500);
38692                         }
38693                     },
38694                     "mouseover" : function(e){
38695                         st.cancel();
38696                     },
38697                     scope : this
38698                 };
38699             }
38700             this.el.on(this.autoHideHd);
38701         }
38702     },
38703
38704     clearAutoHide : function(){
38705         if(this.autoHide !== false){
38706             this.el.un("mouseout", this.autoHideHd.mouseout);
38707             this.el.un("mouseover", this.autoHideHd.mouseover);
38708         }
38709     },
38710
38711     clearMonitor : function(){
38712         Roo.get(document).un("click", this.slideInIf, this);
38713     },
38714
38715     // these names are backwards but not changed for compat
38716     slideOut : function(){
38717         if(this.isSlid || this.el.hasActiveFx()){
38718             return;
38719         }
38720         this.isSlid = true;
38721         if(this.collapseBtn){
38722             this.collapseBtn.hide();
38723         }
38724         this.closeBtnState = this.closeBtn.getStyle('display');
38725         this.closeBtn.hide();
38726         if(this.stickBtn){
38727             this.stickBtn.show();
38728         }
38729         this.el.show();
38730         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
38731         this.beforeSlide();
38732         this.el.setStyle("z-index", 10001);
38733         this.el.slideIn(this.getSlideAnchor(), {
38734             callback: function(){
38735                 this.afterSlide();
38736                 this.initAutoHide();
38737                 Roo.get(document).on("click", this.slideInIf, this);
38738                 this.fireEvent("slideshow", this);
38739             },
38740             scope: this,
38741             block: true
38742         });
38743     },
38744
38745     afterSlideIn : function(){
38746         this.clearAutoHide();
38747         this.isSlid = false;
38748         this.clearMonitor();
38749         this.el.setStyle("z-index", "");
38750         if(this.collapseBtn){
38751             this.collapseBtn.show();
38752         }
38753         this.closeBtn.setStyle('display', this.closeBtnState);
38754         if(this.stickBtn){
38755             this.stickBtn.hide();
38756         }
38757         this.fireEvent("slidehide", this);
38758     },
38759
38760     slideIn : function(cb){
38761         if(!this.isSlid || this.el.hasActiveFx()){
38762             Roo.callback(cb);
38763             return;
38764         }
38765         this.isSlid = false;
38766         this.beforeSlide();
38767         this.el.slideOut(this.getSlideAnchor(), {
38768             callback: function(){
38769                 this.el.setLeftTop(-10000, -10000);
38770                 this.afterSlide();
38771                 this.afterSlideIn();
38772                 Roo.callback(cb);
38773             },
38774             scope: this,
38775             block: true
38776         });
38777     },
38778     
38779     slideInIf : function(e){
38780         if(!e.within(this.el)){
38781             this.slideIn();
38782         }
38783     },
38784
38785     animateCollapse : function(){
38786         this.beforeSlide();
38787         this.el.setStyle("z-index", 20000);
38788         var anchor = this.getSlideAnchor();
38789         this.el.slideOut(anchor, {
38790             callback : function(){
38791                 this.el.setStyle("z-index", "");
38792                 this.collapsedEl.slideIn(anchor, {duration:.3});
38793                 this.afterSlide();
38794                 this.el.setLocation(-10000,-10000);
38795                 this.el.hide();
38796                 this.fireEvent("collapsed", this);
38797             },
38798             scope: this,
38799             block: true
38800         });
38801     },
38802
38803     animateExpand : function(){
38804         this.beforeSlide();
38805         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
38806         this.el.setStyle("z-index", 20000);
38807         this.collapsedEl.hide({
38808             duration:.1
38809         });
38810         this.el.slideIn(this.getSlideAnchor(), {
38811             callback : function(){
38812                 this.el.setStyle("z-index", "");
38813                 this.afterSlide();
38814                 if(this.split){
38815                     this.split.el.show();
38816                 }
38817                 this.fireEvent("invalidated", this);
38818                 this.fireEvent("expanded", this);
38819             },
38820             scope: this,
38821             block: true
38822         });
38823     },
38824
38825     anchors : {
38826         "west" : "left",
38827         "east" : "right",
38828         "north" : "top",
38829         "south" : "bottom"
38830     },
38831
38832     sanchors : {
38833         "west" : "l",
38834         "east" : "r",
38835         "north" : "t",
38836         "south" : "b"
38837     },
38838
38839     canchors : {
38840         "west" : "tl-tr",
38841         "east" : "tr-tl",
38842         "north" : "tl-bl",
38843         "south" : "bl-tl"
38844     },
38845
38846     getAnchor : function(){
38847         return this.anchors[this.position];
38848     },
38849
38850     getCollapseAnchor : function(){
38851         return this.canchors[this.position];
38852     },
38853
38854     getSlideAnchor : function(){
38855         return this.sanchors[this.position];
38856     },
38857
38858     getAlignAdj : function(){
38859         var cm = this.cmargins;
38860         switch(this.position){
38861             case "west":
38862                 return [0, 0];
38863             break;
38864             case "east":
38865                 return [0, 0];
38866             break;
38867             case "north":
38868                 return [0, 0];
38869             break;
38870             case "south":
38871                 return [0, 0];
38872             break;
38873         }
38874     },
38875
38876     getExpandAdj : function(){
38877         var c = this.collapsedEl, cm = this.cmargins;
38878         switch(this.position){
38879             case "west":
38880                 return [-(cm.right+c.getWidth()+cm.left), 0];
38881             break;
38882             case "east":
38883                 return [cm.right+c.getWidth()+cm.left, 0];
38884             break;
38885             case "north":
38886                 return [0, -(cm.top+cm.bottom+c.getHeight())];
38887             break;
38888             case "south":
38889                 return [0, cm.top+cm.bottom+c.getHeight()];
38890             break;
38891         }
38892     }
38893 });/*
38894  * Based on:
38895  * Ext JS Library 1.1.1
38896  * Copyright(c) 2006-2007, Ext JS, LLC.
38897  *
38898  * Originally Released Under LGPL - original licence link has changed is not relivant.
38899  *
38900  * Fork - LGPL
38901  * <script type="text/javascript">
38902  */
38903 /*
38904  * These classes are private internal classes
38905  */
38906 Roo.bootstrap.layout.Center = function(config){
38907     config.region = "center";
38908     Roo.bootstrap.layout.Region.call(this, config);
38909     this.visible = true;
38910     this.minWidth = config.minWidth || 20;
38911     this.minHeight = config.minHeight || 20;
38912 };
38913
38914 Roo.extend(Roo.bootstrap.layout.Center, Roo.bootstrap.layout.Region, {
38915     hide : function(){
38916         // center panel can't be hidden
38917     },
38918     
38919     show : function(){
38920         // center panel can't be hidden
38921     },
38922     
38923     getMinWidth: function(){
38924         return this.minWidth;
38925     },
38926     
38927     getMinHeight: function(){
38928         return this.minHeight;
38929     }
38930 });
38931
38932
38933
38934
38935  
38936
38937
38938
38939
38940
38941
38942 Roo.bootstrap.layout.North = function(config)
38943 {
38944     config.region = 'north';
38945     config.cursor = 'n-resize';
38946     
38947     Roo.bootstrap.layout.Split.call(this, config);
38948     
38949     
38950     if(this.split){
38951         this.split.placement = Roo.bootstrap.SplitBar.TOP;
38952         this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
38953         this.split.el.addClass("roo-layout-split-v");
38954     }
38955     var size = config.initialSize || config.height;
38956     if(typeof size != "undefined"){
38957         this.el.setHeight(size);
38958     }
38959 };
38960 Roo.extend(Roo.bootstrap.layout.North, Roo.bootstrap.layout.Split,
38961 {
38962     orientation: Roo.bootstrap.SplitBar.VERTICAL,
38963     
38964     
38965     
38966     getBox : function(){
38967         if(this.collapsed){
38968             return this.collapsedEl.getBox();
38969         }
38970         var box = this.el.getBox();
38971         if(this.split){
38972             box.height += this.split.el.getHeight();
38973         }
38974         return box;
38975     },
38976     
38977     updateBox : function(box){
38978         if(this.split && !this.collapsed){
38979             box.height -= this.split.el.getHeight();
38980             this.split.el.setLeft(box.x);
38981             this.split.el.setTop(box.y+box.height);
38982             this.split.el.setWidth(box.width);
38983         }
38984         if(this.collapsed){
38985             this.updateBody(box.width, null);
38986         }
38987         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
38988     }
38989 });
38990
38991
38992
38993
38994
38995 Roo.bootstrap.layout.South = function(config){
38996     config.region = 'south';
38997     config.cursor = 's-resize';
38998     Roo.bootstrap.layout.Split.call(this, config);
38999     if(this.split){
39000         this.split.placement = Roo.bootstrap.SplitBar.BOTTOM;
39001         this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
39002         this.split.el.addClass("roo-layout-split-v");
39003     }
39004     var size = config.initialSize || config.height;
39005     if(typeof size != "undefined"){
39006         this.el.setHeight(size);
39007     }
39008 };
39009
39010 Roo.extend(Roo.bootstrap.layout.South, Roo.bootstrap.layout.Split, {
39011     orientation: Roo.bootstrap.SplitBar.VERTICAL,
39012     getBox : function(){
39013         if(this.collapsed){
39014             return this.collapsedEl.getBox();
39015         }
39016         var box = this.el.getBox();
39017         if(this.split){
39018             var sh = this.split.el.getHeight();
39019             box.height += sh;
39020             box.y -= sh;
39021         }
39022         return box;
39023     },
39024     
39025     updateBox : function(box){
39026         if(this.split && !this.collapsed){
39027             var sh = this.split.el.getHeight();
39028             box.height -= sh;
39029             box.y += sh;
39030             this.split.el.setLeft(box.x);
39031             this.split.el.setTop(box.y-sh);
39032             this.split.el.setWidth(box.width);
39033         }
39034         if(this.collapsed){
39035             this.updateBody(box.width, null);
39036         }
39037         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
39038     }
39039 });
39040
39041 Roo.bootstrap.layout.East = function(config){
39042     config.region = "east";
39043     config.cursor = "e-resize";
39044     Roo.bootstrap.layout.Split.call(this, config);
39045     if(this.split){
39046         this.split.placement = Roo.bootstrap.SplitBar.RIGHT;
39047         this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
39048         this.split.el.addClass("roo-layout-split-h");
39049     }
39050     var size = config.initialSize || config.width;
39051     if(typeof size != "undefined"){
39052         this.el.setWidth(size);
39053     }
39054 };
39055 Roo.extend(Roo.bootstrap.layout.East, Roo.bootstrap.layout.Split, {
39056     orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
39057     getBox : function(){
39058         if(this.collapsed){
39059             return this.collapsedEl.getBox();
39060         }
39061         var box = this.el.getBox();
39062         if(this.split){
39063             var sw = this.split.el.getWidth();
39064             box.width += sw;
39065             box.x -= sw;
39066         }
39067         return box;
39068     },
39069
39070     updateBox : function(box){
39071         if(this.split && !this.collapsed){
39072             var sw = this.split.el.getWidth();
39073             box.width -= sw;
39074             this.split.el.setLeft(box.x);
39075             this.split.el.setTop(box.y);
39076             this.split.el.setHeight(box.height);
39077             box.x += sw;
39078         }
39079         if(this.collapsed){
39080             this.updateBody(null, box.height);
39081         }
39082         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
39083     }
39084 });
39085
39086 Roo.bootstrap.layout.West = function(config){
39087     config.region = "west";
39088     config.cursor = "w-resize";
39089     
39090     Roo.bootstrap.layout.Split.call(this, config);
39091     if(this.split){
39092         this.split.placement = Roo.bootstrap.SplitBar.LEFT;
39093         this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
39094         this.split.el.addClass("roo-layout-split-h");
39095     }
39096     
39097 };
39098 Roo.extend(Roo.bootstrap.layout.West, Roo.bootstrap.layout.Split, {
39099     orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
39100     
39101     onRender: function(ctr, pos)
39102     {
39103         Roo.bootstrap.layout.West.superclass.onRender.call(this, ctr,pos);
39104         var size = this.config.initialSize || this.config.width;
39105         if(typeof size != "undefined"){
39106             this.el.setWidth(size);
39107         }
39108     },
39109     
39110     getBox : function(){
39111         if(this.collapsed){
39112             return this.collapsedEl.getBox();
39113         }
39114         var box = this.el.getBox();
39115         if(this.split){
39116             box.width += this.split.el.getWidth();
39117         }
39118         return box;
39119     },
39120     
39121     updateBox : function(box){
39122         if(this.split && !this.collapsed){
39123             var sw = this.split.el.getWidth();
39124             box.width -= sw;
39125             this.split.el.setLeft(box.x+box.width);
39126             this.split.el.setTop(box.y);
39127             this.split.el.setHeight(box.height);
39128         }
39129         if(this.collapsed){
39130             this.updateBody(null, box.height);
39131         }
39132         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
39133     }
39134 });Roo.namespace("Roo.bootstrap.panel");/*
39135  * Based on:
39136  * Ext JS Library 1.1.1
39137  * Copyright(c) 2006-2007, Ext JS, LLC.
39138  *
39139  * Originally Released Under LGPL - original licence link has changed is not relivant.
39140  *
39141  * Fork - LGPL
39142  * <script type="text/javascript">
39143  */
39144 /**
39145  * @class Roo.ContentPanel
39146  * @extends Roo.util.Observable
39147  * A basic ContentPanel element.
39148  * @cfg {Boolean}   fitToFrame    True for this panel to adjust its size to fit when the region resizes  (defaults to false)
39149  * @cfg {Boolean}   fitContainer   When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container  (defaults to false)
39150  * @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
39151  * @cfg {Boolean}   closable      True if the panel can be closed/removed
39152  * @cfg {Boolean}   background    True if the panel should not be activated when it is added (defaults to false)
39153  * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
39154  * @cfg {Toolbar}   toolbar       A toolbar for this panel
39155  * @cfg {Boolean} autoScroll    True to scroll overflow in this panel (use with {@link #fitToFrame})
39156  * @cfg {String} title          The title for this panel
39157  * @cfg {Array} adjustments     Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
39158  * @cfg {String} url            Calls {@link #setUrl} with this value
39159  * @cfg {String} region         (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
39160  * @cfg {String/Object} params  When used with {@link #url}, calls {@link #setUrl} with this value
39161  * @cfg {Boolean} loadOnce      When used with {@link #url}, calls {@link #setUrl} with this value
39162  * @cfg {String}    content        Raw content to fill content panel with (uses setContent on construction.)
39163  * @cfg {Boolean} badges render the badges
39164  * @cfg {String} cls  extra classes to use  
39165  * @cfg {String} background (primary|secondary|success|info|warning|danger|light|dark)
39166
39167  * @constructor
39168  * Create a new ContentPanel.
39169  * @param {String/HTMLElement/Roo.Element} el The container element for this panel
39170  * @param {String/Object} config A string to set only the title or a config object
39171  * @param {String} content (optional) Set the HTML content for this panel
39172  * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
39173  */
39174 Roo.bootstrap.panel.Content = function( config){
39175     
39176     this.tpl = config.tpl || false;
39177     
39178     var el = config.el;
39179     var content = config.content;
39180
39181     if(config.autoCreate){ // xtype is available if this is called from factory
39182         el = Roo.id();
39183     }
39184     this.el = Roo.get(el);
39185     if(!this.el && config && config.autoCreate){
39186         if(typeof config.autoCreate == "object"){
39187             if(!config.autoCreate.id){
39188                 config.autoCreate.id = config.id||el;
39189             }
39190             this.el = Roo.DomHelper.append(document.body,
39191                         config.autoCreate, true);
39192         }else{
39193             var elcfg =  {
39194                 tag: "div",
39195                 cls: (config.cls || '') +
39196                     (config.background ? ' bg-' + config.background : '') +
39197                     " roo-layout-inactive-content",
39198                 id: config.id||el
39199             };
39200             if (config.html) {
39201                 elcfg.html = config.html;
39202                 
39203             }
39204                         
39205             this.el = Roo.DomHelper.append(document.body, elcfg , true);
39206         }
39207     } 
39208     this.closable = false;
39209     this.loaded = false;
39210     this.active = false;
39211    
39212       
39213     if (config.toolbar && !config.toolbar.el && config.toolbar.xtype) {
39214         
39215         this.toolbar = new config.toolbar.xns[config.toolbar.xtype](config.toolbar);
39216         
39217         this.wrapEl = this.el; //this.el.wrap();
39218         var ti = [];
39219         if (config.toolbar.items) {
39220             ti = config.toolbar.items ;
39221             delete config.toolbar.items ;
39222         }
39223         
39224         var nitems = [];
39225         this.toolbar.render(this.wrapEl, 'before');
39226         for(var i =0;i < ti.length;i++) {
39227           //  Roo.log(['add child', items[i]]);
39228             nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
39229         }
39230         this.toolbar.items = nitems;
39231         this.toolbar.el.insertBefore(this.wrapEl.dom.firstChild);
39232         delete config.toolbar;
39233         
39234     }
39235     /*
39236     // xtype created footer. - not sure if will work as we normally have to render first..
39237     if (this.footer && !this.footer.el && this.footer.xtype) {
39238         if (!this.wrapEl) {
39239             this.wrapEl = this.el.wrap();
39240         }
39241     
39242         this.footer.container = this.wrapEl.createChild();
39243          
39244         this.footer = Roo.factory(this.footer, Roo);
39245         
39246     }
39247     */
39248     
39249      if(typeof config == "string"){
39250         this.title = config;
39251     }else{
39252         Roo.apply(this, config);
39253     }
39254     
39255     if(this.resizeEl){
39256         this.resizeEl = Roo.get(this.resizeEl, true);
39257     }else{
39258         this.resizeEl = this.el;
39259     }
39260     // handle view.xtype
39261     
39262  
39263     
39264     
39265     this.addEvents({
39266         /**
39267          * @event activate
39268          * Fires when this panel is activated. 
39269          * @param {Roo.ContentPanel} this
39270          */
39271         "activate" : true,
39272         /**
39273          * @event deactivate
39274          * Fires when this panel is activated. 
39275          * @param {Roo.ContentPanel} this
39276          */
39277         "deactivate" : true,
39278
39279         /**
39280          * @event resize
39281          * Fires when this panel is resized if fitToFrame is true.
39282          * @param {Roo.ContentPanel} this
39283          * @param {Number} width The width after any component adjustments
39284          * @param {Number} height The height after any component adjustments
39285          */
39286         "resize" : true,
39287         
39288          /**
39289          * @event render
39290          * Fires when this tab is created
39291          * @param {Roo.ContentPanel} this
39292          */
39293         "render" : true
39294         
39295         
39296         
39297     });
39298     
39299
39300     
39301     
39302     if(this.autoScroll){
39303         this.resizeEl.setStyle("overflow", "auto");
39304     } else {
39305         // fix randome scrolling
39306         //this.el.on('scroll', function() {
39307         //    Roo.log('fix random scolling');
39308         //    this.scrollTo('top',0); 
39309         //});
39310     }
39311     content = content || this.content;
39312     if(content){
39313         this.setContent(content);
39314     }
39315     if(config && config.url){
39316         this.setUrl(this.url, this.params, this.loadOnce);
39317     }
39318     
39319     
39320     
39321     Roo.bootstrap.panel.Content.superclass.constructor.call(this);
39322     
39323     if (this.view && typeof(this.view.xtype) != 'undefined') {
39324         this.view.el = this.el.appendChild(document.createElement("div"));
39325         this.view = Roo.factory(this.view); 
39326         this.view.render  &&  this.view.render(false, '');  
39327     }
39328     
39329     
39330     this.fireEvent('render', this);
39331 };
39332
39333 Roo.extend(Roo.bootstrap.panel.Content, Roo.bootstrap.Component, {
39334     
39335     cls : '',
39336     background : '',
39337     
39338     tabTip : '',
39339     
39340     setRegion : function(region){
39341         this.region = region;
39342         this.setActiveClass(region && !this.background);
39343     },
39344     
39345     
39346     setActiveClass: function(state)
39347     {
39348         if(state){
39349            this.el.replaceClass("roo-layout-inactive-content", "roo-layout-active-content");
39350            this.el.setStyle('position','relative');
39351         }else{
39352            this.el.replaceClass("roo-layout-active-content", "roo-layout-inactive-content");
39353            this.el.setStyle('position', 'absolute');
39354         } 
39355     },
39356     
39357     /**
39358      * Returns the toolbar for this Panel if one was configured. 
39359      * @return {Roo.Toolbar} 
39360      */
39361     getToolbar : function(){
39362         return this.toolbar;
39363     },
39364     
39365     setActiveState : function(active)
39366     {
39367         this.active = active;
39368         this.setActiveClass(active);
39369         if(!active){
39370             if(this.fireEvent("deactivate", this) === false){
39371                 return false;
39372             }
39373             return true;
39374         }
39375         this.fireEvent("activate", this);
39376         return true;
39377     },
39378     /**
39379      * Updates this panel's element
39380      * @param {String} content The new content
39381      * @param {Boolean} loadScripts (optional) true to look for and process scripts
39382     */
39383     setContent : function(content, loadScripts){
39384         this.el.update(content, loadScripts);
39385     },
39386
39387     ignoreResize : function(w, h){
39388         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
39389             return true;
39390         }else{
39391             this.lastSize = {width: w, height: h};
39392             return false;
39393         }
39394     },
39395     /**
39396      * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
39397      * @return {Roo.UpdateManager} The UpdateManager
39398      */
39399     getUpdateManager : function(){
39400         return this.el.getUpdateManager();
39401     },
39402      /**
39403      * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
39404      * @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:
39405 <pre><code>
39406 panel.load({
39407     url: "your-url.php",
39408     params: {param1: "foo", param2: "bar"}, // or a URL encoded string
39409     callback: yourFunction,
39410     scope: yourObject, //(optional scope)
39411     discardUrl: false,
39412     nocache: false,
39413     text: "Loading...",
39414     timeout: 30,
39415     scripts: false
39416 });
39417 </code></pre>
39418      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
39419      * 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.
39420      * @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}
39421      * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
39422      * @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.
39423      * @return {Roo.ContentPanel} this
39424      */
39425     load : function(){
39426         var um = this.el.getUpdateManager();
39427         um.update.apply(um, arguments);
39428         return this;
39429     },
39430
39431
39432     /**
39433      * 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.
39434      * @param {String/Function} url The URL to load the content from or a function to call to get the URL
39435      * @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)
39436      * @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)
39437      * @return {Roo.UpdateManager} The UpdateManager
39438      */
39439     setUrl : function(url, params, loadOnce){
39440         if(this.refreshDelegate){
39441             this.removeListener("activate", this.refreshDelegate);
39442         }
39443         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
39444         this.on("activate", this.refreshDelegate);
39445         return this.el.getUpdateManager();
39446     },
39447     
39448     _handleRefresh : function(url, params, loadOnce){
39449         if(!loadOnce || !this.loaded){
39450             var updater = this.el.getUpdateManager();
39451             updater.update(url, params, this._setLoaded.createDelegate(this));
39452         }
39453     },
39454     
39455     _setLoaded : function(){
39456         this.loaded = true;
39457     }, 
39458     
39459     /**
39460      * Returns this panel's id
39461      * @return {String} 
39462      */
39463     getId : function(){
39464         return this.el.id;
39465     },
39466     
39467     /** 
39468      * Returns this panel's element - used by regiosn to add.
39469      * @return {Roo.Element} 
39470      */
39471     getEl : function(){
39472         return this.wrapEl || this.el;
39473     },
39474     
39475    
39476     
39477     adjustForComponents : function(width, height)
39478     {
39479         //Roo.log('adjustForComponents ');
39480         if(this.resizeEl != this.el){
39481             width -= this.el.getFrameWidth('lr');
39482             height -= this.el.getFrameWidth('tb');
39483         }
39484         if(this.toolbar){
39485             var te = this.toolbar.getEl();
39486             te.setWidth(width);
39487             height -= te.getHeight();
39488         }
39489         if(this.footer){
39490             var te = this.footer.getEl();
39491             te.setWidth(width);
39492             height -= te.getHeight();
39493         }
39494         
39495         
39496         if(this.adjustments){
39497             width += this.adjustments[0];
39498             height += this.adjustments[1];
39499         }
39500         return {"width": width, "height": height};
39501     },
39502     
39503     setSize : function(width, height){
39504         if(this.fitToFrame && !this.ignoreResize(width, height)){
39505             if(this.fitContainer && this.resizeEl != this.el){
39506                 this.el.setSize(width, height);
39507             }
39508             var size = this.adjustForComponents(width, height);
39509             this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
39510             this.fireEvent('resize', this, size.width, size.height);
39511         }
39512     },
39513     
39514     /**
39515      * Returns this panel's title
39516      * @return {String} 
39517      */
39518     getTitle : function(){
39519         
39520         if (typeof(this.title) != 'object') {
39521             return this.title;
39522         }
39523         
39524         var t = '';
39525         for (var k in this.title) {
39526             if (!this.title.hasOwnProperty(k)) {
39527                 continue;
39528             }
39529             
39530             if (k.indexOf('-') >= 0) {
39531                 var s = k.split('-');
39532                 for (var i = 0; i<s.length; i++) {
39533                     t += "<span class='visible-"+s[i]+"'>"+this.title[k]+"</span>";
39534                 }
39535             } else {
39536                 t += "<span class='visible-"+k+"'>"+this.title[k]+"</span>";
39537             }
39538         }
39539         return t;
39540     },
39541     
39542     /**
39543      * Set this panel's title
39544      * @param {String} title
39545      */
39546     setTitle : function(title){
39547         this.title = title;
39548         if(this.region){
39549             this.region.updatePanelTitle(this, title);
39550         }
39551     },
39552     
39553     /**
39554      * Returns true is this panel was configured to be closable
39555      * @return {Boolean} 
39556      */
39557     isClosable : function(){
39558         return this.closable;
39559     },
39560     
39561     beforeSlide : function(){
39562         this.el.clip();
39563         this.resizeEl.clip();
39564     },
39565     
39566     afterSlide : function(){
39567         this.el.unclip();
39568         this.resizeEl.unclip();
39569     },
39570     
39571     /**
39572      *   Force a content refresh from the URL specified in the {@link #setUrl} method.
39573      *   Will fail silently if the {@link #setUrl} method has not been called.
39574      *   This does not activate the panel, just updates its content.
39575      */
39576     refresh : function(){
39577         if(this.refreshDelegate){
39578            this.loaded = false;
39579            this.refreshDelegate();
39580         }
39581     },
39582     
39583     /**
39584      * Destroys this panel
39585      */
39586     destroy : function(){
39587         this.el.removeAllListeners();
39588         var tempEl = document.createElement("span");
39589         tempEl.appendChild(this.el.dom);
39590         tempEl.innerHTML = "";
39591         this.el.remove();
39592         this.el = null;
39593     },
39594     
39595     /**
39596      * form - if the content panel contains a form - this is a reference to it.
39597      * @type {Roo.form.Form}
39598      */
39599     form : false,
39600     /**
39601      * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
39602      *    This contains a reference to it.
39603      * @type {Roo.View}
39604      */
39605     view : false,
39606     
39607       /**
39608      * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
39609      * <pre><code>
39610
39611 layout.addxtype({
39612        xtype : 'Form',
39613        items: [ .... ]
39614    }
39615 );
39616
39617 </code></pre>
39618      * @param {Object} cfg Xtype definition of item to add.
39619      */
39620     
39621     
39622     getChildContainer: function () {
39623         return this.getEl();
39624     }
39625     
39626     
39627     /*
39628         var  ret = new Roo.factory(cfg);
39629         return ret;
39630         
39631         
39632         // add form..
39633         if (cfg.xtype.match(/^Form$/)) {
39634             
39635             var el;
39636             //if (this.footer) {
39637             //    el = this.footer.container.insertSibling(false, 'before');
39638             //} else {
39639                 el = this.el.createChild();
39640             //}
39641
39642             this.form = new  Roo.form.Form(cfg);
39643             
39644             
39645             if ( this.form.allItems.length) {
39646                 this.form.render(el.dom);
39647             }
39648             return this.form;
39649         }
39650         // should only have one of theses..
39651         if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
39652             // views.. should not be just added - used named prop 'view''
39653             
39654             cfg.el = this.el.appendChild(document.createElement("div"));
39655             // factory?
39656             
39657             var ret = new Roo.factory(cfg);
39658              
39659              ret.render && ret.render(false, ''); // render blank..
39660             this.view = ret;
39661             return ret;
39662         }
39663         return false;
39664     }
39665     \*/
39666 });
39667  
39668 /**
39669  * @class Roo.bootstrap.panel.Grid
39670  * @extends Roo.bootstrap.panel.Content
39671  * @constructor
39672  * Create a new GridPanel.
39673  * @cfg {Roo.bootstrap.Table} grid The grid for this panel
39674  * @param {Object} config A the config object
39675   
39676  */
39677
39678
39679
39680 Roo.bootstrap.panel.Grid = function(config)
39681 {
39682     
39683       
39684     this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
39685         {tag: "div", cls: "roo-layout-grid-wrapper roo-layout-inactive-content"}, true);
39686
39687     config.el = this.wrapper;
39688     //this.el = this.wrapper;
39689     
39690       if (config.container) {
39691         // ctor'ed from a Border/panel.grid
39692         
39693         
39694         this.wrapper.setStyle("overflow", "hidden");
39695         this.wrapper.addClass('roo-grid-container');
39696
39697     }
39698     
39699     
39700     if(config.toolbar){
39701         var tool_el = this.wrapper.createChild();    
39702         this.toolbar = Roo.factory(config.toolbar);
39703         var ti = [];
39704         if (config.toolbar.items) {
39705             ti = config.toolbar.items ;
39706             delete config.toolbar.items ;
39707         }
39708         
39709         var nitems = [];
39710         this.toolbar.render(tool_el);
39711         for(var i =0;i < ti.length;i++) {
39712           //  Roo.log(['add child', items[i]]);
39713             nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
39714         }
39715         this.toolbar.items = nitems;
39716         
39717         delete config.toolbar;
39718     }
39719     
39720     Roo.bootstrap.panel.Grid.superclass.constructor.call(this, config);
39721     config.grid.scrollBody = true;;
39722     config.grid.monitorWindowResize = false; // turn off autosizing
39723     config.grid.autoHeight = false;
39724     config.grid.autoWidth = false;
39725     
39726     this.grid = new config.grid.xns[config.grid.xtype](config.grid);
39727     
39728     if (config.background) {
39729         // render grid on panel activation (if panel background)
39730         this.on('activate', function(gp) {
39731             if (!gp.grid.rendered) {
39732                 gp.grid.render(this.wrapper);
39733                 gp.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");   
39734             }
39735         });
39736             
39737     } else {
39738         this.grid.render(this.wrapper);
39739         this.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");               
39740
39741     }
39742     //this.wrapper.dom.appendChild(config.grid.getGridEl().dom);
39743     // ??? needed ??? config.el = this.wrapper;
39744     
39745     
39746     
39747   
39748     // xtype created footer. - not sure if will work as we normally have to render first..
39749     if (this.footer && !this.footer.el && this.footer.xtype) {
39750         
39751         var ctr = this.grid.getView().getFooterPanel(true);
39752         this.footer.dataSource = this.grid.dataSource;
39753         this.footer = Roo.factory(this.footer, Roo);
39754         this.footer.render(ctr);
39755         
39756     }
39757     
39758     
39759     
39760     
39761      
39762 };
39763
39764 Roo.extend(Roo.bootstrap.panel.Grid, Roo.bootstrap.panel.Content, {
39765     getId : function(){
39766         return this.grid.id;
39767     },
39768     
39769     /**
39770      * Returns the grid for this panel
39771      * @return {Roo.bootstrap.Table} 
39772      */
39773     getGrid : function(){
39774         return this.grid;    
39775     },
39776     
39777     setSize : function(width, height){
39778         if(!this.ignoreResize(width, height)){
39779             var grid = this.grid;
39780             var size = this.adjustForComponents(width, height);
39781             // tfoot is not a footer?
39782           
39783             
39784             var gridel = grid.getGridEl();
39785             gridel.setSize(size.width, size.height);
39786             
39787             var tbd = grid.getGridEl().select('tbody', true).first();
39788             var thd = grid.getGridEl().select('thead',true).first();
39789             var tbf= grid.getGridEl().select('tfoot', true).first();
39790
39791             if (tbf) {
39792                 size.height -= thd.getHeight();
39793             }
39794             if (thd) {
39795                 size.height -= thd.getHeight();
39796             }
39797             
39798             tbd.setSize(size.width, size.height );
39799             // this is for the account management tab -seems to work there.
39800             var thd = grid.getGridEl().select('thead',true).first();
39801             //if (tbd) {
39802             //    tbd.setSize(size.width, size.height - thd.getHeight());
39803             //}
39804              
39805             grid.autoSize();
39806         }
39807     },
39808      
39809     
39810     
39811     beforeSlide : function(){
39812         this.grid.getView().scroller.clip();
39813     },
39814     
39815     afterSlide : function(){
39816         this.grid.getView().scroller.unclip();
39817     },
39818     
39819     destroy : function(){
39820         this.grid.destroy();
39821         delete this.grid;
39822         Roo.bootstrap.panel.Grid.superclass.destroy.call(this); 
39823     }
39824 });
39825
39826 /**
39827  * @class Roo.bootstrap.panel.Nest
39828  * @extends Roo.bootstrap.panel.Content
39829  * @constructor
39830  * Create a new Panel, that can contain a layout.Border.
39831  * 
39832  * 
39833  * @param {Roo.BorderLayout} layout The layout for this panel
39834  * @param {String/Object} config A string to set only the title or a config object
39835  */
39836 Roo.bootstrap.panel.Nest = function(config)
39837 {
39838     // construct with only one argument..
39839     /* FIXME - implement nicer consturctors
39840     if (layout.layout) {
39841         config = layout;
39842         layout = config.layout;
39843         delete config.layout;
39844     }
39845     if (layout.xtype && !layout.getEl) {
39846         // then layout needs constructing..
39847         layout = Roo.factory(layout, Roo);
39848     }
39849     */
39850     
39851     config.el =  config.layout.getEl();
39852     
39853     Roo.bootstrap.panel.Nest.superclass.constructor.call(this, config);
39854     
39855     config.layout.monitorWindowResize = false; // turn off autosizing
39856     this.layout = config.layout;
39857     this.layout.getEl().addClass("roo-layout-nested-layout");
39858     this.layout.parent = this;
39859     
39860     
39861     
39862     
39863 };
39864
39865 Roo.extend(Roo.bootstrap.panel.Nest, Roo.bootstrap.panel.Content, {
39866
39867     setSize : function(width, height){
39868         if(!this.ignoreResize(width, height)){
39869             var size = this.adjustForComponents(width, height);
39870             var el = this.layout.getEl();
39871             if (size.height < 1) {
39872                 el.setWidth(size.width);   
39873             } else {
39874                 el.setSize(size.width, size.height);
39875             }
39876             var touch = el.dom.offsetWidth;
39877             this.layout.layout();
39878             // ie requires a double layout on the first pass
39879             if(Roo.isIE && !this.initialized){
39880                 this.initialized = true;
39881                 this.layout.layout();
39882             }
39883         }
39884     },
39885     
39886     // activate all subpanels if not currently active..
39887     
39888     setActiveState : function(active){
39889         this.active = active;
39890         this.setActiveClass(active);
39891         
39892         if(!active){
39893             this.fireEvent("deactivate", this);
39894             return;
39895         }
39896         
39897         this.fireEvent("activate", this);
39898         // not sure if this should happen before or after..
39899         if (!this.layout) {
39900             return; // should not happen..
39901         }
39902         var reg = false;
39903         for (var r in this.layout.regions) {
39904             reg = this.layout.getRegion(r);
39905             if (reg.getActivePanel()) {
39906                 //reg.showPanel(reg.getActivePanel()); // force it to activate.. 
39907                 reg.setActivePanel(reg.getActivePanel());
39908                 continue;
39909             }
39910             if (!reg.panels.length) {
39911                 continue;
39912             }
39913             reg.showPanel(reg.getPanel(0));
39914         }
39915         
39916         
39917         
39918         
39919     },
39920     
39921     /**
39922      * Returns the nested BorderLayout for this panel
39923      * @return {Roo.BorderLayout} 
39924      */
39925     getLayout : function(){
39926         return this.layout;
39927     },
39928     
39929      /**
39930      * Adds a xtype elements to the layout of the nested panel
39931      * <pre><code>
39932
39933 panel.addxtype({
39934        xtype : 'ContentPanel',
39935        region: 'west',
39936        items: [ .... ]
39937    }
39938 );
39939
39940 panel.addxtype({
39941         xtype : 'NestedLayoutPanel',
39942         region: 'west',
39943         layout: {
39944            center: { },
39945            west: { }   
39946         },
39947         items : [ ... list of content panels or nested layout panels.. ]
39948    }
39949 );
39950 </code></pre>
39951      * @param {Object} cfg Xtype definition of item to add.
39952      */
39953     addxtype : function(cfg) {
39954         return this.layout.addxtype(cfg);
39955     
39956     }
39957 });/*
39958  * Based on:
39959  * Ext JS Library 1.1.1
39960  * Copyright(c) 2006-2007, Ext JS, LLC.
39961  *
39962  * Originally Released Under LGPL - original licence link has changed is not relivant.
39963  *
39964  * Fork - LGPL
39965  * <script type="text/javascript">
39966  */
39967 /**
39968  * @class Roo.TabPanel
39969  * @extends Roo.util.Observable
39970  * A lightweight tab container.
39971  * <br><br>
39972  * Usage:
39973  * <pre><code>
39974 // basic tabs 1, built from existing content
39975 var tabs = new Roo.TabPanel("tabs1");
39976 tabs.addTab("script", "View Script");
39977 tabs.addTab("markup", "View Markup");
39978 tabs.activate("script");
39979
39980 // more advanced tabs, built from javascript
39981 var jtabs = new Roo.TabPanel("jtabs");
39982 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
39983
39984 // set up the UpdateManager
39985 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
39986 var updater = tab2.getUpdateManager();
39987 updater.setDefaultUrl("ajax1.htm");
39988 tab2.on('activate', updater.refresh, updater, true);
39989
39990 // Use setUrl for Ajax loading
39991 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
39992 tab3.setUrl("ajax2.htm", null, true);
39993
39994 // Disabled tab
39995 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
39996 tab4.disable();
39997
39998 jtabs.activate("jtabs-1");
39999  * </code></pre>
40000  * @constructor
40001  * Create a new TabPanel.
40002  * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
40003  * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
40004  */
40005 Roo.bootstrap.panel.Tabs = function(config){
40006     /**
40007     * The container element for this TabPanel.
40008     * @type Roo.Element
40009     */
40010     this.el = Roo.get(config.el);
40011     delete config.el;
40012     if(config){
40013         if(typeof config == "boolean"){
40014             this.tabPosition = config ? "bottom" : "top";
40015         }else{
40016             Roo.apply(this, config);
40017         }
40018     }
40019     
40020     if(this.tabPosition == "bottom"){
40021         // if tabs are at the bottom = create the body first.
40022         this.bodyEl = Roo.get(this.createBody(this.el.dom));
40023         this.el.addClass("roo-tabs-bottom");
40024     }
40025     // next create the tabs holders
40026     
40027     if (this.tabPosition == "west"){
40028         
40029         var reg = this.region; // fake it..
40030         while (reg) {
40031             if (!reg.mgr.parent) {
40032                 break;
40033             }
40034             reg = reg.mgr.parent.region;
40035         }
40036         Roo.log("got nest?");
40037         Roo.log(reg);
40038         if (reg.mgr.getRegion('west')) {
40039             var ctrdom = reg.mgr.getRegion('west').bodyEl.dom;
40040             this.stripWrap = Roo.get(this.createStrip(ctrdom ), true);
40041             this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
40042             this.stripEl.setVisibilityMode(Roo.Element.DISPLAY);
40043             this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
40044         
40045             
40046         }
40047         
40048         
40049     } else {
40050      
40051         this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
40052         this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
40053         this.stripEl.setVisibilityMode(Roo.Element.DISPLAY);
40054         this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
40055     }
40056     
40057     
40058     if(Roo.isIE){
40059         Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
40060     }
40061     
40062     // finally - if tabs are at the top, then create the body last..
40063     if(this.tabPosition != "bottom"){
40064         /** The body element that contains {@link Roo.TabPanelItem} bodies. +
40065          * @type Roo.Element
40066          */
40067         this.bodyEl = Roo.get(this.createBody(this.el.dom));
40068         this.el.addClass("roo-tabs-top");
40069     }
40070     this.items = [];
40071
40072     this.bodyEl.setStyle("position", "relative");
40073
40074     this.active = null;
40075     this.activateDelegate = this.activate.createDelegate(this);
40076
40077     this.addEvents({
40078         /**
40079          * @event tabchange
40080          * Fires when the active tab changes
40081          * @param {Roo.TabPanel} this
40082          * @param {Roo.TabPanelItem} activePanel The new active tab
40083          */
40084         "tabchange": true,
40085         /**
40086          * @event beforetabchange
40087          * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
40088          * @param {Roo.TabPanel} this
40089          * @param {Object} e Set cancel to true on this object to cancel the tab change
40090          * @param {Roo.TabPanelItem} tab The tab being changed to
40091          */
40092         "beforetabchange" : true
40093     });
40094
40095     Roo.EventManager.onWindowResize(this.onResize, this);
40096     this.cpad = this.el.getPadding("lr");
40097     this.hiddenCount = 0;
40098
40099
40100     // toolbar on the tabbar support...
40101     if (this.toolbar) {
40102         alert("no toolbar support yet");
40103         this.toolbar  = false;
40104         /*
40105         var tcfg = this.toolbar;
40106         tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');  
40107         this.toolbar = new Roo.Toolbar(tcfg);
40108         if (Roo.isSafari) {
40109             var tbl = tcfg.container.child('table', true);
40110             tbl.setAttribute('width', '100%');
40111         }
40112         */
40113         
40114     }
40115    
40116
40117
40118     Roo.bootstrap.panel.Tabs.superclass.constructor.call(this);
40119 };
40120
40121 Roo.extend(Roo.bootstrap.panel.Tabs, Roo.util.Observable, {
40122     /*
40123      *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
40124      */
40125     tabPosition : "top",
40126     /*
40127      *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
40128      */
40129     currentTabWidth : 0,
40130     /*
40131      *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
40132      */
40133     minTabWidth : 40,
40134     /*
40135      *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
40136      */
40137     maxTabWidth : 250,
40138     /*
40139      *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
40140      */
40141     preferredTabWidth : 175,
40142     /*
40143      *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
40144      */
40145     resizeTabs : false,
40146     /*
40147      *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
40148      */
40149     monitorResize : true,
40150     /*
40151      *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar. 
40152      */
40153     toolbar : false,  // set by caller..
40154     
40155     region : false, /// set by caller
40156     
40157     disableTooltips : true, // not used yet...
40158
40159     /**
40160      * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
40161      * @param {String} id The id of the div to use <b>or create</b>
40162      * @param {String} text The text for the tab
40163      * @param {String} content (optional) Content to put in the TabPanelItem body
40164      * @param {Boolean} closable (optional) True to create a close icon on the tab
40165      * @return {Roo.TabPanelItem} The created TabPanelItem
40166      */
40167     addTab : function(id, text, content, closable, tpl)
40168     {
40169         var item = new Roo.bootstrap.panel.TabItem({
40170             panel: this,
40171             id : id,
40172             text : text,
40173             closable : closable,
40174             tpl : tpl
40175         });
40176         this.addTabItem(item);
40177         if(content){
40178             item.setContent(content);
40179         }
40180         return item;
40181     },
40182
40183     /**
40184      * Returns the {@link Roo.TabPanelItem} with the specified id/index
40185      * @param {String/Number} id The id or index of the TabPanelItem to fetch.
40186      * @return {Roo.TabPanelItem}
40187      */
40188     getTab : function(id){
40189         return this.items[id];
40190     },
40191
40192     /**
40193      * Hides the {@link Roo.TabPanelItem} with the specified id/index
40194      * @param {String/Number} id The id or index of the TabPanelItem to hide.
40195      */
40196     hideTab : function(id){
40197         var t = this.items[id];
40198         if(!t.isHidden()){
40199            t.setHidden(true);
40200            this.hiddenCount++;
40201            this.autoSizeTabs();
40202         }
40203     },
40204
40205     /**
40206      * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
40207      * @param {String/Number} id The id or index of the TabPanelItem to unhide.
40208      */
40209     unhideTab : function(id){
40210         var t = this.items[id];
40211         if(t.isHidden()){
40212            t.setHidden(false);
40213            this.hiddenCount--;
40214            this.autoSizeTabs();
40215         }
40216     },
40217
40218     /**
40219      * Adds an existing {@link Roo.TabPanelItem}.
40220      * @param {Roo.TabPanelItem} item The TabPanelItem to add
40221      */
40222     addTabItem : function(item)
40223     {
40224         this.items[item.id] = item;
40225         this.items.push(item);
40226         this.autoSizeTabs();
40227       //  if(this.resizeTabs){
40228     //       item.setWidth(this.currentTabWidth || this.preferredTabWidth);
40229   //         this.autoSizeTabs();
40230 //        }else{
40231 //            item.autoSize();
40232        // }
40233     },
40234
40235     /**
40236      * Removes a {@link Roo.TabPanelItem}.
40237      * @param {String/Number} id The id or index of the TabPanelItem to remove.
40238      */
40239     removeTab : function(id){
40240         var items = this.items;
40241         var tab = items[id];
40242         if(!tab) { return; }
40243         var index = items.indexOf(tab);
40244         if(this.active == tab && items.length > 1){
40245             var newTab = this.getNextAvailable(index);
40246             if(newTab) {
40247                 newTab.activate();
40248             }
40249         }
40250         this.stripEl.dom.removeChild(tab.pnode.dom);
40251         if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
40252             this.bodyEl.dom.removeChild(tab.bodyEl.dom);
40253         }
40254         items.splice(index, 1);
40255         delete this.items[tab.id];
40256         tab.fireEvent("close", tab);
40257         tab.purgeListeners();
40258         this.autoSizeTabs();
40259     },
40260
40261     getNextAvailable : function(start){
40262         var items = this.items;
40263         var index = start;
40264         // look for a next tab that will slide over to
40265         // replace the one being removed
40266         while(index < items.length){
40267             var item = items[++index];
40268             if(item && !item.isHidden()){
40269                 return item;
40270             }
40271         }
40272         // if one isn't found select the previous tab (on the left)
40273         index = start;
40274         while(index >= 0){
40275             var item = items[--index];
40276             if(item && !item.isHidden()){
40277                 return item;
40278             }
40279         }
40280         return null;
40281     },
40282
40283     /**
40284      * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
40285      * @param {String/Number} id The id or index of the TabPanelItem to disable.
40286      */
40287     disableTab : function(id){
40288         var tab = this.items[id];
40289         if(tab && this.active != tab){
40290             tab.disable();
40291         }
40292     },
40293
40294     /**
40295      * Enables a {@link Roo.TabPanelItem} that is disabled.
40296      * @param {String/Number} id The id or index of the TabPanelItem to enable.
40297      */
40298     enableTab : function(id){
40299         var tab = this.items[id];
40300         tab.enable();
40301     },
40302
40303     /**
40304      * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
40305      * @param {String/Number} id The id or index of the TabPanelItem to activate.
40306      * @return {Roo.TabPanelItem} The TabPanelItem.
40307      */
40308     activate : function(id)
40309     {
40310         //Roo.log('activite:'  + id);
40311         
40312         var tab = this.items[id];
40313         if(!tab){
40314             return null;
40315         }
40316         if(tab == this.active || tab.disabled){
40317             return tab;
40318         }
40319         var e = {};
40320         this.fireEvent("beforetabchange", this, e, tab);
40321         if(e.cancel !== true && !tab.disabled){
40322             if(this.active){
40323                 this.active.hide();
40324             }
40325             this.active = this.items[id];
40326             this.active.show();
40327             this.fireEvent("tabchange", this, this.active);
40328         }
40329         return tab;
40330     },
40331
40332     /**
40333      * Gets the active {@link Roo.TabPanelItem}.
40334      * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
40335      */
40336     getActiveTab : function(){
40337         return this.active;
40338     },
40339
40340     /**
40341      * Updates the tab body element to fit the height of the container element
40342      * for overflow scrolling
40343      * @param {Number} targetHeight (optional) Override the starting height from the elements height
40344      */
40345     syncHeight : function(targetHeight){
40346         var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
40347         var bm = this.bodyEl.getMargins();
40348         var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
40349         this.bodyEl.setHeight(newHeight);
40350         return newHeight;
40351     },
40352
40353     onResize : function(){
40354         if(this.monitorResize){
40355             this.autoSizeTabs();
40356         }
40357     },
40358
40359     /**
40360      * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
40361      */
40362     beginUpdate : function(){
40363         this.updating = true;
40364     },
40365
40366     /**
40367      * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
40368      */
40369     endUpdate : function(){
40370         this.updating = false;
40371         this.autoSizeTabs();
40372     },
40373
40374     /**
40375      * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
40376      */
40377     autoSizeTabs : function()
40378     {
40379         var count = this.items.length;
40380         var vcount = count - this.hiddenCount;
40381         
40382         if (vcount < 2) {
40383             this.stripEl.hide();
40384         } else {
40385             this.stripEl.show();
40386         }
40387         
40388         if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) {
40389             return;
40390         }
40391         
40392         
40393         var w = Math.max(this.el.getWidth() - this.cpad, 10);
40394         var availWidth = Math.floor(w / vcount);
40395         var b = this.stripBody;
40396         if(b.getWidth() > w){
40397             var tabs = this.items;
40398             this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
40399             if(availWidth < this.minTabWidth){
40400                 /*if(!this.sleft){    // incomplete scrolling code
40401                     this.createScrollButtons();
40402                 }
40403                 this.showScroll();
40404                 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
40405             }
40406         }else{
40407             if(this.currentTabWidth < this.preferredTabWidth){
40408                 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
40409             }
40410         }
40411     },
40412
40413     /**
40414      * Returns the number of tabs in this TabPanel.
40415      * @return {Number}
40416      */
40417      getCount : function(){
40418          return this.items.length;
40419      },
40420
40421     /**
40422      * Resizes all the tabs to the passed width
40423      * @param {Number} The new width
40424      */
40425     setTabWidth : function(width){
40426         this.currentTabWidth = width;
40427         for(var i = 0, len = this.items.length; i < len; i++) {
40428                 if(!this.items[i].isHidden()) {
40429                 this.items[i].setWidth(width);
40430             }
40431         }
40432     },
40433
40434     /**
40435      * Destroys this TabPanel
40436      * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
40437      */
40438     destroy : function(removeEl){
40439         Roo.EventManager.removeResizeListener(this.onResize, this);
40440         for(var i = 0, len = this.items.length; i < len; i++){
40441             this.items[i].purgeListeners();
40442         }
40443         if(removeEl === true){
40444             this.el.update("");
40445             this.el.remove();
40446         }
40447     },
40448     
40449     createStrip : function(container)
40450     {
40451         var strip = document.createElement("nav");
40452         strip.className = Roo.bootstrap.version == 4 ?
40453             "navbar-light bg-light" : 
40454             "navbar navbar-default"; //"x-tabs-wrap";
40455         container.appendChild(strip);
40456         return strip;
40457     },
40458     
40459     createStripList : function(strip)
40460     {
40461         // div wrapper for retard IE
40462         // returns the "tr" element.
40463         strip.innerHTML = '<ul class="nav nav-tabs" role="tablist"></ul>';
40464         //'<div class="x-tabs-strip-wrap">'+
40465           //  '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
40466           //  '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
40467         return strip.firstChild; //.firstChild.firstChild.firstChild;
40468     },
40469     createBody : function(container)
40470     {
40471         var body = document.createElement("div");
40472         Roo.id(body, "tab-body");
40473         //Roo.fly(body).addClass("x-tabs-body");
40474         Roo.fly(body).addClass("tab-content");
40475         container.appendChild(body);
40476         return body;
40477     },
40478     createItemBody :function(bodyEl, id){
40479         var body = Roo.getDom(id);
40480         if(!body){
40481             body = document.createElement("div");
40482             body.id = id;
40483         }
40484         //Roo.fly(body).addClass("x-tabs-item-body");
40485         Roo.fly(body).addClass("tab-pane");
40486          bodyEl.insertBefore(body, bodyEl.firstChild);
40487         return body;
40488     },
40489     /** @private */
40490     createStripElements :  function(stripEl, text, closable, tpl)
40491     {
40492         var td = document.createElement("li"); // was td..
40493         td.className = 'nav-item';
40494         
40495         //stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
40496         
40497         
40498         stripEl.appendChild(td);
40499         /*if(closable){
40500             td.className = "x-tabs-closable";
40501             if(!this.closeTpl){
40502                 this.closeTpl = new Roo.Template(
40503                    '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
40504                    '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
40505                    '<div unselectable="on" class="close-icon">&#160;</div></em></span></a>'
40506                 );
40507             }
40508             var el = this.closeTpl.overwrite(td, {"text": text});
40509             var close = el.getElementsByTagName("div")[0];
40510             var inner = el.getElementsByTagName("em")[0];
40511             return {"el": el, "close": close, "inner": inner};
40512         } else {
40513         */
40514         // not sure what this is..
40515 //            if(!this.tabTpl){
40516                 //this.tabTpl = new Roo.Template(
40517                 //   '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
40518                 //   '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
40519                 //);
40520 //                this.tabTpl = new Roo.Template(
40521 //                   '<a href="#">' +
40522 //                   '<span unselectable="on"' +
40523 //                            (this.disableTooltips ? '' : ' title="{text}"') +
40524 //                            ' >{text}</span></a>'
40525 //                );
40526 //                
40527 //            }
40528
40529
40530             var template = tpl || this.tabTpl || false;
40531             
40532             if(!template){
40533                 template =  new Roo.Template(
40534                         Roo.bootstrap.version == 4 ? 
40535                             (
40536                                 '<a class="nav-link" href="#" unselectable="on"' +
40537                                      (this.disableTooltips ? '' : ' title="{text}"') +
40538                                      ' >{text}</a>'
40539                             ) : (
40540                                 '<a class="nav-link" href="#">' +
40541                                 '<span unselectable="on"' +
40542                                          (this.disableTooltips ? '' : ' title="{text}"') +
40543                                     ' >{text}</span></a>'
40544                             )
40545                 );
40546             }
40547             
40548             switch (typeof(template)) {
40549                 case 'object' :
40550                     break;
40551                 case 'string' :
40552                     template = new Roo.Template(template);
40553                     break;
40554                 default :
40555                     break;
40556             }
40557             
40558             var el = template.overwrite(td, {"text": text});
40559             
40560             var inner = el.getElementsByTagName("span")[0];
40561             
40562             return {"el": el, "inner": inner};
40563             
40564     }
40565         
40566     
40567 });
40568
40569 /**
40570  * @class Roo.TabPanelItem
40571  * @extends Roo.util.Observable
40572  * Represents an individual item (tab plus body) in a TabPanel.
40573  * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
40574  * @param {String} id The id of this TabPanelItem
40575  * @param {String} text The text for the tab of this TabPanelItem
40576  * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
40577  */
40578 Roo.bootstrap.panel.TabItem = function(config){
40579     /**
40580      * The {@link Roo.TabPanel} this TabPanelItem belongs to
40581      * @type Roo.TabPanel
40582      */
40583     this.tabPanel = config.panel;
40584     /**
40585      * The id for this TabPanelItem
40586      * @type String
40587      */
40588     this.id = config.id;
40589     /** @private */
40590     this.disabled = false;
40591     /** @private */
40592     this.text = config.text;
40593     /** @private */
40594     this.loaded = false;
40595     this.closable = config.closable;
40596
40597     /**
40598      * The body element for this TabPanelItem.
40599      * @type Roo.Element
40600      */
40601     this.bodyEl = Roo.get(this.tabPanel.createItemBody(this.tabPanel.bodyEl.dom, config.id));
40602     this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
40603     this.bodyEl.setStyle("display", "block");
40604     this.bodyEl.setStyle("zoom", "1");
40605     //this.hideAction();
40606
40607     var els = this.tabPanel.createStripElements(this.tabPanel.stripEl.dom, config.text, config.closable, config.tpl);
40608     /** @private */
40609     this.el = Roo.get(els.el);
40610     this.inner = Roo.get(els.inner, true);
40611      this.textEl = Roo.bootstrap.version == 4 ?
40612         this.el : Roo.get(this.el.dom.firstChild, true);
40613
40614     this.pnode = this.linode = Roo.get(els.el.parentNode, true);
40615     this.status_node = Roo.bootstrap.version == 4 ? this.el : this.linode;
40616
40617     
40618 //    this.el.on("mousedown", this.onTabMouseDown, this);
40619     this.el.on("click", this.onTabClick, this);
40620     /** @private */
40621     if(config.closable){
40622         var c = Roo.get(els.close, true);
40623         c.dom.title = this.closeText;
40624         c.addClassOnOver("close-over");
40625         c.on("click", this.closeClick, this);
40626      }
40627
40628     this.addEvents({
40629          /**
40630          * @event activate
40631          * Fires when this tab becomes the active tab.
40632          * @param {Roo.TabPanel} tabPanel The parent TabPanel
40633          * @param {Roo.TabPanelItem} this
40634          */
40635         "activate": true,
40636         /**
40637          * @event beforeclose
40638          * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
40639          * @param {Roo.TabPanelItem} this
40640          * @param {Object} e Set cancel to true on this object to cancel the close.
40641          */
40642         "beforeclose": true,
40643         /**
40644          * @event close
40645          * Fires when this tab is closed.
40646          * @param {Roo.TabPanelItem} this
40647          */
40648          "close": true,
40649         /**
40650          * @event deactivate
40651          * Fires when this tab is no longer the active tab.
40652          * @param {Roo.TabPanel} tabPanel The parent TabPanel
40653          * @param {Roo.TabPanelItem} this
40654          */
40655          "deactivate" : true
40656     });
40657     this.hidden = false;
40658
40659     Roo.bootstrap.panel.TabItem.superclass.constructor.call(this);
40660 };
40661
40662 Roo.extend(Roo.bootstrap.panel.TabItem, Roo.util.Observable,
40663            {
40664     purgeListeners : function(){
40665        Roo.util.Observable.prototype.purgeListeners.call(this);
40666        this.el.removeAllListeners();
40667     },
40668     /**
40669      * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
40670      */
40671     show : function(){
40672         this.status_node.addClass("active");
40673         this.showAction();
40674         if(Roo.isOpera){
40675             this.tabPanel.stripWrap.repaint();
40676         }
40677         this.fireEvent("activate", this.tabPanel, this);
40678     },
40679
40680     /**
40681      * Returns true if this tab is the active tab.
40682      * @return {Boolean}
40683      */
40684     isActive : function(){
40685         return this.tabPanel.getActiveTab() == this;
40686     },
40687
40688     /**
40689      * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
40690      */
40691     hide : function(){
40692         this.status_node.removeClass("active");
40693         this.hideAction();
40694         this.fireEvent("deactivate", this.tabPanel, this);
40695     },
40696
40697     hideAction : function(){
40698         this.bodyEl.hide();
40699         this.bodyEl.setStyle("position", "absolute");
40700         this.bodyEl.setLeft("-20000px");
40701         this.bodyEl.setTop("-20000px");
40702     },
40703
40704     showAction : function(){
40705         this.bodyEl.setStyle("position", "relative");
40706         this.bodyEl.setTop("");
40707         this.bodyEl.setLeft("");
40708         this.bodyEl.show();
40709     },
40710
40711     /**
40712      * Set the tooltip for the tab.
40713      * @param {String} tooltip The tab's tooltip
40714      */
40715     setTooltip : function(text){
40716         if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
40717             this.textEl.dom.qtip = text;
40718             this.textEl.dom.removeAttribute('title');
40719         }else{
40720             this.textEl.dom.title = text;
40721         }
40722     },
40723
40724     onTabClick : function(e){
40725         e.preventDefault();
40726         this.tabPanel.activate(this.id);
40727     },
40728
40729     onTabMouseDown : function(e){
40730         e.preventDefault();
40731         this.tabPanel.activate(this.id);
40732     },
40733 /*
40734     getWidth : function(){
40735         return this.inner.getWidth();
40736     },
40737
40738     setWidth : function(width){
40739         var iwidth = width - this.linode.getPadding("lr");
40740         this.inner.setWidth(iwidth);
40741         this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
40742         this.linode.setWidth(width);
40743     },
40744 */
40745     /**
40746      * Show or hide the tab
40747      * @param {Boolean} hidden True to hide or false to show.
40748      */
40749     setHidden : function(hidden){
40750         this.hidden = hidden;
40751         this.linode.setStyle("display", hidden ? "none" : "");
40752     },
40753
40754     /**
40755      * Returns true if this tab is "hidden"
40756      * @return {Boolean}
40757      */
40758     isHidden : function(){
40759         return this.hidden;
40760     },
40761
40762     /**
40763      * Returns the text for this tab
40764      * @return {String}
40765      */
40766     getText : function(){
40767         return this.text;
40768     },
40769     /*
40770     autoSize : function(){
40771         //this.el.beginMeasure();
40772         this.textEl.setWidth(1);
40773         /*
40774          *  #2804 [new] Tabs in Roojs
40775          *  increase the width by 2-4 pixels to prevent the ellipssis showing in chrome
40776          */
40777         //this.setWidth(this.textEl.dom.scrollWidth+this.linode.getPadding("lr")+this.inner.getPadding("lr") + 2);
40778         //this.el.endMeasure();
40779     //},
40780
40781     /**
40782      * Sets the text for the tab (Note: this also sets the tooltip text)
40783      * @param {String} text The tab's text and tooltip
40784      */
40785     setText : function(text){
40786         this.text = text;
40787         this.textEl.update(text);
40788         this.setTooltip(text);
40789         //if(!this.tabPanel.resizeTabs){
40790         //    this.autoSize();
40791         //}
40792     },
40793     /**
40794      * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
40795      */
40796     activate : function(){
40797         this.tabPanel.activate(this.id);
40798     },
40799
40800     /**
40801      * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
40802      */
40803     disable : function(){
40804         if(this.tabPanel.active != this){
40805             this.disabled = true;
40806             this.status_node.addClass("disabled");
40807         }
40808     },
40809
40810     /**
40811      * Enables this TabPanelItem if it was previously disabled.
40812      */
40813     enable : function(){
40814         this.disabled = false;
40815         this.status_node.removeClass("disabled");
40816     },
40817
40818     /**
40819      * Sets the content for this TabPanelItem.
40820      * @param {String} content The content
40821      * @param {Boolean} loadScripts true to look for and load scripts
40822      */
40823     setContent : function(content, loadScripts){
40824         this.bodyEl.update(content, loadScripts);
40825     },
40826
40827     /**
40828      * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
40829      * @return {Roo.UpdateManager} The UpdateManager
40830      */
40831     getUpdateManager : function(){
40832         return this.bodyEl.getUpdateManager();
40833     },
40834
40835     /**
40836      * Set a URL to be used to load the content for this TabPanelItem.
40837      * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
40838      * @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)
40839      * @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)
40840      * @return {Roo.UpdateManager} The UpdateManager
40841      */
40842     setUrl : function(url, params, loadOnce){
40843         if(this.refreshDelegate){
40844             this.un('activate', this.refreshDelegate);
40845         }
40846         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
40847         this.on("activate", this.refreshDelegate);
40848         return this.bodyEl.getUpdateManager();
40849     },
40850
40851     /** @private */
40852     _handleRefresh : function(url, params, loadOnce){
40853         if(!loadOnce || !this.loaded){
40854             var updater = this.bodyEl.getUpdateManager();
40855             updater.update(url, params, this._setLoaded.createDelegate(this));
40856         }
40857     },
40858
40859     /**
40860      *   Forces a content refresh from the URL specified in the {@link #setUrl} method.
40861      *   Will fail silently if the setUrl method has not been called.
40862      *   This does not activate the panel, just updates its content.
40863      */
40864     refresh : function(){
40865         if(this.refreshDelegate){
40866            this.loaded = false;
40867            this.refreshDelegate();
40868         }
40869     },
40870
40871     /** @private */
40872     _setLoaded : function(){
40873         this.loaded = true;
40874     },
40875
40876     /** @private */
40877     closeClick : function(e){
40878         var o = {};
40879         e.stopEvent();
40880         this.fireEvent("beforeclose", this, o);
40881         if(o.cancel !== true){
40882             this.tabPanel.removeTab(this.id);
40883         }
40884     },
40885     /**
40886      * The text displayed in the tooltip for the close icon.
40887      * @type String
40888      */
40889     closeText : "Close this tab"
40890 });
40891 /**
40892 *    This script refer to:
40893 *    Title: International Telephone Input
40894 *    Author: Jack O'Connor
40895 *    Code version:  v12.1.12
40896 *    Availability: https://github.com/jackocnr/intl-tel-input.git
40897 **/
40898
40899 Roo.bootstrap.PhoneInputData = function() {
40900     var d = [
40901       [
40902         "Afghanistan (‫افغانستان‬‎)",
40903         "af",
40904         "93"
40905       ],
40906       [
40907         "Albania (Shqipëri)",
40908         "al",
40909         "355"
40910       ],
40911       [
40912         "Algeria (‫الجزائر‬‎)",
40913         "dz",
40914         "213"
40915       ],
40916       [
40917         "American Samoa",
40918         "as",
40919         "1684"
40920       ],
40921       [
40922         "Andorra",
40923         "ad",
40924         "376"
40925       ],
40926       [
40927         "Angola",
40928         "ao",
40929         "244"
40930       ],
40931       [
40932         "Anguilla",
40933         "ai",
40934         "1264"
40935       ],
40936       [
40937         "Antigua and Barbuda",
40938         "ag",
40939         "1268"
40940       ],
40941       [
40942         "Argentina",
40943         "ar",
40944         "54"
40945       ],
40946       [
40947         "Armenia (Հայաստան)",
40948         "am",
40949         "374"
40950       ],
40951       [
40952         "Aruba",
40953         "aw",
40954         "297"
40955       ],
40956       [
40957         "Australia",
40958         "au",
40959         "61",
40960         0
40961       ],
40962       [
40963         "Austria (Österreich)",
40964         "at",
40965         "43"
40966       ],
40967       [
40968         "Azerbaijan (Azərbaycan)",
40969         "az",
40970         "994"
40971       ],
40972       [
40973         "Bahamas",
40974         "bs",
40975         "1242"
40976       ],
40977       [
40978         "Bahrain (‫البحرين‬‎)",
40979         "bh",
40980         "973"
40981       ],
40982       [
40983         "Bangladesh (বাংলাদেশ)",
40984         "bd",
40985         "880"
40986       ],
40987       [
40988         "Barbados",
40989         "bb",
40990         "1246"
40991       ],
40992       [
40993         "Belarus (Беларусь)",
40994         "by",
40995         "375"
40996       ],
40997       [
40998         "Belgium (België)",
40999         "be",
41000         "32"
41001       ],
41002       [
41003         "Belize",
41004         "bz",
41005         "501"
41006       ],
41007       [
41008         "Benin (Bénin)",
41009         "bj",
41010         "229"
41011       ],
41012       [
41013         "Bermuda",
41014         "bm",
41015         "1441"
41016       ],
41017       [
41018         "Bhutan (འབྲུག)",
41019         "bt",
41020         "975"
41021       ],
41022       [
41023         "Bolivia",
41024         "bo",
41025         "591"
41026       ],
41027       [
41028         "Bosnia and Herzegovina (Босна и Херцеговина)",
41029         "ba",
41030         "387"
41031       ],
41032       [
41033         "Botswana",
41034         "bw",
41035         "267"
41036       ],
41037       [
41038         "Brazil (Brasil)",
41039         "br",
41040         "55"
41041       ],
41042       [
41043         "British Indian Ocean Territory",
41044         "io",
41045         "246"
41046       ],
41047       [
41048         "British Virgin Islands",
41049         "vg",
41050         "1284"
41051       ],
41052       [
41053         "Brunei",
41054         "bn",
41055         "673"
41056       ],
41057       [
41058         "Bulgaria (България)",
41059         "bg",
41060         "359"
41061       ],
41062       [
41063         "Burkina Faso",
41064         "bf",
41065         "226"
41066       ],
41067       [
41068         "Burundi (Uburundi)",
41069         "bi",
41070         "257"
41071       ],
41072       [
41073         "Cambodia (កម្ពុជា)",
41074         "kh",
41075         "855"
41076       ],
41077       [
41078         "Cameroon (Cameroun)",
41079         "cm",
41080         "237"
41081       ],
41082       [
41083         "Canada",
41084         "ca",
41085         "1",
41086         1,
41087         ["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"]
41088       ],
41089       [
41090         "Cape Verde (Kabu Verdi)",
41091         "cv",
41092         "238"
41093       ],
41094       [
41095         "Caribbean Netherlands",
41096         "bq",
41097         "599",
41098         1
41099       ],
41100       [
41101         "Cayman Islands",
41102         "ky",
41103         "1345"
41104       ],
41105       [
41106         "Central African Republic (République centrafricaine)",
41107         "cf",
41108         "236"
41109       ],
41110       [
41111         "Chad (Tchad)",
41112         "td",
41113         "235"
41114       ],
41115       [
41116         "Chile",
41117         "cl",
41118         "56"
41119       ],
41120       [
41121         "China (中国)",
41122         "cn",
41123         "86"
41124       ],
41125       [
41126         "Christmas Island",
41127         "cx",
41128         "61",
41129         2
41130       ],
41131       [
41132         "Cocos (Keeling) Islands",
41133         "cc",
41134         "61",
41135         1
41136       ],
41137       [
41138         "Colombia",
41139         "co",
41140         "57"
41141       ],
41142       [
41143         "Comoros (‫جزر القمر‬‎)",
41144         "km",
41145         "269"
41146       ],
41147       [
41148         "Congo (DRC) (Jamhuri ya Kidemokrasia ya Kongo)",
41149         "cd",
41150         "243"
41151       ],
41152       [
41153         "Congo (Republic) (Congo-Brazzaville)",
41154         "cg",
41155         "242"
41156       ],
41157       [
41158         "Cook Islands",
41159         "ck",
41160         "682"
41161       ],
41162       [
41163         "Costa Rica",
41164         "cr",
41165         "506"
41166       ],
41167       [
41168         "Côte d’Ivoire",
41169         "ci",
41170         "225"
41171       ],
41172       [
41173         "Croatia (Hrvatska)",
41174         "hr",
41175         "385"
41176       ],
41177       [
41178         "Cuba",
41179         "cu",
41180         "53"
41181       ],
41182       [
41183         "Curaçao",
41184         "cw",
41185         "599",
41186         0
41187       ],
41188       [
41189         "Cyprus (Κύπρος)",
41190         "cy",
41191         "357"
41192       ],
41193       [
41194         "Czech Republic (Česká republika)",
41195         "cz",
41196         "420"
41197       ],
41198       [
41199         "Denmark (Danmark)",
41200         "dk",
41201         "45"
41202       ],
41203       [
41204         "Djibouti",
41205         "dj",
41206         "253"
41207       ],
41208       [
41209         "Dominica",
41210         "dm",
41211         "1767"
41212       ],
41213       [
41214         "Dominican Republic (República Dominicana)",
41215         "do",
41216         "1",
41217         2,
41218         ["809", "829", "849"]
41219       ],
41220       [
41221         "Ecuador",
41222         "ec",
41223         "593"
41224       ],
41225       [
41226         "Egypt (‫مصر‬‎)",
41227         "eg",
41228         "20"
41229       ],
41230       [
41231         "El Salvador",
41232         "sv",
41233         "503"
41234       ],
41235       [
41236         "Equatorial Guinea (Guinea Ecuatorial)",
41237         "gq",
41238         "240"
41239       ],
41240       [
41241         "Eritrea",
41242         "er",
41243         "291"
41244       ],
41245       [
41246         "Estonia (Eesti)",
41247         "ee",
41248         "372"
41249       ],
41250       [
41251         "Ethiopia",
41252         "et",
41253         "251"
41254       ],
41255       [
41256         "Falkland Islands (Islas Malvinas)",
41257         "fk",
41258         "500"
41259       ],
41260       [
41261         "Faroe Islands (Føroyar)",
41262         "fo",
41263         "298"
41264       ],
41265       [
41266         "Fiji",
41267         "fj",
41268         "679"
41269       ],
41270       [
41271         "Finland (Suomi)",
41272         "fi",
41273         "358",
41274         0
41275       ],
41276       [
41277         "France",
41278         "fr",
41279         "33"
41280       ],
41281       [
41282         "French Guiana (Guyane française)",
41283         "gf",
41284         "594"
41285       ],
41286       [
41287         "French Polynesia (Polynésie française)",
41288         "pf",
41289         "689"
41290       ],
41291       [
41292         "Gabon",
41293         "ga",
41294         "241"
41295       ],
41296       [
41297         "Gambia",
41298         "gm",
41299         "220"
41300       ],
41301       [
41302         "Georgia (საქართველო)",
41303         "ge",
41304         "995"
41305       ],
41306       [
41307         "Germany (Deutschland)",
41308         "de",
41309         "49"
41310       ],
41311       [
41312         "Ghana (Gaana)",
41313         "gh",
41314         "233"
41315       ],
41316       [
41317         "Gibraltar",
41318         "gi",
41319         "350"
41320       ],
41321       [
41322         "Greece (Ελλάδα)",
41323         "gr",
41324         "30"
41325       ],
41326       [
41327         "Greenland (Kalaallit Nunaat)",
41328         "gl",
41329         "299"
41330       ],
41331       [
41332         "Grenada",
41333         "gd",
41334         "1473"
41335       ],
41336       [
41337         "Guadeloupe",
41338         "gp",
41339         "590",
41340         0
41341       ],
41342       [
41343         "Guam",
41344         "gu",
41345         "1671"
41346       ],
41347       [
41348         "Guatemala",
41349         "gt",
41350         "502"
41351       ],
41352       [
41353         "Guernsey",
41354         "gg",
41355         "44",
41356         1
41357       ],
41358       [
41359         "Guinea (Guinée)",
41360         "gn",
41361         "224"
41362       ],
41363       [
41364         "Guinea-Bissau (Guiné Bissau)",
41365         "gw",
41366         "245"
41367       ],
41368       [
41369         "Guyana",
41370         "gy",
41371         "592"
41372       ],
41373       [
41374         "Haiti",
41375         "ht",
41376         "509"
41377       ],
41378       [
41379         "Honduras",
41380         "hn",
41381         "504"
41382       ],
41383       [
41384         "Hong Kong (香港)",
41385         "hk",
41386         "852"
41387       ],
41388       [
41389         "Hungary (Magyarország)",
41390         "hu",
41391         "36"
41392       ],
41393       [
41394         "Iceland (Ísland)",
41395         "is",
41396         "354"
41397       ],
41398       [
41399         "India (भारत)",
41400         "in",
41401         "91"
41402       ],
41403       [
41404         "Indonesia",
41405         "id",
41406         "62"
41407       ],
41408       [
41409         "Iran (‫ایران‬‎)",
41410         "ir",
41411         "98"
41412       ],
41413       [
41414         "Iraq (‫العراق‬‎)",
41415         "iq",
41416         "964"
41417       ],
41418       [
41419         "Ireland",
41420         "ie",
41421         "353"
41422       ],
41423       [
41424         "Isle of Man",
41425         "im",
41426         "44",
41427         2
41428       ],
41429       [
41430         "Israel (‫ישראל‬‎)",
41431         "il",
41432         "972"
41433       ],
41434       [
41435         "Italy (Italia)",
41436         "it",
41437         "39",
41438         0
41439       ],
41440       [
41441         "Jamaica",
41442         "jm",
41443         "1876"
41444       ],
41445       [
41446         "Japan (日本)",
41447         "jp",
41448         "81"
41449       ],
41450       [
41451         "Jersey",
41452         "je",
41453         "44",
41454         3
41455       ],
41456       [
41457         "Jordan (‫الأردن‬‎)",
41458         "jo",
41459         "962"
41460       ],
41461       [
41462         "Kazakhstan (Казахстан)",
41463         "kz",
41464         "7",
41465         1
41466       ],
41467       [
41468         "Kenya",
41469         "ke",
41470         "254"
41471       ],
41472       [
41473         "Kiribati",
41474         "ki",
41475         "686"
41476       ],
41477       [
41478         "Kosovo",
41479         "xk",
41480         "383"
41481       ],
41482       [
41483         "Kuwait (‫الكويت‬‎)",
41484         "kw",
41485         "965"
41486       ],
41487       [
41488         "Kyrgyzstan (Кыргызстан)",
41489         "kg",
41490         "996"
41491       ],
41492       [
41493         "Laos (ລາວ)",
41494         "la",
41495         "856"
41496       ],
41497       [
41498         "Latvia (Latvija)",
41499         "lv",
41500         "371"
41501       ],
41502       [
41503         "Lebanon (‫لبنان‬‎)",
41504         "lb",
41505         "961"
41506       ],
41507       [
41508         "Lesotho",
41509         "ls",
41510         "266"
41511       ],
41512       [
41513         "Liberia",
41514         "lr",
41515         "231"
41516       ],
41517       [
41518         "Libya (‫ليبيا‬‎)",
41519         "ly",
41520         "218"
41521       ],
41522       [
41523         "Liechtenstein",
41524         "li",
41525         "423"
41526       ],
41527       [
41528         "Lithuania (Lietuva)",
41529         "lt",
41530         "370"
41531       ],
41532       [
41533         "Luxembourg",
41534         "lu",
41535         "352"
41536       ],
41537       [
41538         "Macau (澳門)",
41539         "mo",
41540         "853"
41541       ],
41542       [
41543         "Macedonia (FYROM) (Македонија)",
41544         "mk",
41545         "389"
41546       ],
41547       [
41548         "Madagascar (Madagasikara)",
41549         "mg",
41550         "261"
41551       ],
41552       [
41553         "Malawi",
41554         "mw",
41555         "265"
41556       ],
41557       [
41558         "Malaysia",
41559         "my",
41560         "60"
41561       ],
41562       [
41563         "Maldives",
41564         "mv",
41565         "960"
41566       ],
41567       [
41568         "Mali",
41569         "ml",
41570         "223"
41571       ],
41572       [
41573         "Malta",
41574         "mt",
41575         "356"
41576       ],
41577       [
41578         "Marshall Islands",
41579         "mh",
41580         "692"
41581       ],
41582       [
41583         "Martinique",
41584         "mq",
41585         "596"
41586       ],
41587       [
41588         "Mauritania (‫موريتانيا‬‎)",
41589         "mr",
41590         "222"
41591       ],
41592       [
41593         "Mauritius (Moris)",
41594         "mu",
41595         "230"
41596       ],
41597       [
41598         "Mayotte",
41599         "yt",
41600         "262",
41601         1
41602       ],
41603       [
41604         "Mexico (México)",
41605         "mx",
41606         "52"
41607       ],
41608       [
41609         "Micronesia",
41610         "fm",
41611         "691"
41612       ],
41613       [
41614         "Moldova (Republica Moldova)",
41615         "md",
41616         "373"
41617       ],
41618       [
41619         "Monaco",
41620         "mc",
41621         "377"
41622       ],
41623       [
41624         "Mongolia (Монгол)",
41625         "mn",
41626         "976"
41627       ],
41628       [
41629         "Montenegro (Crna Gora)",
41630         "me",
41631         "382"
41632       ],
41633       [
41634         "Montserrat",
41635         "ms",
41636         "1664"
41637       ],
41638       [
41639         "Morocco (‫المغرب‬‎)",
41640         "ma",
41641         "212",
41642         0
41643       ],
41644       [
41645         "Mozambique (Moçambique)",
41646         "mz",
41647         "258"
41648       ],
41649       [
41650         "Myanmar (Burma) (မြန်မာ)",
41651         "mm",
41652         "95"
41653       ],
41654       [
41655         "Namibia (Namibië)",
41656         "na",
41657         "264"
41658       ],
41659       [
41660         "Nauru",
41661         "nr",
41662         "674"
41663       ],
41664       [
41665         "Nepal (नेपाल)",
41666         "np",
41667         "977"
41668       ],
41669       [
41670         "Netherlands (Nederland)",
41671         "nl",
41672         "31"
41673       ],
41674       [
41675         "New Caledonia (Nouvelle-Calédonie)",
41676         "nc",
41677         "687"
41678       ],
41679       [
41680         "New Zealand",
41681         "nz",
41682         "64"
41683       ],
41684       [
41685         "Nicaragua",
41686         "ni",
41687         "505"
41688       ],
41689       [
41690         "Niger (Nijar)",
41691         "ne",
41692         "227"
41693       ],
41694       [
41695         "Nigeria",
41696         "ng",
41697         "234"
41698       ],
41699       [
41700         "Niue",
41701         "nu",
41702         "683"
41703       ],
41704       [
41705         "Norfolk Island",
41706         "nf",
41707         "672"
41708       ],
41709       [
41710         "North Korea (조선 민주주의 인민 공화국)",
41711         "kp",
41712         "850"
41713       ],
41714       [
41715         "Northern Mariana Islands",
41716         "mp",
41717         "1670"
41718       ],
41719       [
41720         "Norway (Norge)",
41721         "no",
41722         "47",
41723         0
41724       ],
41725       [
41726         "Oman (‫عُمان‬‎)",
41727         "om",
41728         "968"
41729       ],
41730       [
41731         "Pakistan (‫پاکستان‬‎)",
41732         "pk",
41733         "92"
41734       ],
41735       [
41736         "Palau",
41737         "pw",
41738         "680"
41739       ],
41740       [
41741         "Palestine (‫فلسطين‬‎)",
41742         "ps",
41743         "970"
41744       ],
41745       [
41746         "Panama (Panamá)",
41747         "pa",
41748         "507"
41749       ],
41750       [
41751         "Papua New Guinea",
41752         "pg",
41753         "675"
41754       ],
41755       [
41756         "Paraguay",
41757         "py",
41758         "595"
41759       ],
41760       [
41761         "Peru (Perú)",
41762         "pe",
41763         "51"
41764       ],
41765       [
41766         "Philippines",
41767         "ph",
41768         "63"
41769       ],
41770       [
41771         "Poland (Polska)",
41772         "pl",
41773         "48"
41774       ],
41775       [
41776         "Portugal",
41777         "pt",
41778         "351"
41779       ],
41780       [
41781         "Puerto Rico",
41782         "pr",
41783         "1",
41784         3,
41785         ["787", "939"]
41786       ],
41787       [
41788         "Qatar (‫قطر‬‎)",
41789         "qa",
41790         "974"
41791       ],
41792       [
41793         "Réunion (La Réunion)",
41794         "re",
41795         "262",
41796         0
41797       ],
41798       [
41799         "Romania (România)",
41800         "ro",
41801         "40"
41802       ],
41803       [
41804         "Russia (Россия)",
41805         "ru",
41806         "7",
41807         0
41808       ],
41809       [
41810         "Rwanda",
41811         "rw",
41812         "250"
41813       ],
41814       [
41815         "Saint Barthélemy",
41816         "bl",
41817         "590",
41818         1
41819       ],
41820       [
41821         "Saint Helena",
41822         "sh",
41823         "290"
41824       ],
41825       [
41826         "Saint Kitts and Nevis",
41827         "kn",
41828         "1869"
41829       ],
41830       [
41831         "Saint Lucia",
41832         "lc",
41833         "1758"
41834       ],
41835       [
41836         "Saint Martin (Saint-Martin (partie française))",
41837         "mf",
41838         "590",
41839         2
41840       ],
41841       [
41842         "Saint Pierre and Miquelon (Saint-Pierre-et-Miquelon)",
41843         "pm",
41844         "508"
41845       ],
41846       [
41847         "Saint Vincent and the Grenadines",
41848         "vc",
41849         "1784"
41850       ],
41851       [
41852         "Samoa",
41853         "ws",
41854         "685"
41855       ],
41856       [
41857         "San Marino",
41858         "sm",
41859         "378"
41860       ],
41861       [
41862         "São Tomé and Príncipe (São Tomé e Príncipe)",
41863         "st",
41864         "239"
41865       ],
41866       [
41867         "Saudi Arabia (‫المملكة العربية السعودية‬‎)",
41868         "sa",
41869         "966"
41870       ],
41871       [
41872         "Senegal (Sénégal)",
41873         "sn",
41874         "221"
41875       ],
41876       [
41877         "Serbia (Србија)",
41878         "rs",
41879         "381"
41880       ],
41881       [
41882         "Seychelles",
41883         "sc",
41884         "248"
41885       ],
41886       [
41887         "Sierra Leone",
41888         "sl",
41889         "232"
41890       ],
41891       [
41892         "Singapore",
41893         "sg",
41894         "65"
41895       ],
41896       [
41897         "Sint Maarten",
41898         "sx",
41899         "1721"
41900       ],
41901       [
41902         "Slovakia (Slovensko)",
41903         "sk",
41904         "421"
41905       ],
41906       [
41907         "Slovenia (Slovenija)",
41908         "si",
41909         "386"
41910       ],
41911       [
41912         "Solomon Islands",
41913         "sb",
41914         "677"
41915       ],
41916       [
41917         "Somalia (Soomaaliya)",
41918         "so",
41919         "252"
41920       ],
41921       [
41922         "South Africa",
41923         "za",
41924         "27"
41925       ],
41926       [
41927         "South Korea (대한민국)",
41928         "kr",
41929         "82"
41930       ],
41931       [
41932         "South Sudan (‫جنوب السودان‬‎)",
41933         "ss",
41934         "211"
41935       ],
41936       [
41937         "Spain (España)",
41938         "es",
41939         "34"
41940       ],
41941       [
41942         "Sri Lanka (ශ්‍රී ලංකාව)",
41943         "lk",
41944         "94"
41945       ],
41946       [
41947         "Sudan (‫السودان‬‎)",
41948         "sd",
41949         "249"
41950       ],
41951       [
41952         "Suriname",
41953         "sr",
41954         "597"
41955       ],
41956       [
41957         "Svalbard and Jan Mayen",
41958         "sj",
41959         "47",
41960         1
41961       ],
41962       [
41963         "Swaziland",
41964         "sz",
41965         "268"
41966       ],
41967       [
41968         "Sweden (Sverige)",
41969         "se",
41970         "46"
41971       ],
41972       [
41973         "Switzerland (Schweiz)",
41974         "ch",
41975         "41"
41976       ],
41977       [
41978         "Syria (‫سوريا‬‎)",
41979         "sy",
41980         "963"
41981       ],
41982       [
41983         "Taiwan (台灣)",
41984         "tw",
41985         "886"
41986       ],
41987       [
41988         "Tajikistan",
41989         "tj",
41990         "992"
41991       ],
41992       [
41993         "Tanzania",
41994         "tz",
41995         "255"
41996       ],
41997       [
41998         "Thailand (ไทย)",
41999         "th",
42000         "66"
42001       ],
42002       [
42003         "Timor-Leste",
42004         "tl",
42005         "670"
42006       ],
42007       [
42008         "Togo",
42009         "tg",
42010         "228"
42011       ],
42012       [
42013         "Tokelau",
42014         "tk",
42015         "690"
42016       ],
42017       [
42018         "Tonga",
42019         "to",
42020         "676"
42021       ],
42022       [
42023         "Trinidad and Tobago",
42024         "tt",
42025         "1868"
42026       ],
42027       [
42028         "Tunisia (‫تونس‬‎)",
42029         "tn",
42030         "216"
42031       ],
42032       [
42033         "Turkey (Türkiye)",
42034         "tr",
42035         "90"
42036       ],
42037       [
42038         "Turkmenistan",
42039         "tm",
42040         "993"
42041       ],
42042       [
42043         "Turks and Caicos Islands",
42044         "tc",
42045         "1649"
42046       ],
42047       [
42048         "Tuvalu",
42049         "tv",
42050         "688"
42051       ],
42052       [
42053         "U.S. Virgin Islands",
42054         "vi",
42055         "1340"
42056       ],
42057       [
42058         "Uganda",
42059         "ug",
42060         "256"
42061       ],
42062       [
42063         "Ukraine (Україна)",
42064         "ua",
42065         "380"
42066       ],
42067       [
42068         "United Arab Emirates (‫الإمارات العربية المتحدة‬‎)",
42069         "ae",
42070         "971"
42071       ],
42072       [
42073         "United Kingdom",
42074         "gb",
42075         "44",
42076         0
42077       ],
42078       [
42079         "United States",
42080         "us",
42081         "1",
42082         0
42083       ],
42084       [
42085         "Uruguay",
42086         "uy",
42087         "598"
42088       ],
42089       [
42090         "Uzbekistan (Oʻzbekiston)",
42091         "uz",
42092         "998"
42093       ],
42094       [
42095         "Vanuatu",
42096         "vu",
42097         "678"
42098       ],
42099       [
42100         "Vatican City (Città del Vaticano)",
42101         "va",
42102         "39",
42103         1
42104       ],
42105       [
42106         "Venezuela",
42107         "ve",
42108         "58"
42109       ],
42110       [
42111         "Vietnam (Việt Nam)",
42112         "vn",
42113         "84"
42114       ],
42115       [
42116         "Wallis and Futuna (Wallis-et-Futuna)",
42117         "wf",
42118         "681"
42119       ],
42120       [
42121         "Western Sahara (‫الصحراء الغربية‬‎)",
42122         "eh",
42123         "212",
42124         1
42125       ],
42126       [
42127         "Yemen (‫اليمن‬‎)",
42128         "ye",
42129         "967"
42130       ],
42131       [
42132         "Zambia",
42133         "zm",
42134         "260"
42135       ],
42136       [
42137         "Zimbabwe",
42138         "zw",
42139         "263"
42140       ],
42141       [
42142         "Åland Islands",
42143         "ax",
42144         "358",
42145         1
42146       ]
42147   ];
42148   
42149   return d;
42150 }/**
42151 *    This script refer to:
42152 *    Title: International Telephone Input
42153 *    Author: Jack O'Connor
42154 *    Code version:  v12.1.12
42155 *    Availability: https://github.com/jackocnr/intl-tel-input.git
42156 **/
42157
42158 /**
42159  * @class Roo.bootstrap.PhoneInput
42160  * @extends Roo.bootstrap.TriggerField
42161  * An input with International dial-code selection
42162  
42163  * @cfg {String} defaultDialCode default '+852'
42164  * @cfg {Array} preferedCountries default []
42165   
42166  * @constructor
42167  * Create a new PhoneInput.
42168  * @param {Object} config Configuration options
42169  */
42170
42171 Roo.bootstrap.PhoneInput = function(config) {
42172     Roo.bootstrap.PhoneInput.superclass.constructor.call(this, config);
42173 };
42174
42175 Roo.extend(Roo.bootstrap.PhoneInput, Roo.bootstrap.TriggerField, {
42176         
42177         listWidth: undefined,
42178         
42179         selectedClass: 'active',
42180         
42181         invalidClass : "has-warning",
42182         
42183         validClass: 'has-success',
42184         
42185         allowed: '0123456789',
42186         
42187         max_length: 15,
42188         
42189         /**
42190          * @cfg {String} defaultDialCode The default dial code when initializing the input
42191          */
42192         defaultDialCode: '+852',
42193         
42194         /**
42195          * @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
42196          */
42197         preferedCountries: false,
42198         
42199         getAutoCreate : function()
42200         {
42201             var data = Roo.bootstrap.PhoneInputData();
42202             var align = this.labelAlign || this.parentLabelAlign();
42203             var id = Roo.id();
42204             
42205             this.allCountries = [];
42206             this.dialCodeMapping = [];
42207             
42208             for (var i = 0; i < data.length; i++) {
42209               var c = data[i];
42210               this.allCountries[i] = {
42211                 name: c[0],
42212                 iso2: c[1],
42213                 dialCode: c[2],
42214                 priority: c[3] || 0,
42215                 areaCodes: c[4] || null
42216               };
42217               this.dialCodeMapping[c[2]] = {
42218                   name: c[0],
42219                   iso2: c[1],
42220                   priority: c[3] || 0,
42221                   areaCodes: c[4] || null
42222               };
42223             }
42224             
42225             var cfg = {
42226                 cls: 'form-group',
42227                 cn: []
42228             };
42229             
42230             var input =  {
42231                 tag: 'input',
42232                 id : id,
42233                 // type: 'number', -- do not use number - we get the flaky up/down arrows.
42234                 maxlength: this.max_length,
42235                 cls : 'form-control tel-input',
42236                 autocomplete: 'new-password'
42237             };
42238             
42239             var hiddenInput = {
42240                 tag: 'input',
42241                 type: 'hidden',
42242                 cls: 'hidden-tel-input'
42243             };
42244             
42245             if (this.name) {
42246                 hiddenInput.name = this.name;
42247             }
42248             
42249             if (this.disabled) {
42250                 input.disabled = true;
42251             }
42252             
42253             var flag_container = {
42254                 tag: 'div',
42255                 cls: 'flag-box',
42256                 cn: [
42257                     {
42258                         tag: 'div',
42259                         cls: 'flag'
42260                     },
42261                     {
42262                         tag: 'div',
42263                         cls: 'caret'
42264                     }
42265                 ]
42266             };
42267             
42268             var box = {
42269                 tag: 'div',
42270                 cls: this.hasFeedback ? 'has-feedback' : '',
42271                 cn: [
42272                     hiddenInput,
42273                     input,
42274                     {
42275                         tag: 'input',
42276                         cls: 'dial-code-holder',
42277                         disabled: true
42278                     }
42279                 ]
42280             };
42281             
42282             var container = {
42283                 cls: 'roo-select2-container input-group',
42284                 cn: [
42285                     flag_container,
42286                     box
42287                 ]
42288             };
42289             
42290             if (this.fieldLabel.length) {
42291                 var indicator = {
42292                     tag: 'i',
42293                     tooltip: 'This field is required'
42294                 };
42295                 
42296                 var label = {
42297                     tag: 'label',
42298                     'for':  id,
42299                     cls: 'control-label',
42300                     cn: []
42301                 };
42302                 
42303                 var label_text = {
42304                     tag: 'span',
42305                     html: this.fieldLabel
42306                 };
42307                 
42308                 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
42309                 label.cn = [
42310                     indicator,
42311                     label_text
42312                 ];
42313                 
42314                 if(this.indicatorpos == 'right') {
42315                     indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
42316                     label.cn = [
42317                         label_text,
42318                         indicator
42319                     ];
42320                 }
42321                 
42322                 if(align == 'left') {
42323                     container = {
42324                         tag: 'div',
42325                         cn: [
42326                             container
42327                         ]
42328                     };
42329                     
42330                     if(this.labelWidth > 12){
42331                         label.style = "width: " + this.labelWidth + 'px';
42332                     }
42333                     if(this.labelWidth < 13 && this.labelmd == 0){
42334                         this.labelmd = this.labelWidth;
42335                     }
42336                     if(this.labellg > 0){
42337                         label.cls += ' col-lg-' + this.labellg;
42338                         input.cls += ' col-lg-' + (12 - this.labellg);
42339                     }
42340                     if(this.labelmd > 0){
42341                         label.cls += ' col-md-' + this.labelmd;
42342                         container.cls += ' col-md-' + (12 - this.labelmd);
42343                     }
42344                     if(this.labelsm > 0){
42345                         label.cls += ' col-sm-' + this.labelsm;
42346                         container.cls += ' col-sm-' + (12 - this.labelsm);
42347                     }
42348                     if(this.labelxs > 0){
42349                         label.cls += ' col-xs-' + this.labelxs;
42350                         container.cls += ' col-xs-' + (12 - this.labelxs);
42351                     }
42352                 }
42353             }
42354             
42355             cfg.cn = [
42356                 label,
42357                 container
42358             ];
42359             
42360             var settings = this;
42361             
42362             ['xs','sm','md','lg'].map(function(size){
42363                 if (settings[size]) {
42364                     cfg.cls += ' col-' + size + '-' + settings[size];
42365                 }
42366             });
42367             
42368             this.store = new Roo.data.Store({
42369                 proxy : new Roo.data.MemoryProxy({}),
42370                 reader : new Roo.data.JsonReader({
42371                     fields : [
42372                         {
42373                             'name' : 'name',
42374                             'type' : 'string'
42375                         },
42376                         {
42377                             'name' : 'iso2',
42378                             'type' : 'string'
42379                         },
42380                         {
42381                             'name' : 'dialCode',
42382                             'type' : 'string'
42383                         },
42384                         {
42385                             'name' : 'priority',
42386                             'type' : 'string'
42387                         },
42388                         {
42389                             'name' : 'areaCodes',
42390                             'type' : 'string'
42391                         }
42392                     ]
42393                 })
42394             });
42395             
42396             if(!this.preferedCountries) {
42397                 this.preferedCountries = [
42398                     'hk',
42399                     'gb',
42400                     'us'
42401                 ];
42402             }
42403             
42404             var p = this.preferedCountries.reverse();
42405             
42406             if(p) {
42407                 for (var i = 0; i < p.length; i++) {
42408                     for (var j = 0; j < this.allCountries.length; j++) {
42409                         if(this.allCountries[j].iso2 == p[i]) {
42410                             var t = this.allCountries[j];
42411                             this.allCountries.splice(j,1);
42412                             this.allCountries.unshift(t);
42413                         }
42414                     } 
42415                 }
42416             }
42417             
42418             this.store.proxy.data = {
42419                 success: true,
42420                 data: this.allCountries
42421             };
42422             
42423             return cfg;
42424         },
42425         
42426         initEvents : function()
42427         {
42428             this.createList();
42429             Roo.bootstrap.PhoneInput.superclass.initEvents.call(this);
42430             
42431             this.indicator = this.indicatorEl();
42432             this.flag = this.flagEl();
42433             this.dialCodeHolder = this.dialCodeHolderEl();
42434             
42435             this.trigger = this.el.select('div.flag-box',true).first();
42436             this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
42437             
42438             var _this = this;
42439             
42440             (function(){
42441                 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
42442                 _this.list.setWidth(lw);
42443             }).defer(100);
42444             
42445             this.list.on('mouseover', this.onViewOver, this);
42446             this.list.on('mousemove', this.onViewMove, this);
42447             this.inputEl().on("keyup", this.onKeyUp, this);
42448             this.inputEl().on("keypress", this.onKeyPress, this);
42449             
42450             this.tpl = '<li><a href="#"><div class="flag {iso2}"></div>{name} <span class="dial-code">+{dialCode}</span></a></li>';
42451
42452             this.view = new Roo.View(this.list, this.tpl, {
42453                 singleSelect:true, store: this.store, selectedClass: this.selectedClass
42454             });
42455             
42456             this.view.on('click', this.onViewClick, this);
42457             this.setValue(this.defaultDialCode);
42458         },
42459         
42460         onTriggerClick : function(e)
42461         {
42462             Roo.log('trigger click');
42463             if(this.disabled){
42464                 return;
42465             }
42466             
42467             if(this.isExpanded()){
42468                 this.collapse();
42469                 this.hasFocus = false;
42470             }else {
42471                 this.store.load({});
42472                 this.hasFocus = true;
42473                 this.expand();
42474             }
42475         },
42476         
42477         isExpanded : function()
42478         {
42479             return this.list.isVisible();
42480         },
42481         
42482         collapse : function()
42483         {
42484             if(!this.isExpanded()){
42485                 return;
42486             }
42487             this.list.hide();
42488             Roo.get(document).un('mousedown', this.collapseIf, this);
42489             Roo.get(document).un('mousewheel', this.collapseIf, this);
42490             this.fireEvent('collapse', this);
42491             this.validate();
42492         },
42493         
42494         expand : function()
42495         {
42496             Roo.log('expand');
42497
42498             if(this.isExpanded() || !this.hasFocus){
42499                 return;
42500             }
42501             
42502             var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
42503             this.list.setWidth(lw);
42504             
42505             this.list.show();
42506             this.restrictHeight();
42507             
42508             Roo.get(document).on('mousedown', this.collapseIf, this);
42509             Roo.get(document).on('mousewheel', this.collapseIf, this);
42510             
42511             this.fireEvent('expand', this);
42512         },
42513         
42514         restrictHeight : function()
42515         {
42516             this.list.alignTo(this.inputEl(), this.listAlign);
42517             this.list.alignTo(this.inputEl(), this.listAlign);
42518         },
42519         
42520         onViewOver : function(e, t)
42521         {
42522             if(this.inKeyMode){
42523                 return;
42524             }
42525             var item = this.view.findItemFromChild(t);
42526             
42527             if(item){
42528                 var index = this.view.indexOf(item);
42529                 this.select(index, false);
42530             }
42531         },
42532
42533         // private
42534         onViewClick : function(view, doFocus, el, e)
42535         {
42536             var index = this.view.getSelectedIndexes()[0];
42537             
42538             var r = this.store.getAt(index);
42539             
42540             if(r){
42541                 this.onSelect(r, index);
42542             }
42543             if(doFocus !== false && !this.blockFocus){
42544                 this.inputEl().focus();
42545             }
42546         },
42547         
42548         onViewMove : function(e, t)
42549         {
42550             this.inKeyMode = false;
42551         },
42552         
42553         select : function(index, scrollIntoView)
42554         {
42555             this.selectedIndex = index;
42556             this.view.select(index);
42557             if(scrollIntoView !== false){
42558                 var el = this.view.getNode(index);
42559                 if(el){
42560                     this.list.scrollChildIntoView(el, false);
42561                 }
42562             }
42563         },
42564         
42565         createList : function()
42566         {
42567             this.list = Roo.get(document.body).createChild({
42568                 tag: 'ul',
42569                 cls: 'typeahead typeahead-long dropdown-menu tel-list',
42570                 style: 'display:none'
42571             });
42572             
42573             this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
42574         },
42575         
42576         collapseIf : function(e)
42577         {
42578             var in_combo  = e.within(this.el);
42579             var in_list =  e.within(this.list);
42580             var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
42581             
42582             if (in_combo || in_list || is_list) {
42583                 return;
42584             }
42585             this.collapse();
42586         },
42587         
42588         onSelect : function(record, index)
42589         {
42590             if(this.fireEvent('beforeselect', this, record, index) !== false){
42591                 
42592                 this.setFlagClass(record.data.iso2);
42593                 this.setDialCode(record.data.dialCode);
42594                 this.hasFocus = false;
42595                 this.collapse();
42596                 this.fireEvent('select', this, record, index);
42597             }
42598         },
42599         
42600         flagEl : function()
42601         {
42602             var flag = this.el.select('div.flag',true).first();
42603             if(!flag){
42604                 return false;
42605             }
42606             return flag;
42607         },
42608         
42609         dialCodeHolderEl : function()
42610         {
42611             var d = this.el.select('input.dial-code-holder',true).first();
42612             if(!d){
42613                 return false;
42614             }
42615             return d;
42616         },
42617         
42618         setDialCode : function(v)
42619         {
42620             this.dialCodeHolder.dom.value = '+'+v;
42621         },
42622         
42623         setFlagClass : function(n)
42624         {
42625             this.flag.dom.className = 'flag '+n;
42626         },
42627         
42628         getValue : function()
42629         {
42630             var v = this.inputEl().getValue();
42631             if(this.dialCodeHolder) {
42632                 v = this.dialCodeHolder.dom.value+this.inputEl().getValue();
42633             }
42634             return v;
42635         },
42636         
42637         setValue : function(v)
42638         {
42639             var d = this.getDialCode(v);
42640             
42641             //invalid dial code
42642             if(v.length == 0 || !d || d.length == 0) {
42643                 if(this.rendered){
42644                     this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
42645                     this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
42646                 }
42647                 return;
42648             }
42649             
42650             //valid dial code
42651             this.setFlagClass(this.dialCodeMapping[d].iso2);
42652             this.setDialCode(d);
42653             this.inputEl().dom.value = v.replace('+'+d,'');
42654             this.hiddenEl().dom.value = this.getValue();
42655             
42656             this.validate();
42657         },
42658         
42659         getDialCode : function(v)
42660         {
42661             v = v ||  '';
42662             
42663             if (v.length == 0) {
42664                 return this.dialCodeHolder.dom.value;
42665             }
42666             
42667             var dialCode = "";
42668             if (v.charAt(0) != "+") {
42669                 return false;
42670             }
42671             var numericChars = "";
42672             for (var i = 1; i < v.length; i++) {
42673               var c = v.charAt(i);
42674               if (!isNaN(c)) {
42675                 numericChars += c;
42676                 if (this.dialCodeMapping[numericChars]) {
42677                   dialCode = v.substr(1, i);
42678                 }
42679                 if (numericChars.length == 4) {
42680                   break;
42681                 }
42682               }
42683             }
42684             return dialCode;
42685         },
42686         
42687         reset : function()
42688         {
42689             this.setValue(this.defaultDialCode);
42690             this.validate();
42691         },
42692         
42693         hiddenEl : function()
42694         {
42695             return this.el.select('input.hidden-tel-input',true).first();
42696         },
42697         
42698         // after setting val
42699         onKeyUp : function(e){
42700             this.setValue(this.getValue());
42701         },
42702         
42703         onKeyPress : function(e){
42704             if(this.allowed.indexOf(String.fromCharCode(e.getCharCode())) === -1){
42705                 e.stopEvent();
42706             }
42707         }
42708         
42709 });
42710 /**
42711  * @class Roo.bootstrap.MoneyField
42712  * @extends Roo.bootstrap.ComboBox
42713  * Bootstrap MoneyField class
42714  * 
42715  * @constructor
42716  * Create a new MoneyField.
42717  * @param {Object} config Configuration options
42718  */
42719
42720 Roo.bootstrap.MoneyField = function(config) {
42721     
42722     Roo.bootstrap.MoneyField.superclass.constructor.call(this, config);
42723     
42724 };
42725
42726 Roo.extend(Roo.bootstrap.MoneyField, Roo.bootstrap.ComboBox, {
42727     
42728     /**
42729      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
42730      */
42731     allowDecimals : true,
42732     /**
42733      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
42734      */
42735     decimalSeparator : ".",
42736     /**
42737      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
42738      */
42739     decimalPrecision : 0,
42740     /**
42741      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
42742      */
42743     allowNegative : true,
42744     /**
42745      * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
42746      */
42747     allowZero: true,
42748     /**
42749      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
42750      */
42751     minValue : Number.NEGATIVE_INFINITY,
42752     /**
42753      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
42754      */
42755     maxValue : Number.MAX_VALUE,
42756     /**
42757      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
42758      */
42759     minText : "The minimum value for this field is {0}",
42760     /**
42761      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
42762      */
42763     maxText : "The maximum value for this field is {0}",
42764     /**
42765      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
42766      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
42767      */
42768     nanText : "{0} is not a valid number",
42769     /**
42770      * @cfg {Boolean} castInt (true|false) cast int if true (defalut true)
42771      */
42772     castInt : true,
42773     /**
42774      * @cfg {String} defaults currency of the MoneyField
42775      * value should be in lkey
42776      */
42777     defaultCurrency : false,
42778     /**
42779      * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
42780      */
42781     thousandsDelimiter : false,
42782     /**
42783      * @cfg {Number} max_length Maximum input field length allowed (defaults to Number.MAX_VALUE)
42784      */
42785     max_length: false,
42786     
42787     inputlg : 9,
42788     inputmd : 9,
42789     inputsm : 9,
42790     inputxs : 6,
42791     
42792     store : false,
42793     
42794     getAutoCreate : function()
42795     {
42796         var align = this.labelAlign || this.parentLabelAlign();
42797         
42798         var id = Roo.id();
42799
42800         var cfg = {
42801             cls: 'form-group',
42802             cn: []
42803         };
42804
42805         var input =  {
42806             tag: 'input',
42807             id : id,
42808             cls : 'form-control roo-money-amount-input',
42809             autocomplete: 'new-password'
42810         };
42811         
42812         var hiddenInput = {
42813             tag: 'input',
42814             type: 'hidden',
42815             id: Roo.id(),
42816             cls: 'hidden-number-input'
42817         };
42818         
42819         if(this.max_length) {
42820             input.maxlength = this.max_length; 
42821         }
42822         
42823         if (this.name) {
42824             hiddenInput.name = this.name;
42825         }
42826
42827         if (this.disabled) {
42828             input.disabled = true;
42829         }
42830
42831         var clg = 12 - this.inputlg;
42832         var cmd = 12 - this.inputmd;
42833         var csm = 12 - this.inputsm;
42834         var cxs = 12 - this.inputxs;
42835         
42836         var container = {
42837             tag : 'div',
42838             cls : 'row roo-money-field',
42839             cn : [
42840                 {
42841                     tag : 'div',
42842                     cls : 'roo-money-currency column col-lg-' + clg + ' col-md-' + cmd + ' col-sm-' + csm + ' col-xs-' + cxs,
42843                     cn : [
42844                         {
42845                             tag : 'div',
42846                             cls: 'roo-select2-container input-group',
42847                             cn: [
42848                                 {
42849                                     tag : 'input',
42850                                     cls : 'form-control roo-money-currency-input',
42851                                     autocomplete: 'new-password',
42852                                     readOnly : 1,
42853                                     name : this.currencyName
42854                                 },
42855                                 {
42856                                     tag :'span',
42857                                     cls : 'input-group-addon',
42858                                     cn : [
42859                                         {
42860                                             tag: 'span',
42861                                             cls: 'caret'
42862                                         }
42863                                     ]
42864                                 }
42865                             ]
42866                         }
42867                     ]
42868                 },
42869                 {
42870                     tag : 'div',
42871                     cls : 'roo-money-amount column col-lg-' + this.inputlg + ' col-md-' + this.inputmd + ' col-sm-' + this.inputsm + ' col-xs-' + this.inputxs,
42872                     cn : [
42873                         {
42874                             tag: 'div',
42875                             cls: this.hasFeedback ? 'has-feedback' : '',
42876                             cn: [
42877                                 input
42878                             ]
42879                         }
42880                     ]
42881                 }
42882             ]
42883             
42884         };
42885         
42886         if (this.fieldLabel.length) {
42887             var indicator = {
42888                 tag: 'i',
42889                 tooltip: 'This field is required'
42890             };
42891
42892             var label = {
42893                 tag: 'label',
42894                 'for':  id,
42895                 cls: 'control-label',
42896                 cn: []
42897             };
42898
42899             var label_text = {
42900                 tag: 'span',
42901                 html: this.fieldLabel
42902             };
42903
42904             indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
42905             label.cn = [
42906                 indicator,
42907                 label_text
42908             ];
42909
42910             if(this.indicatorpos == 'right') {
42911                 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
42912                 label.cn = [
42913                     label_text,
42914                     indicator
42915                 ];
42916             }
42917
42918             if(align == 'left') {
42919                 container = {
42920                     tag: 'div',
42921                     cn: [
42922                         container
42923                     ]
42924                 };
42925
42926                 if(this.labelWidth > 12){
42927                     label.style = "width: " + this.labelWidth + 'px';
42928                 }
42929                 if(this.labelWidth < 13 && this.labelmd == 0){
42930                     this.labelmd = this.labelWidth;
42931                 }
42932                 if(this.labellg > 0){
42933                     label.cls += ' col-lg-' + this.labellg;
42934                     input.cls += ' col-lg-' + (12 - this.labellg);
42935                 }
42936                 if(this.labelmd > 0){
42937                     label.cls += ' col-md-' + this.labelmd;
42938                     container.cls += ' col-md-' + (12 - this.labelmd);
42939                 }
42940                 if(this.labelsm > 0){
42941                     label.cls += ' col-sm-' + this.labelsm;
42942                     container.cls += ' col-sm-' + (12 - this.labelsm);
42943                 }
42944                 if(this.labelxs > 0){
42945                     label.cls += ' col-xs-' + this.labelxs;
42946                     container.cls += ' col-xs-' + (12 - this.labelxs);
42947                 }
42948             }
42949         }
42950
42951         cfg.cn = [
42952             label,
42953             container,
42954             hiddenInput
42955         ];
42956         
42957         var settings = this;
42958
42959         ['xs','sm','md','lg'].map(function(size){
42960             if (settings[size]) {
42961                 cfg.cls += ' col-' + size + '-' + settings[size];
42962             }
42963         });
42964         
42965         return cfg;
42966     },
42967     
42968     initEvents : function()
42969     {
42970         this.indicator = this.indicatorEl();
42971         
42972         this.initCurrencyEvent();
42973         
42974         this.initNumberEvent();
42975     },
42976     
42977     initCurrencyEvent : function()
42978     {
42979         if (!this.store) {
42980             throw "can not find store for combo";
42981         }
42982         
42983         this.store = Roo.factory(this.store, Roo.data);
42984         this.store.parent = this;
42985         
42986         this.createList();
42987         
42988         this.triggerEl = this.el.select('.input-group-addon', true).first();
42989         
42990         this.triggerEl.on("click", this.onTriggerClick, this, { preventDefault : true });
42991         
42992         var _this = this;
42993         
42994         (function(){
42995             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
42996             _this.list.setWidth(lw);
42997         }).defer(100);
42998         
42999         this.list.on('mouseover', this.onViewOver, this);
43000         this.list.on('mousemove', this.onViewMove, this);
43001         this.list.on('scroll', this.onViewScroll, this);
43002         
43003         if(!this.tpl){
43004             this.tpl = '<li><a href="#">{' + this.currencyField + '}</a></li>';
43005         }
43006         
43007         this.view = new Roo.View(this.list, this.tpl, {
43008             singleSelect:true, store: this.store, selectedClass: this.selectedClass
43009         });
43010         
43011         this.view.on('click', this.onViewClick, this);
43012         
43013         this.store.on('beforeload', this.onBeforeLoad, this);
43014         this.store.on('load', this.onLoad, this);
43015         this.store.on('loadexception', this.onLoadException, this);
43016         
43017         this.keyNav = new Roo.KeyNav(this.currencyEl(), {
43018             "up" : function(e){
43019                 this.inKeyMode = true;
43020                 this.selectPrev();
43021             },
43022
43023             "down" : function(e){
43024                 if(!this.isExpanded()){
43025                     this.onTriggerClick();
43026                 }else{
43027                     this.inKeyMode = true;
43028                     this.selectNext();
43029                 }
43030             },
43031
43032             "enter" : function(e){
43033                 this.collapse();
43034                 
43035                 if(this.fireEvent("specialkey", this, e)){
43036                     this.onViewClick(false);
43037                 }
43038                 
43039                 return true;
43040             },
43041
43042             "esc" : function(e){
43043                 this.collapse();
43044             },
43045
43046             "tab" : function(e){
43047                 this.collapse();
43048                 
43049                 if(this.fireEvent("specialkey", this, e)){
43050                     this.onViewClick(false);
43051                 }
43052                 
43053                 return true;
43054             },
43055
43056             scope : this,
43057
43058             doRelay : function(foo, bar, hname){
43059                 if(hname == 'down' || this.scope.isExpanded()){
43060                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
43061                 }
43062                 return true;
43063             },
43064
43065             forceKeyDown: true
43066         });
43067         
43068         this.currencyEl().on("click", this.onTriggerClick, this, { preventDefault : true });
43069         
43070     },
43071     
43072     initNumberEvent : function(e)
43073     {
43074         this.inputEl().on("keydown" , this.fireKey,  this);
43075         this.inputEl().on("focus", this.onFocus,  this);
43076         this.inputEl().on("blur", this.onBlur,  this);
43077         
43078         this.inputEl().relayEvent('keyup', this);
43079         
43080         if(this.indicator){
43081             this.indicator.addClass('invisible');
43082         }
43083  
43084         this.originalValue = this.getValue();
43085         
43086         if(this.validationEvent == 'keyup'){
43087             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
43088             this.inputEl().on('keyup', this.filterValidation, this);
43089         }
43090         else if(this.validationEvent !== false){
43091             this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
43092         }
43093         
43094         if(this.selectOnFocus){
43095             this.on("focus", this.preFocus, this);
43096             
43097         }
43098         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
43099             this.inputEl().on("keypress", this.filterKeys, this);
43100         } else {
43101             this.inputEl().relayEvent('keypress', this);
43102         }
43103         
43104         var allowed = "0123456789";
43105         
43106         if(this.allowDecimals){
43107             allowed += this.decimalSeparator;
43108         }
43109         
43110         if(this.allowNegative){
43111             allowed += "-";
43112         }
43113         
43114         if(this.thousandsDelimiter) {
43115             allowed += ",";
43116         }
43117         
43118         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
43119         
43120         var keyPress = function(e){
43121             
43122             var k = e.getKey();
43123             
43124             var c = e.getCharCode();
43125             
43126             if(
43127                     (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
43128                     allowed.indexOf(String.fromCharCode(c)) === -1
43129             ){
43130                 e.stopEvent();
43131                 return;
43132             }
43133             
43134             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
43135                 return;
43136             }
43137             
43138             if(allowed.indexOf(String.fromCharCode(c)) === -1){
43139                 e.stopEvent();
43140             }
43141         };
43142         
43143         this.inputEl().on("keypress", keyPress, this);
43144         
43145     },
43146     
43147     onTriggerClick : function(e)
43148     {   
43149         if(this.disabled){
43150             return;
43151         }
43152         
43153         this.page = 0;
43154         this.loadNext = false;
43155         
43156         if(this.isExpanded()){
43157             this.collapse();
43158             return;
43159         }
43160         
43161         this.hasFocus = true;
43162         
43163         if(this.triggerAction == 'all') {
43164             this.doQuery(this.allQuery, true);
43165             return;
43166         }
43167         
43168         this.doQuery(this.getRawValue());
43169     },
43170     
43171     getCurrency : function()
43172     {   
43173         var v = this.currencyEl().getValue();
43174         
43175         return v;
43176     },
43177     
43178     restrictHeight : function()
43179     {
43180         this.list.alignTo(this.currencyEl(), this.listAlign);
43181         this.list.alignTo(this.currencyEl(), this.listAlign);
43182     },
43183     
43184     onViewClick : function(view, doFocus, el, e)
43185     {
43186         var index = this.view.getSelectedIndexes()[0];
43187         
43188         var r = this.store.getAt(index);
43189         
43190         if(r){
43191             this.onSelect(r, index);
43192         }
43193     },
43194     
43195     onSelect : function(record, index){
43196         
43197         if(this.fireEvent('beforeselect', this, record, index) !== false){
43198         
43199             this.setFromCurrencyData(index > -1 ? record.data : false);
43200             
43201             this.collapse();
43202             
43203             this.fireEvent('select', this, record, index);
43204         }
43205     },
43206     
43207     setFromCurrencyData : function(o)
43208     {
43209         var currency = '';
43210         
43211         this.lastCurrency = o;
43212         
43213         if (this.currencyField) {
43214             currency = !o || typeof(o[this.currencyField]) == 'undefined' ? '' : o[this.currencyField];
43215         } else {
43216             Roo.log('no  currencyField value set for '+ (this.name ? this.name : this.id));
43217         }
43218         
43219         this.lastSelectionText = currency;
43220         
43221         //setting default currency
43222         if(o[this.currencyField] * 1 == 0 && this.defaultCurrency) {
43223             this.setCurrency(this.defaultCurrency);
43224             return;
43225         }
43226         
43227         this.setCurrency(currency);
43228     },
43229     
43230     setFromData : function(o)
43231     {
43232         var c = {};
43233         
43234         c[this.currencyField] = !o || typeof(o[this.currencyName]) == 'undefined' ? '' : o[this.currencyName];
43235         
43236         this.setFromCurrencyData(c);
43237         
43238         var value = '';
43239         
43240         if (this.name) {
43241             value = !o || typeof(o[this.name]) == 'undefined' ? '' : o[this.name];
43242         } else {
43243             Roo.log('no value set for '+ (this.name ? this.name : this.id));
43244         }
43245         
43246         this.setValue(value);
43247         
43248     },
43249     
43250     setCurrency : function(v)
43251     {   
43252         this.currencyValue = v;
43253         
43254         if(this.rendered){
43255             this.currencyEl().dom.value = (v === null || v === undefined ? '' : v);
43256             this.validate();
43257         }
43258     },
43259     
43260     setValue : function(v)
43261     {
43262         v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
43263         
43264         this.value = v;
43265         
43266         if(this.rendered){
43267             
43268             this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
43269             
43270             this.inputEl().dom.value = (v == '') ? '' :
43271                 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
43272             
43273             if(!this.allowZero && v === '0') {
43274                 this.hiddenEl().dom.value = '';
43275                 this.inputEl().dom.value = '';
43276             }
43277             
43278             this.validate();
43279         }
43280     },
43281     
43282     getRawValue : function()
43283     {
43284         var v = this.inputEl().getValue();
43285         
43286         return v;
43287     },
43288     
43289     getValue : function()
43290     {
43291         return this.fixPrecision(this.parseValue(this.getRawValue()));
43292     },
43293     
43294     parseValue : function(value)
43295     {
43296         if(this.thousandsDelimiter) {
43297             value += "";
43298             r = new RegExp(",", "g");
43299             value = value.replace(r, "");
43300         }
43301         
43302         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
43303         return isNaN(value) ? '' : value;
43304         
43305     },
43306     
43307     fixPrecision : function(value)
43308     {
43309         if(this.thousandsDelimiter) {
43310             value += "";
43311             r = new RegExp(",", "g");
43312             value = value.replace(r, "");
43313         }
43314         
43315         var nan = isNaN(value);
43316         
43317         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
43318             return nan ? '' : value;
43319         }
43320         return parseFloat(value).toFixed(this.decimalPrecision);
43321     },
43322     
43323     decimalPrecisionFcn : function(v)
43324     {
43325         return Math.floor(v);
43326     },
43327     
43328     validateValue : function(value)
43329     {
43330         if(!Roo.bootstrap.MoneyField.superclass.validateValue.call(this, value)){
43331             return false;
43332         }
43333         
43334         var num = this.parseValue(value);
43335         
43336         if(isNaN(num)){
43337             this.markInvalid(String.format(this.nanText, value));
43338             return false;
43339         }
43340         
43341         if(num < this.minValue){
43342             this.markInvalid(String.format(this.minText, this.minValue));
43343             return false;
43344         }
43345         
43346         if(num > this.maxValue){
43347             this.markInvalid(String.format(this.maxText, this.maxValue));
43348             return false;
43349         }
43350         
43351         return true;
43352     },
43353     
43354     validate : function()
43355     {
43356         if(this.disabled || this.allowBlank){
43357             this.markValid();
43358             return true;
43359         }
43360         
43361         var currency = this.getCurrency();
43362         
43363         if(this.validateValue(this.getRawValue()) && currency.length){
43364             this.markValid();
43365             return true;
43366         }
43367         
43368         this.markInvalid();
43369         return false;
43370     },
43371     
43372     getName: function()
43373     {
43374         return this.name;
43375     },
43376     
43377     beforeBlur : function()
43378     {
43379         if(!this.castInt){
43380             return;
43381         }
43382         
43383         var v = this.parseValue(this.getRawValue());
43384         
43385         if(v || v == 0){
43386             this.setValue(v);
43387         }
43388     },
43389     
43390     onBlur : function()
43391     {
43392         this.beforeBlur();
43393         
43394         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
43395             //this.el.removeClass(this.focusClass);
43396         }
43397         
43398         this.hasFocus = false;
43399         
43400         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
43401             this.validate();
43402         }
43403         
43404         var v = this.getValue();
43405         
43406         if(String(v) !== String(this.startValue)){
43407             this.fireEvent('change', this, v, this.startValue);
43408         }
43409         
43410         this.fireEvent("blur", this);
43411     },
43412     
43413     inputEl : function()
43414     {
43415         return this.el.select('.roo-money-amount-input', true).first();
43416     },
43417     
43418     currencyEl : function()
43419     {
43420         return this.el.select('.roo-money-currency-input', true).first();
43421     },
43422     
43423     hiddenEl : function()
43424     {
43425         return this.el.select('input.hidden-number-input',true).first();
43426     }
43427     
43428 });/**
43429  * @class Roo.bootstrap.BezierSignature
43430  * @extends Roo.bootstrap.Component
43431  * Bootstrap BezierSignature class
43432  * This script refer to:
43433  *    Title: Signature Pad
43434  *    Author: szimek
43435  *    Availability: https://github.com/szimek/signature_pad
43436  *
43437  * @constructor
43438  * Create a new BezierSignature
43439  * @param {Object} config The config object
43440  */
43441
43442 Roo.bootstrap.BezierSignature = function(config){
43443     Roo.bootstrap.BezierSignature.superclass.constructor.call(this, config);
43444     this.addEvents({
43445         "resize" : true
43446     });
43447 };
43448
43449 Roo.extend(Roo.bootstrap.BezierSignature, Roo.bootstrap.Component,
43450 {
43451      
43452     curve_data: [],
43453     
43454     is_empty: true,
43455     
43456     mouse_btn_down: true,
43457     
43458     /**
43459      * @cfg {int} canvas height
43460      */
43461     canvas_height: '200px',
43462     
43463     /**
43464      * @cfg {float|function} Radius of a single dot.
43465      */ 
43466     dot_size: false,
43467     
43468     /**
43469      * @cfg {float} Minimum width of a line. Defaults to 0.5.
43470      */
43471     min_width: 0.5,
43472     
43473     /**
43474      * @cfg {float} Maximum width of a line. Defaults to 2.5.
43475      */
43476     max_width: 2.5,
43477     
43478     /**
43479      * @cfg {integer} Draw the next point at most once per every x milliseconds. Set it to 0 to turn off throttling. Defaults to 16.
43480      */
43481     throttle: 16,
43482     
43483     /**
43484      * @cfg {integer} Add the next point only if the previous one is farther than x pixels. Defaults to 5.
43485      */
43486     min_distance: 5,
43487     
43488     /**
43489      * @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.
43490      */
43491     bg_color: 'rgba(0, 0, 0, 0)',
43492     
43493     /**
43494      * @cfg {string} Color used to draw the lines. Can be any color format accepted by context.fillStyle. Defaults to "black".
43495      */
43496     dot_color: 'black',
43497     
43498     /**
43499      * @cfg {float} Weight used to modify new velocity based on the previous velocity. Defaults to 0.7.
43500      */ 
43501     velocity_filter_weight: 0.7,
43502     
43503     /**
43504      * @cfg {function} Callback when stroke begin. 
43505      */
43506     onBegin: false,
43507     
43508     /**
43509      * @cfg {function} Callback when stroke end.
43510      */
43511     onEnd: false,
43512     
43513     getAutoCreate : function()
43514     {
43515         var cls = 'roo-signature column';
43516         
43517         if(this.cls){
43518             cls += ' ' + this.cls;
43519         }
43520         
43521         var col_sizes = [
43522             'lg',
43523             'md',
43524             'sm',
43525             'xs'
43526         ];
43527         
43528         for(var i = 0; i < col_sizes.length; i++) {
43529             if(this[col_sizes[i]]) {
43530                 cls += " col-"+col_sizes[i]+"-"+this[col_sizes[i]];
43531             }
43532         }
43533         
43534         var cfg = {
43535             tag: 'div',
43536             cls: cls,
43537             cn: [
43538                 {
43539                     tag: 'div',
43540                     cls: 'roo-signature-body',
43541                     cn: [
43542                         {
43543                             tag: 'canvas',
43544                             cls: 'roo-signature-body-canvas',
43545                             height: this.canvas_height,
43546                             width: this.canvas_width
43547                         }
43548                     ]
43549                 },
43550                 {
43551                     tag: 'input',
43552                     type: 'file',
43553                     style: 'display: none'
43554                 }
43555             ]
43556         };
43557         
43558         return cfg;
43559     },
43560     
43561     initEvents: function() 
43562     {
43563         Roo.bootstrap.BezierSignature.superclass.initEvents.call(this);
43564         
43565         var canvas = this.canvasEl();
43566         
43567         // mouse && touch event swapping...
43568         canvas.dom.style.touchAction = 'none';
43569         canvas.dom.style.msTouchAction = 'none';
43570         
43571         this.mouse_btn_down = false;
43572         canvas.on('mousedown', this._handleMouseDown, this);
43573         canvas.on('mousemove', this._handleMouseMove, this);
43574         Roo.select('html').first().on('mouseup', this._handleMouseUp, this);
43575         
43576         if (window.PointerEvent) {
43577             canvas.on('pointerdown', this._handleMouseDown, this);
43578             canvas.on('pointermove', this._handleMouseMove, this);
43579             Roo.select('html').first().on('pointerup', this._handleMouseUp, this);
43580         }
43581         
43582         if ('ontouchstart' in window) {
43583             canvas.on('touchstart', this._handleTouchStart, this);
43584             canvas.on('touchmove', this._handleTouchMove, this);
43585             canvas.on('touchend', this._handleTouchEnd, this);
43586         }
43587         
43588         Roo.EventManager.onWindowResize(this.resize, this, true);
43589         
43590         // file input event
43591         this.fileEl().on('change', this.uploadImage, this);
43592         
43593         this.clear();
43594         
43595         this.resize();
43596     },
43597     
43598     resize: function(){
43599         
43600         var canvas = this.canvasEl().dom;
43601         var ctx = this.canvasElCtx();
43602         var img_data = false;
43603         
43604         if(canvas.width > 0) {
43605             var img_data = ctx.getImageData(0, 0, canvas.width, canvas.height);
43606         }
43607         // setting canvas width will clean img data
43608         canvas.width = 0;
43609         
43610         var style = window.getComputedStyle ? 
43611             getComputedStyle(this.el.dom, null) : this.el.dom.currentStyle;
43612             
43613         var padding_left = parseInt(style.paddingLeft) || 0;
43614         var padding_right = parseInt(style.paddingRight) || 0;
43615         
43616         canvas.width = this.el.dom.clientWidth - padding_left - padding_right;
43617         
43618         if(img_data) {
43619             ctx.putImageData(img_data, 0, 0);
43620         }
43621     },
43622     
43623     _handleMouseDown: function(e)
43624     {
43625         if (e.browserEvent.which === 1) {
43626             this.mouse_btn_down = true;
43627             this.strokeBegin(e);
43628         }
43629     },
43630     
43631     _handleMouseMove: function (e)
43632     {
43633         if (this.mouse_btn_down) {
43634             this.strokeMoveUpdate(e);
43635         }
43636     },
43637     
43638     _handleMouseUp: function (e)
43639     {
43640         if (e.browserEvent.which === 1 && this.mouse_btn_down) {
43641             this.mouse_btn_down = false;
43642             this.strokeEnd(e);
43643         }
43644     },
43645     
43646     _handleTouchStart: function (e) {
43647         
43648         e.preventDefault();
43649         if (e.browserEvent.targetTouches.length === 1) {
43650             // var touch = e.browserEvent.changedTouches[0];
43651             // this.strokeBegin(touch);
43652             
43653              this.strokeBegin(e); // assume e catching the correct xy...
43654         }
43655     },
43656     
43657     _handleTouchMove: function (e) {
43658         e.preventDefault();
43659         // var touch = event.targetTouches[0];
43660         // _this._strokeMoveUpdate(touch);
43661         this.strokeMoveUpdate(e);
43662     },
43663     
43664     _handleTouchEnd: function (e) {
43665         var wasCanvasTouched = e.target === this.canvasEl().dom;
43666         if (wasCanvasTouched) {
43667             e.preventDefault();
43668             // var touch = event.changedTouches[0];
43669             // _this._strokeEnd(touch);
43670             this.strokeEnd(e);
43671         }
43672     },
43673     
43674     reset: function () {
43675         this._lastPoints = [];
43676         this._lastVelocity = 0;
43677         this._lastWidth = (this.min_width + this.max_width) / 2;
43678         this.canvasElCtx().fillStyle = this.dot_color;
43679     },
43680     
43681     strokeMoveUpdate: function(e)
43682     {
43683         this.strokeUpdate(e);
43684         
43685         if (this.throttle) {
43686             this.throttleStroke(this.strokeUpdate, this.throttle);
43687         }
43688         else {
43689             this.strokeUpdate(e);
43690         }
43691     },
43692     
43693     strokeBegin: function(e)
43694     {
43695         var newPointGroup = {
43696             color: this.dot_color,
43697             points: []
43698         };
43699         
43700         if (typeof this.onBegin === 'function') {
43701             this.onBegin(e);
43702         }
43703         
43704         this.curve_data.push(newPointGroup);
43705         this.reset();
43706         this.strokeUpdate(e);
43707     },
43708     
43709     strokeUpdate: function(e)
43710     {
43711         var rect = this.canvasEl().dom.getBoundingClientRect();
43712         var point = new this.Point(e.xy[0] - rect.left, e.xy[1] - rect.top, new Date().getTime());
43713         var lastPointGroup = this.curve_data[this.curve_data.length - 1];
43714         var lastPoints = lastPointGroup.points;
43715         var lastPoint = lastPoints.length > 0 && lastPoints[lastPoints.length - 1];
43716         var isLastPointTooClose = lastPoint
43717             ? point.distanceTo(lastPoint) <= this.min_distance
43718             : false;
43719         var color = lastPointGroup.color;
43720         if (!lastPoint || !(lastPoint && isLastPointTooClose)) {
43721             var curve = this.addPoint(point);
43722             if (!lastPoint) {
43723                 this.drawDot({color: color, point: point});
43724             }
43725             else if (curve) {
43726                 this.drawCurve({color: color, curve: curve});
43727             }
43728             lastPoints.push({
43729                 time: point.time,
43730                 x: point.x,
43731                 y: point.y
43732             });
43733         }
43734     },
43735     
43736     strokeEnd: function(e)
43737     {
43738         this.strokeUpdate(e);
43739         if (typeof this.onEnd === 'function') {
43740             this.onEnd(e);
43741         }
43742     },
43743     
43744     addPoint:  function (point) {
43745         var _lastPoints = this._lastPoints;
43746         _lastPoints.push(point);
43747         if (_lastPoints.length > 2) {
43748             if (_lastPoints.length === 3) {
43749                 _lastPoints.unshift(_lastPoints[0]);
43750             }
43751             var widths = this.calculateCurveWidths(_lastPoints[1], _lastPoints[2]);
43752             var curve = this.Bezier.fromPoints(_lastPoints, widths, this);
43753             _lastPoints.shift();
43754             return curve;
43755         }
43756         return null;
43757     },
43758     
43759     calculateCurveWidths: function (startPoint, endPoint) {
43760         var velocity = this.velocity_filter_weight * endPoint.velocityFrom(startPoint) +
43761             (1 - this.velocity_filter_weight) * this._lastVelocity;
43762
43763         var newWidth = Math.max(this.max_width / (velocity + 1), this.min_width);
43764         var widths = {
43765             end: newWidth,
43766             start: this._lastWidth
43767         };
43768         
43769         this._lastVelocity = velocity;
43770         this._lastWidth = newWidth;
43771         return widths;
43772     },
43773     
43774     drawDot: function (_a) {
43775         var color = _a.color, point = _a.point;
43776         var ctx = this.canvasElCtx();
43777         var width = typeof this.dot_size === 'function' ? this.dot_size() : this.dot_size;
43778         ctx.beginPath();
43779         this.drawCurveSegment(point.x, point.y, width);
43780         ctx.closePath();
43781         ctx.fillStyle = color;
43782         ctx.fill();
43783     },
43784     
43785     drawCurve: function (_a) {
43786         var color = _a.color, curve = _a.curve;
43787         var ctx = this.canvasElCtx();
43788         var widthDelta = curve.endWidth - curve.startWidth;
43789         var drawSteps = Math.floor(curve.length()) * 2;
43790         ctx.beginPath();
43791         ctx.fillStyle = color;
43792         for (var i = 0; i < drawSteps; i += 1) {
43793         var t = i / drawSteps;
43794         var tt = t * t;
43795         var ttt = tt * t;
43796         var u = 1 - t;
43797         var uu = u * u;
43798         var uuu = uu * u;
43799         var x = uuu * curve.startPoint.x;
43800         x += 3 * uu * t * curve.control1.x;
43801         x += 3 * u * tt * curve.control2.x;
43802         x += ttt * curve.endPoint.x;
43803         var y = uuu * curve.startPoint.y;
43804         y += 3 * uu * t * curve.control1.y;
43805         y += 3 * u * tt * curve.control2.y;
43806         y += ttt * curve.endPoint.y;
43807         var width = curve.startWidth + ttt * widthDelta;
43808         this.drawCurveSegment(x, y, width);
43809         }
43810         ctx.closePath();
43811         ctx.fill();
43812     },
43813     
43814     drawCurveSegment: function (x, y, width) {
43815         var ctx = this.canvasElCtx();
43816         ctx.moveTo(x, y);
43817         ctx.arc(x, y, width, 0, 2 * Math.PI, false);
43818         this.is_empty = false;
43819     },
43820     
43821     clear: function()
43822     {
43823         var ctx = this.canvasElCtx();
43824         var canvas = this.canvasEl().dom;
43825         ctx.fillStyle = this.bg_color;
43826         ctx.clearRect(0, 0, canvas.width, canvas.height);
43827         ctx.fillRect(0, 0, canvas.width, canvas.height);
43828         this.curve_data = [];
43829         this.reset();
43830         this.is_empty = true;
43831     },
43832     
43833     fileEl: function()
43834     {
43835         return  this.el.select('input',true).first();
43836     },
43837     
43838     canvasEl: function()
43839     {
43840         return this.el.select('canvas',true).first();
43841     },
43842     
43843     canvasElCtx: function()
43844     {
43845         return this.el.select('canvas',true).first().dom.getContext('2d');
43846     },
43847     
43848     getImage: function(type)
43849     {
43850         if(this.is_empty) {
43851             return false;
43852         }
43853         
43854         // encryption ?
43855         return this.canvasEl().dom.toDataURL('image/'+type, 1);
43856     },
43857     
43858     drawFromImage: function(img_src)
43859     {
43860         var img = new Image();
43861         
43862         img.onload = function(){
43863             this.canvasElCtx().drawImage(img, 0, 0);
43864         }.bind(this);
43865         
43866         img.src = img_src;
43867         
43868         this.is_empty = false;
43869     },
43870     
43871     selectImage: function()
43872     {
43873         this.fileEl().dom.click();
43874     },
43875     
43876     uploadImage: function(e)
43877     {
43878         var reader = new FileReader();
43879         
43880         reader.onload = function(e){
43881             var img = new Image();
43882             img.onload = function(){
43883                 this.reset();
43884                 this.canvasElCtx().drawImage(img, 0, 0);
43885             }.bind(this);
43886             img.src = e.target.result;
43887         }.bind(this);
43888         
43889         reader.readAsDataURL(e.target.files[0]);
43890     },
43891     
43892     // Bezier Point Constructor
43893     Point: (function () {
43894         function Point(x, y, time) {
43895             this.x = x;
43896             this.y = y;
43897             this.time = time || Date.now();
43898         }
43899         Point.prototype.distanceTo = function (start) {
43900             return Math.sqrt(Math.pow(this.x - start.x, 2) + Math.pow(this.y - start.y, 2));
43901         };
43902         Point.prototype.equals = function (other) {
43903             return this.x === other.x && this.y === other.y && this.time === other.time;
43904         };
43905         Point.prototype.velocityFrom = function (start) {
43906             return this.time !== start.time
43907             ? this.distanceTo(start) / (this.time - start.time)
43908             : 0;
43909         };
43910         return Point;
43911     }()),
43912     
43913     
43914     // Bezier Constructor
43915     Bezier: (function () {
43916         function Bezier(startPoint, control2, control1, endPoint, startWidth, endWidth) {
43917             this.startPoint = startPoint;
43918             this.control2 = control2;
43919             this.control1 = control1;
43920             this.endPoint = endPoint;
43921             this.startWidth = startWidth;
43922             this.endWidth = endWidth;
43923         }
43924         Bezier.fromPoints = function (points, widths, scope) {
43925             var c2 = this.calculateControlPoints(points[0], points[1], points[2], scope).c2;
43926             var c3 = this.calculateControlPoints(points[1], points[2], points[3], scope).c1;
43927             return new Bezier(points[1], c2, c3, points[2], widths.start, widths.end);
43928         };
43929         Bezier.calculateControlPoints = function (s1, s2, s3, scope) {
43930             var dx1 = s1.x - s2.x;
43931             var dy1 = s1.y - s2.y;
43932             var dx2 = s2.x - s3.x;
43933             var dy2 = s2.y - s3.y;
43934             var m1 = { x: (s1.x + s2.x) / 2.0, y: (s1.y + s2.y) / 2.0 };
43935             var m2 = { x: (s2.x + s3.x) / 2.0, y: (s2.y + s3.y) / 2.0 };
43936             var l1 = Math.sqrt(dx1 * dx1 + dy1 * dy1);
43937             var l2 = Math.sqrt(dx2 * dx2 + dy2 * dy2);
43938             var dxm = m1.x - m2.x;
43939             var dym = m1.y - m2.y;
43940             var k = l2 / (l1 + l2);
43941             var cm = { x: m2.x + dxm * k, y: m2.y + dym * k };
43942             var tx = s2.x - cm.x;
43943             var ty = s2.y - cm.y;
43944             return {
43945                 c1: new scope.Point(m1.x + tx, m1.y + ty),
43946                 c2: new scope.Point(m2.x + tx, m2.y + ty)
43947             };
43948         };
43949         Bezier.prototype.length = function () {
43950             var steps = 10;
43951             var length = 0;
43952             var px;
43953             var py;
43954             for (var i = 0; i <= steps; i += 1) {
43955                 var t = i / steps;
43956                 var cx = this.point(t, this.startPoint.x, this.control1.x, this.control2.x, this.endPoint.x);
43957                 var cy = this.point(t, this.startPoint.y, this.control1.y, this.control2.y, this.endPoint.y);
43958                 if (i > 0) {
43959                     var xdiff = cx - px;
43960                     var ydiff = cy - py;
43961                     length += Math.sqrt(xdiff * xdiff + ydiff * ydiff);
43962                 }
43963                 px = cx;
43964                 py = cy;
43965             }
43966             return length;
43967         };
43968         Bezier.prototype.point = function (t, start, c1, c2, end) {
43969             return (start * (1.0 - t) * (1.0 - t) * (1.0 - t))
43970             + (3.0 * c1 * (1.0 - t) * (1.0 - t) * t)
43971             + (3.0 * c2 * (1.0 - t) * t * t)
43972             + (end * t * t * t);
43973         };
43974         return Bezier;
43975     }()),
43976     
43977     throttleStroke: function(fn, wait) {
43978       if (wait === void 0) { wait = 250; }
43979       var previous = 0;
43980       var timeout = null;
43981       var result;
43982       var storedContext;
43983       var storedArgs;
43984       var later = function () {
43985           previous = Date.now();
43986           timeout = null;
43987           result = fn.apply(storedContext, storedArgs);
43988           if (!timeout) {
43989               storedContext = null;
43990               storedArgs = [];
43991           }
43992       };
43993       return function wrapper() {
43994           var args = [];
43995           for (var _i = 0; _i < arguments.length; _i++) {
43996               args[_i] = arguments[_i];
43997           }
43998           var now = Date.now();
43999           var remaining = wait - (now - previous);
44000           storedContext = this;
44001           storedArgs = args;
44002           if (remaining <= 0 || remaining > wait) {
44003               if (timeout) {
44004                   clearTimeout(timeout);
44005                   timeout = null;
44006               }
44007               previous = now;
44008               result = fn.apply(storedContext, storedArgs);
44009               if (!timeout) {
44010                   storedContext = null;
44011                   storedArgs = [];
44012               }
44013           }
44014           else if (!timeout) {
44015               timeout = window.setTimeout(later, remaining);
44016           }
44017           return result;
44018       };
44019   }
44020   
44021 });
44022
44023  
44024
44025