48c3b0b07ee651f1385f8ebf719e3069a906a584
[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['margin' + (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         var v = this.getValue();
26146         if (v === false) {
26147             v = '';
26148         }
26149         this.markdownEl.dom.innerHTML = Roo.Markdown.toHtml(Roo.util.Format.htmlEncode(v));
26150         this.markdownEl.on('click', this.toggleTextEdit, this);
26151         this.on('blur', this.toggleTextEdit, this);
26152         this.on('specialkey', this.resizeTextArea, this);
26153     },
26154     
26155     toggleTextEdit : function()
26156     {
26157         var sh = this.markdownEl.getHeight();
26158         this.inputEl().addClass('d-none');
26159         this.markdownEl.addClass('d-none');
26160         if (!this.editing) {
26161             // show editor?
26162             this.inputEl().setHeight(Math.min(500, Math.max(sh,(this.getValue().split("\n").length+1) * 30)));
26163             this.inputEl().removeClass('d-none');
26164             this.inputEl().focus();
26165             this.editing = true;
26166             return;
26167         }
26168         // show showdown...
26169         this.updateMarkdown();
26170         this.markdownEl.removeClass('d-none');
26171         this.editing = false;
26172         return;
26173     },
26174     updateMarkdown : function()
26175     {
26176         if (this.getValue() == '') {
26177             this.markdownEl.dom.innerHTML = String.format('<span class="roo-placeholder">{0}</span>', this.placeholder || '');
26178             return;
26179         }
26180          var v = this.getValue();
26181         if (v === false) {
26182             v = '';
26183         }
26184         this.markdownEl.dom.innerHTML = Roo.Markdown.toHtml(Roo.util.Format.htmlEncode(v));
26185     },
26186     
26187     resizeTextArea: function () {
26188         
26189         var sh = 100;
26190         Roo.log([sh, this.getValue().split("\n").length * 30]);
26191         this.inputEl().setHeight(Math.min(500, Math.max(sh, (this.getValue().split("\n").length +1) * 30)));
26192     },
26193     setValue : function(val)
26194     {
26195         Roo.bootstrap.TextArea.prototype.setValue.call(this,val);
26196         if (!this.editing) {
26197             this.updateMarkdown();
26198         }
26199         
26200     },
26201     focus : function()
26202     {
26203         if (!this.editing) {
26204             this.toggleTextEdit();
26205         }
26206         
26207     }
26208
26209
26210 });
26211 /**
26212  * @class Roo.bootstrap.Table.AbstractSelectionModel
26213  * @extends Roo.util.Observable
26214  * Abstract base class for grid SelectionModels.  It provides the interface that should be
26215  * implemented by descendant classes.  This class should not be directly instantiated.
26216  * @constructor
26217  */
26218 Roo.bootstrap.Table.AbstractSelectionModel = function(){
26219     this.locked = false;
26220     Roo.bootstrap.Table.AbstractSelectionModel.superclass.constructor.call(this);
26221 };
26222
26223
26224 Roo.extend(Roo.bootstrap.Table.AbstractSelectionModel, Roo.util.Observable,  {
26225     /** @ignore Called by the grid automatically. Do not call directly. */
26226     init : function(grid){
26227         this.grid = grid;
26228         this.initEvents();
26229     },
26230
26231     /**
26232      * Locks the selections.
26233      */
26234     lock : function(){
26235         this.locked = true;
26236     },
26237
26238     /**
26239      * Unlocks the selections.
26240      */
26241     unlock : function(){
26242         this.locked = false;
26243     },
26244
26245     /**
26246      * Returns true if the selections are locked.
26247      * @return {Boolean}
26248      */
26249     isLocked : function(){
26250         return this.locked;
26251     },
26252     
26253     
26254     initEvents : function ()
26255     {
26256         
26257     }
26258 });
26259 /**
26260  * @extends Roo.bootstrap.Table.AbstractSelectionModel
26261  * @class Roo.bootstrap.Table.RowSelectionModel
26262  * The default SelectionModel used by {@link Roo.bootstrap.Table}.
26263  * It supports multiple selections and keyboard selection/navigation. 
26264  * @constructor
26265  * @param {Object} config
26266  */
26267
26268 Roo.bootstrap.Table.RowSelectionModel = function(config){
26269     Roo.apply(this, config);
26270     this.selections = new Roo.util.MixedCollection(false, function(o){
26271         return o.id;
26272     });
26273
26274     this.last = false;
26275     this.lastActive = false;
26276
26277     this.addEvents({
26278         /**
26279              * @event selectionchange
26280              * Fires when the selection changes
26281              * @param {SelectionModel} this
26282              */
26283             "selectionchange" : true,
26284         /**
26285              * @event afterselectionchange
26286              * Fires after the selection changes (eg. by key press or clicking)
26287              * @param {SelectionModel} this
26288              */
26289             "afterselectionchange" : true,
26290         /**
26291              * @event beforerowselect
26292              * Fires when a row is selected being selected, return false to cancel.
26293              * @param {SelectionModel} this
26294              * @param {Number} rowIndex The selected index
26295              * @param {Boolean} keepExisting False if other selections will be cleared
26296              */
26297             "beforerowselect" : true,
26298         /**
26299              * @event rowselect
26300              * Fires when a row is selected.
26301              * @param {SelectionModel} this
26302              * @param {Number} rowIndex The selected index
26303              * @param {Roo.data.Record} r The record
26304              */
26305             "rowselect" : true,
26306         /**
26307              * @event rowdeselect
26308              * Fires when a row is deselected.
26309              * @param {SelectionModel} this
26310              * @param {Number} rowIndex The selected index
26311              */
26312         "rowdeselect" : true
26313     });
26314     Roo.bootstrap.Table.RowSelectionModel.superclass.constructor.call(this);
26315     this.locked = false;
26316  };
26317
26318 Roo.extend(Roo.bootstrap.Table.RowSelectionModel, Roo.bootstrap.Table.AbstractSelectionModel,  {
26319     /**
26320      * @cfg {Boolean} singleSelect
26321      * True to allow selection of only one row at a time (defaults to false)
26322      */
26323     singleSelect : false,
26324
26325     // private
26326     initEvents : function()
26327     {
26328
26329         //if(!this.grid.enableDragDrop && !this.grid.enableDrag){
26330         //    this.growclickrid.on("mousedown", this.handleMouseDown, this);
26331         //}else{ // allow click to work like normal
26332          //   this.grid.on("rowclick", this.handleDragableRowClick, this);
26333         //}
26334         //this.grid.on("rowdblclick", this.handleMouseDBClick, this);
26335         this.grid.on("rowclick", this.handleMouseDown, this);
26336         
26337         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
26338             "up" : function(e){
26339                 if(!e.shiftKey){
26340                     this.selectPrevious(e.shiftKey);
26341                 }else if(this.last !== false && this.lastActive !== false){
26342                     var last = this.last;
26343                     this.selectRange(this.last,  this.lastActive-1);
26344                     this.grid.getView().focusRow(this.lastActive);
26345                     if(last !== false){
26346                         this.last = last;
26347                     }
26348                 }else{
26349                     this.selectFirstRow();
26350                 }
26351                 this.fireEvent("afterselectionchange", this);
26352             },
26353             "down" : function(e){
26354                 if(!e.shiftKey){
26355                     this.selectNext(e.shiftKey);
26356                 }else if(this.last !== false && this.lastActive !== false){
26357                     var last = this.last;
26358                     this.selectRange(this.last,  this.lastActive+1);
26359                     this.grid.getView().focusRow(this.lastActive);
26360                     if(last !== false){
26361                         this.last = last;
26362                     }
26363                 }else{
26364                     this.selectFirstRow();
26365                 }
26366                 this.fireEvent("afterselectionchange", this);
26367             },
26368             scope: this
26369         });
26370         this.grid.store.on('load', function(){
26371             this.selections.clear();
26372         },this);
26373         /*
26374         var view = this.grid.view;
26375         view.on("refresh", this.onRefresh, this);
26376         view.on("rowupdated", this.onRowUpdated, this);
26377         view.on("rowremoved", this.onRemove, this);
26378         */
26379     },
26380
26381     // private
26382     onRefresh : function()
26383     {
26384         var ds = this.grid.store, i, v = this.grid.view;
26385         var s = this.selections;
26386         s.each(function(r){
26387             if((i = ds.indexOfId(r.id)) != -1){
26388                 v.onRowSelect(i);
26389             }else{
26390                 s.remove(r);
26391             }
26392         });
26393     },
26394
26395     // private
26396     onRemove : function(v, index, r){
26397         this.selections.remove(r);
26398     },
26399
26400     // private
26401     onRowUpdated : function(v, index, r){
26402         if(this.isSelected(r)){
26403             v.onRowSelect(index);
26404         }
26405     },
26406
26407     /**
26408      * Select records.
26409      * @param {Array} records The records to select
26410      * @param {Boolean} keepExisting (optional) True to keep existing selections
26411      */
26412     selectRecords : function(records, keepExisting)
26413     {
26414         if(!keepExisting){
26415             this.clearSelections();
26416         }
26417             var ds = this.grid.store;
26418         for(var i = 0, len = records.length; i < len; i++){
26419             this.selectRow(ds.indexOf(records[i]), true);
26420         }
26421     },
26422
26423     /**
26424      * Gets the number of selected rows.
26425      * @return {Number}
26426      */
26427     getCount : function(){
26428         return this.selections.length;
26429     },
26430
26431     /**
26432      * Selects the first row in the grid.
26433      */
26434     selectFirstRow : function(){
26435         this.selectRow(0);
26436     },
26437
26438     /**
26439      * Select the last row.
26440      * @param {Boolean} keepExisting (optional) True to keep existing selections
26441      */
26442     selectLastRow : function(keepExisting){
26443         //this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
26444         this.selectRow(this.grid.store.getCount() - 1, keepExisting);
26445     },
26446
26447     /**
26448      * Selects the row immediately following the last selected row.
26449      * @param {Boolean} keepExisting (optional) True to keep existing selections
26450      */
26451     selectNext : function(keepExisting)
26452     {
26453             if(this.last !== false && (this.last+1) < this.grid.store.getCount()){
26454             this.selectRow(this.last+1, keepExisting);
26455             this.grid.getView().focusRow(this.last);
26456         }
26457     },
26458
26459     /**
26460      * Selects the row that precedes the last selected row.
26461      * @param {Boolean} keepExisting (optional) True to keep existing selections
26462      */
26463     selectPrevious : function(keepExisting){
26464         if(this.last){
26465             this.selectRow(this.last-1, keepExisting);
26466             this.grid.getView().focusRow(this.last);
26467         }
26468     },
26469
26470     /**
26471      * Returns the selected records
26472      * @return {Array} Array of selected records
26473      */
26474     getSelections : function(){
26475         return [].concat(this.selections.items);
26476     },
26477
26478     /**
26479      * Returns the first selected record.
26480      * @return {Record}
26481      */
26482     getSelected : function(){
26483         return this.selections.itemAt(0);
26484     },
26485
26486
26487     /**
26488      * Clears all selections.
26489      */
26490     clearSelections : function(fast)
26491     {
26492         if(this.locked) {
26493             return;
26494         }
26495         if(fast !== true){
26496                 var ds = this.grid.store;
26497             var s = this.selections;
26498             s.each(function(r){
26499                 this.deselectRow(ds.indexOfId(r.id));
26500             }, this);
26501             s.clear();
26502         }else{
26503             this.selections.clear();
26504         }
26505         this.last = false;
26506     },
26507
26508
26509     /**
26510      * Selects all rows.
26511      */
26512     selectAll : function(){
26513         if(this.locked) {
26514             return;
26515         }
26516         this.selections.clear();
26517         for(var i = 0, len = this.grid.store.getCount(); i < len; i++){
26518             this.selectRow(i, true);
26519         }
26520     },
26521
26522     /**
26523      * Returns True if there is a selection.
26524      * @return {Boolean}
26525      */
26526     hasSelection : function(){
26527         return this.selections.length > 0;
26528     },
26529
26530     /**
26531      * Returns True if the specified row is selected.
26532      * @param {Number/Record} record The record or index of the record to check
26533      * @return {Boolean}
26534      */
26535     isSelected : function(index){
26536             var r = typeof index == "number" ? this.grid.store.getAt(index) : index;
26537         return (r && this.selections.key(r.id) ? true : false);
26538     },
26539
26540     /**
26541      * Returns True if the specified record id is selected.
26542      * @param {String} id The id of record to check
26543      * @return {Boolean}
26544      */
26545     isIdSelected : function(id){
26546         return (this.selections.key(id) ? true : false);
26547     },
26548
26549
26550     // private
26551     handleMouseDBClick : function(e, t){
26552         
26553     },
26554     // private
26555     handleMouseDown : function(e, t)
26556     {
26557             var rowIndex = this.grid.headerShow  ? t.dom.rowIndex - 1 : t.dom.rowIndex ; // first row is header???
26558         if(this.isLocked() || rowIndex < 0 ){
26559             return;
26560         };
26561         if(e.shiftKey && this.last !== false){
26562             var last = this.last;
26563             this.selectRange(last, rowIndex, e.ctrlKey);
26564             this.last = last; // reset the last
26565             t.focus();
26566     
26567         }else{
26568             var isSelected = this.isSelected(rowIndex);
26569             //Roo.log("select row:" + rowIndex);
26570             if(isSelected){
26571                 this.deselectRow(rowIndex);
26572             } else {
26573                         this.selectRow(rowIndex, true);
26574             }
26575     
26576             /*
26577                 if(e.button !== 0 && isSelected){
26578                 alert('rowIndex 2: ' + rowIndex);
26579                     view.focusRow(rowIndex);
26580                 }else if(e.ctrlKey && isSelected){
26581                     this.deselectRow(rowIndex);
26582                 }else if(!isSelected){
26583                     this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
26584                     view.focusRow(rowIndex);
26585                 }
26586             */
26587         }
26588         this.fireEvent("afterselectionchange", this);
26589     },
26590     // private
26591     handleDragableRowClick :  function(grid, rowIndex, e) 
26592     {
26593         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
26594             this.selectRow(rowIndex, false);
26595             grid.view.focusRow(rowIndex);
26596              this.fireEvent("afterselectionchange", this);
26597         }
26598     },
26599     
26600     /**
26601      * Selects multiple rows.
26602      * @param {Array} rows Array of the indexes of the row to select
26603      * @param {Boolean} keepExisting (optional) True to keep existing selections
26604      */
26605     selectRows : function(rows, keepExisting){
26606         if(!keepExisting){
26607             this.clearSelections();
26608         }
26609         for(var i = 0, len = rows.length; i < len; i++){
26610             this.selectRow(rows[i], true);
26611         }
26612     },
26613
26614     /**
26615      * Selects a range of rows. All rows in between startRow and endRow are also selected.
26616      * @param {Number} startRow The index of the first row in the range
26617      * @param {Number} endRow The index of the last row in the range
26618      * @param {Boolean} keepExisting (optional) True to retain existing selections
26619      */
26620     selectRange : function(startRow, endRow, keepExisting){
26621         if(this.locked) {
26622             return;
26623         }
26624         if(!keepExisting){
26625             this.clearSelections();
26626         }
26627         if(startRow <= endRow){
26628             for(var i = startRow; i <= endRow; i++){
26629                 this.selectRow(i, true);
26630             }
26631         }else{
26632             for(var i = startRow; i >= endRow; i--){
26633                 this.selectRow(i, true);
26634             }
26635         }
26636     },
26637
26638     /**
26639      * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
26640      * @param {Number} startRow The index of the first row in the range
26641      * @param {Number} endRow The index of the last row in the range
26642      */
26643     deselectRange : function(startRow, endRow, preventViewNotify){
26644         if(this.locked) {
26645             return;
26646         }
26647         for(var i = startRow; i <= endRow; i++){
26648             this.deselectRow(i, preventViewNotify);
26649         }
26650     },
26651
26652     /**
26653      * Selects a row.
26654      * @param {Number} row The index of the row to select
26655      * @param {Boolean} keepExisting (optional) True to keep existing selections
26656      */
26657     selectRow : function(index, keepExisting, preventViewNotify)
26658     {
26659             if(this.locked || (index < 0 || index > this.grid.store.getCount())) {
26660             return;
26661         }
26662         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
26663             if(!keepExisting || this.singleSelect){
26664                 this.clearSelections();
26665             }
26666             
26667             var r = this.grid.store.getAt(index);
26668             //console.log('selectRow - record id :' + r.id);
26669             
26670             this.selections.add(r);
26671             this.last = this.lastActive = index;
26672             if(!preventViewNotify){
26673                 var proxy = new Roo.Element(
26674                                 this.grid.getRowDom(index)
26675                 );
26676                 proxy.addClass('bg-info info');
26677             }
26678             this.fireEvent("rowselect", this, index, r);
26679             this.fireEvent("selectionchange", this);
26680         }
26681     },
26682
26683     /**
26684      * Deselects a row.
26685      * @param {Number} row The index of the row to deselect
26686      */
26687     deselectRow : function(index, preventViewNotify)
26688     {
26689         if(this.locked) {
26690             return;
26691         }
26692         if(this.last == index){
26693             this.last = false;
26694         }
26695         if(this.lastActive == index){
26696             this.lastActive = false;
26697         }
26698         
26699         var r = this.grid.store.getAt(index);
26700         if (!r) {
26701             return;
26702         }
26703         
26704         this.selections.remove(r);
26705         //.console.log('deselectRow - record id :' + r.id);
26706         if(!preventViewNotify){
26707         
26708             var proxy = new Roo.Element(
26709                 this.grid.getRowDom(index)
26710             );
26711             proxy.removeClass('bg-info info');
26712         }
26713         this.fireEvent("rowdeselect", this, index);
26714         this.fireEvent("selectionchange", this);
26715     },
26716
26717     // private
26718     restoreLast : function(){
26719         if(this._last){
26720             this.last = this._last;
26721         }
26722     },
26723
26724     // private
26725     acceptsNav : function(row, col, cm){
26726         return !cm.isHidden(col) && cm.isCellEditable(col, row);
26727     },
26728
26729     // private
26730     onEditorKey : function(field, e){
26731         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
26732         if(k == e.TAB){
26733             e.stopEvent();
26734             ed.completeEdit();
26735             if(e.shiftKey){
26736                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
26737             }else{
26738                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
26739             }
26740         }else if(k == e.ENTER && !e.ctrlKey){
26741             e.stopEvent();
26742             ed.completeEdit();
26743             if(e.shiftKey){
26744                 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
26745             }else{
26746                 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
26747             }
26748         }else if(k == e.ESC){
26749             ed.cancelEdit();
26750         }
26751         if(newCell){
26752             g.startEditing(newCell[0], newCell[1]);
26753         }
26754     }
26755 });
26756 /*
26757  * Based on:
26758  * Ext JS Library 1.1.1
26759  * Copyright(c) 2006-2007, Ext JS, LLC.
26760  *
26761  * Originally Released Under LGPL - original licence link has changed is not relivant.
26762  *
26763  * Fork - LGPL
26764  * <script type="text/javascript">
26765  */
26766  
26767 /**
26768  * @class Roo.bootstrap.PagingToolbar
26769  * @extends Roo.bootstrap.NavSimplebar
26770  * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
26771  * @constructor
26772  * Create a new PagingToolbar
26773  * @param {Object} config The config object
26774  * @param {Roo.data.Store} store
26775  */
26776 Roo.bootstrap.PagingToolbar = function(config)
26777 {
26778     // old args format still supported... - xtype is prefered..
26779         // created from xtype...
26780     
26781     this.ds = config.dataSource;
26782     
26783     if (config.store && !this.ds) {
26784         this.store= Roo.factory(config.store, Roo.data);
26785         this.ds = this.store;
26786         this.ds.xmodule = this.xmodule || false;
26787     }
26788     
26789     this.toolbarItems = [];
26790     if (config.items) {
26791         this.toolbarItems = config.items;
26792     }
26793     
26794     Roo.bootstrap.PagingToolbar.superclass.constructor.call(this, config);
26795     
26796     this.cursor = 0;
26797     
26798     if (this.ds) { 
26799         this.bind(this.ds);
26800     }
26801     
26802     if (Roo.bootstrap.version == 4) {
26803         this.navgroup = new Roo.bootstrap.ButtonGroup({ cls: 'pagination' });
26804     } else {
26805         this.navgroup = new Roo.bootstrap.NavGroup({ cls: 'pagination' });
26806     }
26807     
26808 };
26809
26810 Roo.extend(Roo.bootstrap.PagingToolbar, Roo.bootstrap.NavSimplebar, {
26811     /**
26812      * @cfg {Roo.data.Store} dataSource
26813      * The underlying data store providing the paged data
26814      */
26815     /**
26816      * @cfg {String/HTMLElement/Element} container
26817      * container The id or element that will contain the toolbar
26818      */
26819     /**
26820      * @cfg {Boolean} displayInfo
26821      * True to display the displayMsg (defaults to false)
26822      */
26823     /**
26824      * @cfg {Number} pageSize
26825      * The number of records to display per page (defaults to 20)
26826      */
26827     pageSize: 20,
26828     /**
26829      * @cfg {String} displayMsg
26830      * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
26831      */
26832     displayMsg : 'Displaying {0} - {1} of {2}',
26833     /**
26834      * @cfg {String} emptyMsg
26835      * The message to display when no records are found (defaults to "No data to display")
26836      */
26837     emptyMsg : 'No data to display',
26838     /**
26839      * Customizable piece of the default paging text (defaults to "Page")
26840      * @type String
26841      */
26842     beforePageText : "Page",
26843     /**
26844      * Customizable piece of the default paging text (defaults to "of %0")
26845      * @type String
26846      */
26847     afterPageText : "of {0}",
26848     /**
26849      * Customizable piece of the default paging text (defaults to "First Page")
26850      * @type String
26851      */
26852     firstText : "First Page",
26853     /**
26854      * Customizable piece of the default paging text (defaults to "Previous Page")
26855      * @type String
26856      */
26857     prevText : "Previous Page",
26858     /**
26859      * Customizable piece of the default paging text (defaults to "Next Page")
26860      * @type String
26861      */
26862     nextText : "Next Page",
26863     /**
26864      * Customizable piece of the default paging text (defaults to "Last Page")
26865      * @type String
26866      */
26867     lastText : "Last Page",
26868     /**
26869      * Customizable piece of the default paging text (defaults to "Refresh")
26870      * @type String
26871      */
26872     refreshText : "Refresh",
26873
26874     buttons : false,
26875     // private
26876     onRender : function(ct, position) 
26877     {
26878         Roo.bootstrap.PagingToolbar.superclass.onRender.call(this, ct, position);
26879         this.navgroup.parentId = this.id;
26880         this.navgroup.onRender(this.el, null);
26881         // add the buttons to the navgroup
26882         
26883         if(this.displayInfo){
26884             this.el.select('ul.navbar-nav',true).first().createChild({cls:'x-paging-info'});
26885             this.displayEl = this.el.select('.x-paging-info', true).first();
26886 //            var navel = this.navgroup.addItem( { tagtype : 'span', html : '', cls : 'x-paging-info', preventDefault : true } );
26887 //            this.displayEl = navel.el.select('span',true).first();
26888         }
26889         
26890         var _this = this;
26891         
26892         if(this.buttons){
26893             Roo.each(_this.buttons, function(e){ // this might need to use render????
26894                Roo.factory(e).render(_this.el);
26895             });
26896         }
26897             
26898         Roo.each(_this.toolbarItems, function(e) {
26899             _this.navgroup.addItem(e);
26900         });
26901         
26902         
26903         this.first = this.navgroup.addItem({
26904             tooltip: this.firstText,
26905             cls: "prev btn-outline-secondary",
26906             html : ' <i class="fa fa-step-backward"></i>',
26907             disabled: true,
26908             preventDefault: true,
26909             listeners : { click : this.onClick.createDelegate(this, ["first"]) }
26910         });
26911         
26912         this.prev =  this.navgroup.addItem({
26913             tooltip: this.prevText,
26914             cls: "prev btn-outline-secondary",
26915             html : ' <i class="fa fa-backward"></i>',
26916             disabled: true,
26917             preventDefault: true,
26918             listeners : { click :  this.onClick.createDelegate(this, ["prev"]) }
26919         });
26920     //this.addSeparator();
26921         
26922         
26923         var field = this.navgroup.addItem( {
26924             tagtype : 'span',
26925             cls : 'x-paging-position  btn-outline-secondary',
26926              disabled: true,
26927             html : this.beforePageText  +
26928                 '<input type="text" size="3" value="1" class="x-grid-page-number">' +
26929                 '<span class="x-paging-after">' +  String.format(this.afterPageText, 1) + '</span>'
26930          } ); //?? escaped?
26931         
26932         this.field = field.el.select('input', true).first();
26933         this.field.on("keydown", this.onPagingKeydown, this);
26934         this.field.on("focus", function(){this.dom.select();});
26935     
26936     
26937         this.afterTextEl =  field.el.select('.x-paging-after',true).first();
26938         //this.field.setHeight(18);
26939         //this.addSeparator();
26940         this.next = this.navgroup.addItem({
26941             tooltip: this.nextText,
26942             cls: "next btn-outline-secondary",
26943             html : ' <i class="fa fa-forward"></i>',
26944             disabled: true,
26945             preventDefault: true,
26946             listeners : { click :  this.onClick.createDelegate(this, ["next"]) }
26947         });
26948         this.last = this.navgroup.addItem({
26949             tooltip: this.lastText,
26950             html : ' <i class="fa fa-step-forward"></i>',
26951             cls: "next btn-outline-secondary",
26952             disabled: true,
26953             preventDefault: true,
26954             listeners : { click :  this.onClick.createDelegate(this, ["last"]) }
26955         });
26956     //this.addSeparator();
26957         this.loading = this.navgroup.addItem({
26958             tooltip: this.refreshText,
26959             cls: "btn-outline-secondary",
26960             html : ' <i class="fa fa-refresh"></i>',
26961             preventDefault: true,
26962             listeners : { click : this.onClick.createDelegate(this, ["refresh"]) }
26963         });
26964         
26965     },
26966
26967     // private
26968     updateInfo : function(){
26969         if(this.displayEl){
26970             var count = (typeof(this.getCount) == 'undefined') ? this.ds.getCount() : this.getCount();
26971             var msg = count == 0 ?
26972                 this.emptyMsg :
26973                 String.format(
26974                     this.displayMsg,
26975                     this.cursor+1, this.cursor+count, this.ds.getTotalCount()    
26976                 );
26977             this.displayEl.update(msg);
26978         }
26979     },
26980
26981     // private
26982     onLoad : function(ds, r, o)
26983     {
26984         this.cursor = o.params.start ? o.params.start : 0;
26985         
26986         var d = this.getPageData(),
26987             ap = d.activePage,
26988             ps = d.pages;
26989         
26990         
26991         this.afterTextEl.dom.innerHTML = String.format(this.afterPageText, d.pages);
26992         this.field.dom.value = ap;
26993         this.first.setDisabled(ap == 1);
26994         this.prev.setDisabled(ap == 1);
26995         this.next.setDisabled(ap == ps);
26996         this.last.setDisabled(ap == ps);
26997         this.loading.enable();
26998         this.updateInfo();
26999     },
27000
27001     // private
27002     getPageData : function(){
27003         var total = this.ds.getTotalCount();
27004         return {
27005             total : total,
27006             activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
27007             pages :  total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
27008         };
27009     },
27010
27011     // private
27012     onLoadError : function(){
27013         this.loading.enable();
27014     },
27015
27016     // private
27017     onPagingKeydown : function(e){
27018         var k = e.getKey();
27019         var d = this.getPageData();
27020         if(k == e.RETURN){
27021             var v = this.field.dom.value, pageNum;
27022             if(!v || isNaN(pageNum = parseInt(v, 10))){
27023                 this.field.dom.value = d.activePage;
27024                 return;
27025             }
27026             pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
27027             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
27028             e.stopEvent();
27029         }
27030         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))
27031         {
27032           var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
27033           this.field.dom.value = pageNum;
27034           this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
27035           e.stopEvent();
27036         }
27037         else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
27038         {
27039           var v = this.field.dom.value, pageNum; 
27040           var increment = (e.shiftKey) ? 10 : 1;
27041           if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN) {
27042                 increment *= -1;
27043           }
27044           if(!v || isNaN(pageNum = parseInt(v, 10))) {
27045             this.field.dom.value = d.activePage;
27046             return;
27047           }
27048           else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
27049           {
27050             this.field.dom.value = parseInt(v, 10) + increment;
27051             pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
27052             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
27053           }
27054           e.stopEvent();
27055         }
27056     },
27057
27058     // private
27059     beforeLoad : function(){
27060         if(this.loading){
27061             this.loading.disable();
27062         }
27063     },
27064
27065     // private
27066     onClick : function(which){
27067         
27068         var ds = this.ds;
27069         if (!ds) {
27070             return;
27071         }
27072         
27073         switch(which){
27074             case "first":
27075                 ds.load({params:{start: 0, limit: this.pageSize}});
27076             break;
27077             case "prev":
27078                 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
27079             break;
27080             case "next":
27081                 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
27082             break;
27083             case "last":
27084                 var total = ds.getTotalCount();
27085                 var extra = total % this.pageSize;
27086                 var lastStart = extra ? (total - extra) : total-this.pageSize;
27087                 ds.load({params:{start: lastStart, limit: this.pageSize}});
27088             break;
27089             case "refresh":
27090                 ds.load({params:{start: this.cursor, limit: this.pageSize}});
27091             break;
27092         }
27093     },
27094
27095     /**
27096      * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
27097      * @param {Roo.data.Store} store The data store to unbind
27098      */
27099     unbind : function(ds){
27100         ds.un("beforeload", this.beforeLoad, this);
27101         ds.un("load", this.onLoad, this);
27102         ds.un("loadexception", this.onLoadError, this);
27103         ds.un("remove", this.updateInfo, this);
27104         ds.un("add", this.updateInfo, this);
27105         this.ds = undefined;
27106     },
27107
27108     /**
27109      * Binds the paging toolbar to the specified {@link Roo.data.Store}
27110      * @param {Roo.data.Store} store The data store to bind
27111      */
27112     bind : function(ds){
27113         ds.on("beforeload", this.beforeLoad, this);
27114         ds.on("load", this.onLoad, this);
27115         ds.on("loadexception", this.onLoadError, this);
27116         ds.on("remove", this.updateInfo, this);
27117         ds.on("add", this.updateInfo, this);
27118         this.ds = ds;
27119     }
27120 });/*
27121  * - LGPL
27122  *
27123  * element
27124  * 
27125  */
27126
27127 /**
27128  * @class Roo.bootstrap.MessageBar
27129  * @extends Roo.bootstrap.Component
27130  * Bootstrap MessageBar class
27131  * @cfg {String} html contents of the MessageBar
27132  * @cfg {String} weight (info | success | warning | danger) default info
27133  * @cfg {String} beforeClass insert the bar before the given class
27134  * @cfg {Boolean} closable (true | false) default false
27135  * @cfg {Boolean} fixed (true | false) default false, fix the bar at the top
27136  * 
27137  * @constructor
27138  * Create a new Element
27139  * @param {Object} config The config object
27140  */
27141
27142 Roo.bootstrap.MessageBar = function(config){
27143     Roo.bootstrap.MessageBar.superclass.constructor.call(this, config);
27144 };
27145
27146 Roo.extend(Roo.bootstrap.MessageBar, Roo.bootstrap.Component,  {
27147     
27148     html: '',
27149     weight: 'info',
27150     closable: false,
27151     fixed: false,
27152     beforeClass: 'bootstrap-sticky-wrap',
27153     
27154     getAutoCreate : function(){
27155         
27156         var cfg = {
27157             tag: 'div',
27158             cls: 'alert alert-dismissable alert-' + this.weight,
27159             cn: [
27160                 {
27161                     tag: 'span',
27162                     cls: 'message',
27163                     html: this.html || ''
27164                 }
27165             ]
27166         };
27167         
27168         if(this.fixed){
27169             cfg.cls += ' alert-messages-fixed';
27170         }
27171         
27172         if(this.closable){
27173             cfg.cn.push({
27174                 tag: 'button',
27175                 cls: 'close',
27176                 html: 'x'
27177             });
27178         }
27179         
27180         return cfg;
27181     },
27182     
27183     onRender : function(ct, position)
27184     {
27185         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
27186         
27187         if(!this.el){
27188             var cfg = Roo.apply({},  this.getAutoCreate());
27189             cfg.id = Roo.id();
27190             
27191             if (this.cls) {
27192                 cfg.cls += ' ' + this.cls;
27193             }
27194             if (this.style) {
27195                 cfg.style = this.style;
27196             }
27197             this.el = Roo.get(document.body).createChild(cfg, Roo.select('.'+this.beforeClass, true).first());
27198             
27199             this.el.setVisibilityMode(Roo.Element.DISPLAY);
27200         }
27201         
27202         this.el.select('>button.close').on('click', this.hide, this);
27203         
27204     },
27205     
27206     show : function()
27207     {
27208         if (!this.rendered) {
27209             this.render();
27210         }
27211         
27212         this.el.show();
27213         
27214         this.fireEvent('show', this);
27215         
27216     },
27217     
27218     hide : function()
27219     {
27220         if (!this.rendered) {
27221             this.render();
27222         }
27223         
27224         this.el.hide();
27225         
27226         this.fireEvent('hide', this);
27227     },
27228     
27229     update : function()
27230     {
27231 //        var e = this.el.dom.firstChild;
27232 //        
27233 //        if(this.closable){
27234 //            e = e.nextSibling;
27235 //        }
27236 //        
27237 //        e.data = this.html || '';
27238
27239         this.el.select('>.message', true).first().dom.innerHTML = this.html || '';
27240     }
27241    
27242 });
27243
27244  
27245
27246      /*
27247  * - LGPL
27248  *
27249  * Graph
27250  * 
27251  */
27252
27253
27254 /**
27255  * @class Roo.bootstrap.Graph
27256  * @extends Roo.bootstrap.Component
27257  * Bootstrap Graph class
27258 > Prameters
27259  -sm {number} sm 4
27260  -md {number} md 5
27261  @cfg {String} graphtype  bar | vbar | pie
27262  @cfg {number} g_x coodinator | centre x (pie)
27263  @cfg {number} g_y coodinator | centre y (pie)
27264  @cfg {number} g_r radius (pie)
27265  @cfg {number} g_height height of the chart (respected by all elements in the set)
27266  @cfg {number} g_width width of the chart (respected by all elements in the set)
27267  @cfg {Object} title The title of the chart
27268     
27269  -{Array}  values
27270  -opts (object) options for the chart 
27271      o {
27272      o type (string) type of endings of the bar. Default: 'square'. Other options are: 'round', 'sharp', 'soft'.
27273      o gutter (number)(string) default '20%' (WHAT DOES IT DO?)
27274      o vgutter (number)
27275      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.
27276      o stacked (boolean) whether or not to tread values as in a stacked bar chart
27277      o to
27278      o stretch (boolean)
27279      o }
27280  -opts (object) options for the pie
27281      o{
27282      o cut
27283      o startAngle (number)
27284      o endAngle (number)
27285      } 
27286  *
27287  * @constructor
27288  * Create a new Input
27289  * @param {Object} config The config object
27290  */
27291
27292 Roo.bootstrap.Graph = function(config){
27293     Roo.bootstrap.Graph.superclass.constructor.call(this, config);
27294     
27295     this.addEvents({
27296         // img events
27297         /**
27298          * @event click
27299          * The img click event for the img.
27300          * @param {Roo.EventObject} e
27301          */
27302         "click" : true
27303     });
27304 };
27305
27306 Roo.extend(Roo.bootstrap.Graph, Roo.bootstrap.Component,  {
27307     
27308     sm: 4,
27309     md: 5,
27310     graphtype: 'bar',
27311     g_height: 250,
27312     g_width: 400,
27313     g_x: 50,
27314     g_y: 50,
27315     g_r: 30,
27316     opts:{
27317         //g_colors: this.colors,
27318         g_type: 'soft',
27319         g_gutter: '20%'
27320
27321     },
27322     title : false,
27323
27324     getAutoCreate : function(){
27325         
27326         var cfg = {
27327             tag: 'div',
27328             html : null
27329         };
27330         
27331         
27332         return  cfg;
27333     },
27334
27335     onRender : function(ct,position){
27336         
27337         
27338         Roo.bootstrap.Graph.superclass.onRender.call(this,ct,position);
27339         
27340         if (typeof(Raphael) == 'undefined') {
27341             Roo.bootstrap.MessageBox.alert("Error","Raphael is not availabe");
27342             return;
27343         }
27344         
27345         this.raphael = Raphael(this.el.dom);
27346         
27347                     // data1 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
27348                     // data2 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
27349                     // data3 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
27350                     // txtattr = { font: "12px 'Fontin Sans', Fontin-Sans, sans-serif" };
27351                 /*
27352                 r.text(160, 10, "Single Series Chart").attr(txtattr);
27353                 r.text(480, 10, "Multiline Series Chart").attr(txtattr);
27354                 r.text(160, 250, "Multiple Series Stacked Chart").attr(txtattr);
27355                 r.text(480, 250, 'Multiline Series Stacked Vertical Chart. Type "round"').attr(txtattr);
27356                 
27357                 r.barchart(10, 10, 300, 220, [[55, 20, 13, 32, 5, 1, 2, 10]], 0, {type: "sharp"});
27358                 r.barchart(330, 10, 300, 220, data1);
27359                 r.barchart(10, 250, 300, 220, data2, {stacked: true});
27360                 r.barchart(330, 250, 300, 220, data3, {stacked: true, type: "round"});
27361                 */
27362                 
27363                 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
27364                 // r.barchart(30, 30, 560, 250,  xdata, {
27365                 //    labels : [55, 20, 13, 32, 5, 1, 2, 10,5 , 10],
27366                 //     axis : "0 0 1 1",
27367                 //     axisxlabels :  xdata
27368                 //     //yvalues : cols,
27369                    
27370                 // });
27371 //        var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
27372 //        
27373 //        this.load(null,xdata,{
27374 //                axis : "0 0 1 1",
27375 //                axisxlabels :  xdata
27376 //                });
27377
27378     },
27379
27380     load : function(graphtype,xdata,opts)
27381     {
27382         this.raphael.clear();
27383         if(!graphtype) {
27384             graphtype = this.graphtype;
27385         }
27386         if(!opts){
27387             opts = this.opts;
27388         }
27389         var r = this.raphael,
27390             fin = function () {
27391                 this.flag = r.popup(this.bar.x, this.bar.y, this.bar.value || "0").insertBefore(this);
27392             },
27393             fout = function () {
27394                 this.flag.animate({opacity: 0}, 300, function () {this.remove();});
27395             },
27396             pfin = function() {
27397                 this.sector.stop();
27398                 this.sector.scale(1.1, 1.1, this.cx, this.cy);
27399
27400                 if (this.label) {
27401                     this.label[0].stop();
27402                     this.label[0].attr({ r: 7.5 });
27403                     this.label[1].attr({ "font-weight": 800 });
27404                 }
27405             },
27406             pfout = function() {
27407                 this.sector.animate({ transform: 's1 1 ' + this.cx + ' ' + this.cy }, 500, "bounce");
27408
27409                 if (this.label) {
27410                     this.label[0].animate({ r: 5 }, 500, "bounce");
27411                     this.label[1].attr({ "font-weight": 400 });
27412                 }
27413             };
27414
27415         switch(graphtype){
27416             case 'bar':
27417                 this.raphael.barchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
27418                 break;
27419             case 'hbar':
27420                 this.raphael.hbarchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
27421                 break;
27422             case 'pie':
27423 //                opts = { legend: ["%% - Enterprise Users", "% - ddd","Chrome Users"], legendpos: "west", 
27424 //                href: ["http://raphaeljs.com", "http://g.raphaeljs.com"]};
27425 //            
27426                 this.raphael.piechart(this.g_x,this.g_y,this.g_r,xdata,opts).hover(pfin, pfout);
27427                 
27428                 break;
27429
27430         }
27431         
27432         if(this.title){
27433             this.raphael.text(this.title.x, this.title.y, this.title.text).attr(this.title.attr);
27434         }
27435         
27436     },
27437     
27438     setTitle: function(o)
27439     {
27440         this.title = o;
27441     },
27442     
27443     initEvents: function() {
27444         
27445         if(!this.href){
27446             this.el.on('click', this.onClick, this);
27447         }
27448     },
27449     
27450     onClick : function(e)
27451     {
27452         Roo.log('img onclick');
27453         this.fireEvent('click', this, e);
27454     }
27455    
27456 });
27457
27458  
27459 /*
27460  * - LGPL
27461  *
27462  * numberBox
27463  * 
27464  */
27465 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
27466
27467 /**
27468  * @class Roo.bootstrap.dash.NumberBox
27469  * @extends Roo.bootstrap.Component
27470  * Bootstrap NumberBox class
27471  * @cfg {String} headline Box headline
27472  * @cfg {String} content Box content
27473  * @cfg {String} icon Box icon
27474  * @cfg {String} footer Footer text
27475  * @cfg {String} fhref Footer href
27476  * 
27477  * @constructor
27478  * Create a new NumberBox
27479  * @param {Object} config The config object
27480  */
27481
27482
27483 Roo.bootstrap.dash.NumberBox = function(config){
27484     Roo.bootstrap.dash.NumberBox.superclass.constructor.call(this, config);
27485     
27486 };
27487
27488 Roo.extend(Roo.bootstrap.dash.NumberBox, Roo.bootstrap.Component,  {
27489     
27490     headline : '',
27491     content : '',
27492     icon : '',
27493     footer : '',
27494     fhref : '',
27495     ficon : '',
27496     
27497     getAutoCreate : function(){
27498         
27499         var cfg = {
27500             tag : 'div',
27501             cls : 'small-box ',
27502             cn : [
27503                 {
27504                     tag : 'div',
27505                     cls : 'inner',
27506                     cn :[
27507                         {
27508                             tag : 'h3',
27509                             cls : 'roo-headline',
27510                             html : this.headline
27511                         },
27512                         {
27513                             tag : 'p',
27514                             cls : 'roo-content',
27515                             html : this.content
27516                         }
27517                     ]
27518                 }
27519             ]
27520         };
27521         
27522         if(this.icon){
27523             cfg.cn.push({
27524                 tag : 'div',
27525                 cls : 'icon',
27526                 cn :[
27527                     {
27528                         tag : 'i',
27529                         cls : 'ion ' + this.icon
27530                     }
27531                 ]
27532             });
27533         }
27534         
27535         if(this.footer){
27536             var footer = {
27537                 tag : 'a',
27538                 cls : 'small-box-footer',
27539                 href : this.fhref || '#',
27540                 html : this.footer
27541             };
27542             
27543             cfg.cn.push(footer);
27544             
27545         }
27546         
27547         return  cfg;
27548     },
27549
27550     onRender : function(ct,position){
27551         Roo.bootstrap.dash.NumberBox.superclass.onRender.call(this,ct,position);
27552
27553
27554        
27555                 
27556     },
27557
27558     setHeadline: function (value)
27559     {
27560         this.el.select('.roo-headline',true).first().dom.innerHTML = value;
27561     },
27562     
27563     setFooter: function (value, href)
27564     {
27565         this.el.select('a.small-box-footer',true).first().dom.innerHTML = value;
27566         
27567         if(href){
27568             this.el.select('a.small-box-footer',true).first().attr('href', href);
27569         }
27570         
27571     },
27572
27573     setContent: function (value)
27574     {
27575         this.el.select('.roo-content',true).first().dom.innerHTML = value;
27576     },
27577
27578     initEvents: function() 
27579     {   
27580         
27581     }
27582     
27583 });
27584
27585  
27586 /*
27587  * - LGPL
27588  *
27589  * TabBox
27590  * 
27591  */
27592 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
27593
27594 /**
27595  * @class Roo.bootstrap.dash.TabBox
27596  * @extends Roo.bootstrap.Component
27597  * Bootstrap TabBox class
27598  * @cfg {String} title Title of the TabBox
27599  * @cfg {String} icon Icon of the TabBox
27600  * @cfg {Boolean} showtabs (true|false) show the tabs default true
27601  * @cfg {Boolean} tabScrollable (true|false) tab scrollable when mobile view default false
27602  * 
27603  * @constructor
27604  * Create a new TabBox
27605  * @param {Object} config The config object
27606  */
27607
27608
27609 Roo.bootstrap.dash.TabBox = function(config){
27610     Roo.bootstrap.dash.TabBox.superclass.constructor.call(this, config);
27611     this.addEvents({
27612         // raw events
27613         /**
27614          * @event addpane
27615          * When a pane is added
27616          * @param {Roo.bootstrap.dash.TabPane} pane
27617          */
27618         "addpane" : true,
27619         /**
27620          * @event activatepane
27621          * When a pane is activated
27622          * @param {Roo.bootstrap.dash.TabPane} pane
27623          */
27624         "activatepane" : true
27625         
27626          
27627     });
27628     
27629     this.panes = [];
27630 };
27631
27632 Roo.extend(Roo.bootstrap.dash.TabBox, Roo.bootstrap.Component,  {
27633
27634     title : '',
27635     icon : false,
27636     showtabs : true,
27637     tabScrollable : false,
27638     
27639     getChildContainer : function()
27640     {
27641         return this.el.select('.tab-content', true).first();
27642     },
27643     
27644     getAutoCreate : function(){
27645         
27646         var header = {
27647             tag: 'li',
27648             cls: 'pull-left header',
27649             html: this.title,
27650             cn : []
27651         };
27652         
27653         if(this.icon){
27654             header.cn.push({
27655                 tag: 'i',
27656                 cls: 'fa ' + this.icon
27657             });
27658         }
27659         
27660         var h = {
27661             tag: 'ul',
27662             cls: 'nav nav-tabs pull-right',
27663             cn: [
27664                 header
27665             ]
27666         };
27667         
27668         if(this.tabScrollable){
27669             h = {
27670                 tag: 'div',
27671                 cls: 'tab-header',
27672                 cn: [
27673                     {
27674                         tag: 'ul',
27675                         cls: 'nav nav-tabs pull-right',
27676                         cn: [
27677                             header
27678                         ]
27679                     }
27680                 ]
27681             };
27682         }
27683         
27684         var cfg = {
27685             tag: 'div',
27686             cls: 'nav-tabs-custom',
27687             cn: [
27688                 h,
27689                 {
27690                     tag: 'div',
27691                     cls: 'tab-content no-padding',
27692                     cn: []
27693                 }
27694             ]
27695         };
27696
27697         return  cfg;
27698     },
27699     initEvents : function()
27700     {
27701         //Roo.log('add add pane handler');
27702         this.on('addpane', this.onAddPane, this);
27703     },
27704      /**
27705      * Updates the box title
27706      * @param {String} html to set the title to.
27707      */
27708     setTitle : function(value)
27709     {
27710         this.el.select('.nav-tabs .header', true).first().dom.innerHTML = value;
27711     },
27712     onAddPane : function(pane)
27713     {
27714         this.panes.push(pane);
27715         //Roo.log('addpane');
27716         //Roo.log(pane);
27717         // tabs are rendere left to right..
27718         if(!this.showtabs){
27719             return;
27720         }
27721         
27722         var ctr = this.el.select('.nav-tabs', true).first();
27723          
27724          
27725         var existing = ctr.select('.nav-tab',true);
27726         var qty = existing.getCount();;
27727         
27728         
27729         var tab = ctr.createChild({
27730             tag : 'li',
27731             cls : 'nav-tab' + (qty ? '' : ' active'),
27732             cn : [
27733                 {
27734                     tag : 'a',
27735                     href:'#',
27736                     html : pane.title
27737                 }
27738             ]
27739         }, qty ? existing.first().dom : ctr.select('.header', true).first().dom );
27740         pane.tab = tab;
27741         
27742         tab.on('click', this.onTabClick.createDelegate(this, [pane], true));
27743         if (!qty) {
27744             pane.el.addClass('active');
27745         }
27746         
27747                 
27748     },
27749     onTabClick : function(ev,un,ob,pane)
27750     {
27751         //Roo.log('tab - prev default');
27752         ev.preventDefault();
27753         
27754         
27755         this.el.select('.nav-tabs li.nav-tab', true).removeClass('active');
27756         pane.tab.addClass('active');
27757         //Roo.log(pane.title);
27758         this.getChildContainer().select('.tab-pane',true).removeClass('active');
27759         // technically we should have a deactivate event.. but maybe add later.
27760         // and it should not de-activate the selected tab...
27761         this.fireEvent('activatepane', pane);
27762         pane.el.addClass('active');
27763         pane.fireEvent('activate');
27764         
27765         
27766     },
27767     
27768     getActivePane : function()
27769     {
27770         var r = false;
27771         Roo.each(this.panes, function(p) {
27772             if(p.el.hasClass('active')){
27773                 r = p;
27774                 return false;
27775             }
27776             
27777             return;
27778         });
27779         
27780         return r;
27781     }
27782     
27783     
27784 });
27785
27786  
27787 /*
27788  * - LGPL
27789  *
27790  * Tab pane
27791  * 
27792  */
27793 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
27794 /**
27795  * @class Roo.bootstrap.TabPane
27796  * @extends Roo.bootstrap.Component
27797  * Bootstrap TabPane class
27798  * @cfg {Boolean} active (false | true) Default false
27799  * @cfg {String} title title of panel
27800
27801  * 
27802  * @constructor
27803  * Create a new TabPane
27804  * @param {Object} config The config object
27805  */
27806
27807 Roo.bootstrap.dash.TabPane = function(config){
27808     Roo.bootstrap.dash.TabPane.superclass.constructor.call(this, config);
27809     
27810     this.addEvents({
27811         // raw events
27812         /**
27813          * @event activate
27814          * When a pane is activated
27815          * @param {Roo.bootstrap.dash.TabPane} pane
27816          */
27817         "activate" : true
27818          
27819     });
27820 };
27821
27822 Roo.extend(Roo.bootstrap.dash.TabPane, Roo.bootstrap.Component,  {
27823     
27824     active : false,
27825     title : '',
27826     
27827     // the tabBox that this is attached to.
27828     tab : false,
27829      
27830     getAutoCreate : function() 
27831     {
27832         var cfg = {
27833             tag: 'div',
27834             cls: 'tab-pane'
27835         };
27836         
27837         if(this.active){
27838             cfg.cls += ' active';
27839         }
27840         
27841         return cfg;
27842     },
27843     initEvents  : function()
27844     {
27845         //Roo.log('trigger add pane handler');
27846         this.parent().fireEvent('addpane', this)
27847     },
27848     
27849      /**
27850      * Updates the tab title 
27851      * @param {String} html to set the title to.
27852      */
27853     setTitle: function(str)
27854     {
27855         if (!this.tab) {
27856             return;
27857         }
27858         this.title = str;
27859         this.tab.select('a', true).first().dom.innerHTML = str;
27860         
27861     }
27862     
27863     
27864     
27865 });
27866
27867  
27868
27869
27870  /*
27871  * - LGPL
27872  *
27873  * menu
27874  * 
27875  */
27876 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
27877
27878 /**
27879  * @class Roo.bootstrap.menu.Menu
27880  * @extends Roo.bootstrap.Component
27881  * Bootstrap Menu class - container for Menu
27882  * @cfg {String} html Text of the menu
27883  * @cfg {String} weight (default | primary | success | info | warning | danger | inverse)
27884  * @cfg {String} icon Font awesome icon
27885  * @cfg {String} pos Menu align to (top | bottom) default bottom
27886  * 
27887  * 
27888  * @constructor
27889  * Create a new Menu
27890  * @param {Object} config The config object
27891  */
27892
27893
27894 Roo.bootstrap.menu.Menu = function(config){
27895     Roo.bootstrap.menu.Menu.superclass.constructor.call(this, config);
27896     
27897     this.addEvents({
27898         /**
27899          * @event beforeshow
27900          * Fires before this menu is displayed
27901          * @param {Roo.bootstrap.menu.Menu} this
27902          */
27903         beforeshow : true,
27904         /**
27905          * @event beforehide
27906          * Fires before this menu is hidden
27907          * @param {Roo.bootstrap.menu.Menu} this
27908          */
27909         beforehide : true,
27910         /**
27911          * @event show
27912          * Fires after this menu is displayed
27913          * @param {Roo.bootstrap.menu.Menu} this
27914          */
27915         show : true,
27916         /**
27917          * @event hide
27918          * Fires after this menu is hidden
27919          * @param {Roo.bootstrap.menu.Menu} this
27920          */
27921         hide : true,
27922         /**
27923          * @event click
27924          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
27925          * @param {Roo.bootstrap.menu.Menu} this
27926          * @param {Roo.EventObject} e
27927          */
27928         click : true
27929     });
27930     
27931 };
27932
27933 Roo.extend(Roo.bootstrap.menu.Menu, Roo.bootstrap.Component,  {
27934     
27935     submenu : false,
27936     html : '',
27937     weight : 'default',
27938     icon : false,
27939     pos : 'bottom',
27940     
27941     
27942     getChildContainer : function() {
27943         if(this.isSubMenu){
27944             return this.el;
27945         }
27946         
27947         return this.el.select('ul.dropdown-menu', true).first();  
27948     },
27949     
27950     getAutoCreate : function()
27951     {
27952         var text = [
27953             {
27954                 tag : 'span',
27955                 cls : 'roo-menu-text',
27956                 html : this.html
27957             }
27958         ];
27959         
27960         if(this.icon){
27961             text.unshift({
27962                 tag : 'i',
27963                 cls : 'fa ' + this.icon
27964             })
27965         }
27966         
27967         
27968         var cfg = {
27969             tag : 'div',
27970             cls : 'btn-group',
27971             cn : [
27972                 {
27973                     tag : 'button',
27974                     cls : 'dropdown-button btn btn-' + this.weight,
27975                     cn : text
27976                 },
27977                 {
27978                     tag : 'button',
27979                     cls : 'dropdown-toggle btn btn-' + this.weight,
27980                     cn : [
27981                         {
27982                             tag : 'span',
27983                             cls : 'caret'
27984                         }
27985                     ]
27986                 },
27987                 {
27988                     tag : 'ul',
27989                     cls : 'dropdown-menu'
27990                 }
27991             ]
27992             
27993         };
27994         
27995         if(this.pos == 'top'){
27996             cfg.cls += ' dropup';
27997         }
27998         
27999         if(this.isSubMenu){
28000             cfg = {
28001                 tag : 'ul',
28002                 cls : 'dropdown-menu'
28003             }
28004         }
28005         
28006         return cfg;
28007     },
28008     
28009     onRender : function(ct, position)
28010     {
28011         this.isSubMenu = ct.hasClass('dropdown-submenu');
28012         
28013         Roo.bootstrap.menu.Menu.superclass.onRender.call(this, ct, position);
28014     },
28015     
28016     initEvents : function() 
28017     {
28018         if(this.isSubMenu){
28019             return;
28020         }
28021         
28022         this.hidden = true;
28023         
28024         this.triggerEl = this.el.select('button.dropdown-toggle', true).first();
28025         this.triggerEl.on('click', this.onTriggerPress, this);
28026         
28027         this.buttonEl = this.el.select('button.dropdown-button', true).first();
28028         this.buttonEl.on('click', this.onClick, this);
28029         
28030     },
28031     
28032     list : function()
28033     {
28034         if(this.isSubMenu){
28035             return this.el;
28036         }
28037         
28038         return this.el.select('ul.dropdown-menu', true).first();
28039     },
28040     
28041     onClick : function(e)
28042     {
28043         this.fireEvent("click", this, e);
28044     },
28045     
28046     onTriggerPress  : function(e)
28047     {   
28048         if (this.isVisible()) {
28049             this.hide();
28050         } else {
28051             this.show();
28052         }
28053     },
28054     
28055     isVisible : function(){
28056         return !this.hidden;
28057     },
28058     
28059     show : function()
28060     {
28061         this.fireEvent("beforeshow", this);
28062         
28063         this.hidden = false;
28064         this.el.addClass('open');
28065         
28066         Roo.get(document).on("mouseup", this.onMouseUp, this);
28067         
28068         this.fireEvent("show", this);
28069         
28070         
28071     },
28072     
28073     hide : function()
28074     {
28075         this.fireEvent("beforehide", this);
28076         
28077         this.hidden = true;
28078         this.el.removeClass('open');
28079         
28080         Roo.get(document).un("mouseup", this.onMouseUp);
28081         
28082         this.fireEvent("hide", this);
28083     },
28084     
28085     onMouseUp : function()
28086     {
28087         this.hide();
28088     }
28089     
28090 });
28091
28092  
28093  /*
28094  * - LGPL
28095  *
28096  * menu item
28097  * 
28098  */
28099 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
28100
28101 /**
28102  * @class Roo.bootstrap.menu.Item
28103  * @extends Roo.bootstrap.Component
28104  * Bootstrap MenuItem class
28105  * @cfg {Boolean} submenu (true | false) default false
28106  * @cfg {String} html text of the item
28107  * @cfg {String} href the link
28108  * @cfg {Boolean} disable (true | false) default false
28109  * @cfg {Boolean} preventDefault (true | false) default true
28110  * @cfg {String} icon Font awesome icon
28111  * @cfg {String} pos Submenu align to (left | right) default right 
28112  * 
28113  * 
28114  * @constructor
28115  * Create a new Item
28116  * @param {Object} config The config object
28117  */
28118
28119
28120 Roo.bootstrap.menu.Item = function(config){
28121     Roo.bootstrap.menu.Item.superclass.constructor.call(this, config);
28122     this.addEvents({
28123         /**
28124          * @event mouseover
28125          * Fires when the mouse is hovering over this menu
28126          * @param {Roo.bootstrap.menu.Item} this
28127          * @param {Roo.EventObject} e
28128          */
28129         mouseover : true,
28130         /**
28131          * @event mouseout
28132          * Fires when the mouse exits this menu
28133          * @param {Roo.bootstrap.menu.Item} this
28134          * @param {Roo.EventObject} e
28135          */
28136         mouseout : true,
28137         // raw events
28138         /**
28139          * @event click
28140          * The raw click event for the entire grid.
28141          * @param {Roo.EventObject} e
28142          */
28143         click : true
28144     });
28145 };
28146
28147 Roo.extend(Roo.bootstrap.menu.Item, Roo.bootstrap.Component,  {
28148     
28149     submenu : false,
28150     href : '',
28151     html : '',
28152     preventDefault: true,
28153     disable : false,
28154     icon : false,
28155     pos : 'right',
28156     
28157     getAutoCreate : function()
28158     {
28159         var text = [
28160             {
28161                 tag : 'span',
28162                 cls : 'roo-menu-item-text',
28163                 html : this.html
28164             }
28165         ];
28166         
28167         if(this.icon){
28168             text.unshift({
28169                 tag : 'i',
28170                 cls : 'fa ' + this.icon
28171             })
28172         }
28173         
28174         var cfg = {
28175             tag : 'li',
28176             cn : [
28177                 {
28178                     tag : 'a',
28179                     href : this.href || '#',
28180                     cn : text
28181                 }
28182             ]
28183         };
28184         
28185         if(this.disable){
28186             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'disabled' : (cfg.cls + ' disabled');
28187         }
28188         
28189         if(this.submenu){
28190             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'dropdown-submenu' : (cfg.cls + ' dropdown-submenu');
28191             
28192             if(this.pos == 'left'){
28193                 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'pull-left' : (cfg.cls + ' pull-left');
28194             }
28195         }
28196         
28197         return cfg;
28198     },
28199     
28200     initEvents : function() 
28201     {
28202         this.el.on('mouseover', this.onMouseOver, this);
28203         this.el.on('mouseout', this.onMouseOut, this);
28204         
28205         this.el.select('a', true).first().on('click', this.onClick, this);
28206         
28207     },
28208     
28209     onClick : function(e)
28210     {
28211         if(this.preventDefault){
28212             e.preventDefault();
28213         }
28214         
28215         this.fireEvent("click", this, e);
28216     },
28217     
28218     onMouseOver : function(e)
28219     {
28220         if(this.submenu && this.pos == 'left'){
28221             this.el.select('ul.dropdown-menu', true).first().setLeft(this.el.select('ul.dropdown-menu', true).first().getWidth() * -1);
28222         }
28223         
28224         this.fireEvent("mouseover", this, e);
28225     },
28226     
28227     onMouseOut : function(e)
28228     {
28229         this.fireEvent("mouseout", this, e);
28230     }
28231 });
28232
28233  
28234
28235  /*
28236  * - LGPL
28237  *
28238  * menu separator
28239  * 
28240  */
28241 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
28242
28243 /**
28244  * @class Roo.bootstrap.menu.Separator
28245  * @extends Roo.bootstrap.Component
28246  * Bootstrap Separator class
28247  * 
28248  * @constructor
28249  * Create a new Separator
28250  * @param {Object} config The config object
28251  */
28252
28253
28254 Roo.bootstrap.menu.Separator = function(config){
28255     Roo.bootstrap.menu.Separator.superclass.constructor.call(this, config);
28256 };
28257
28258 Roo.extend(Roo.bootstrap.menu.Separator, Roo.bootstrap.Component,  {
28259     
28260     getAutoCreate : function(){
28261         var cfg = {
28262             tag : 'li',
28263             cls: 'divider'
28264         };
28265         
28266         return cfg;
28267     }
28268    
28269 });
28270
28271  
28272
28273  /*
28274  * - LGPL
28275  *
28276  * Tooltip
28277  * 
28278  */
28279
28280 /**
28281  * @class Roo.bootstrap.Tooltip
28282  * Bootstrap Tooltip class
28283  * This is basic at present - all componets support it by default, however they should add tooltipEl() method
28284  * to determine which dom element triggers the tooltip.
28285  * 
28286  * It needs to add support for additional attributes like tooltip-position
28287  * 
28288  * @constructor
28289  * Create a new Toolti
28290  * @param {Object} config The config object
28291  */
28292
28293 Roo.bootstrap.Tooltip = function(config){
28294     Roo.bootstrap.Tooltip.superclass.constructor.call(this, config);
28295     
28296     this.alignment = Roo.bootstrap.Tooltip.alignment;
28297     
28298     if(typeof(config) != 'undefined' && typeof(config.alignment) != 'undefined'){
28299         this.alignment = config.alignment;
28300     }
28301     
28302 };
28303
28304 Roo.apply(Roo.bootstrap.Tooltip, {
28305     /**
28306      * @function init initialize tooltip monitoring.
28307      * @static
28308      */
28309     currentEl : false,
28310     currentTip : false,
28311     currentRegion : false,
28312     
28313     //  init : delay?
28314     
28315     init : function()
28316     {
28317         Roo.get(document).on('mouseover', this.enter ,this);
28318         Roo.get(document).on('mouseout', this.leave, this);
28319          
28320         
28321         this.currentTip = new Roo.bootstrap.Tooltip();
28322     },
28323     
28324     enter : function(ev)
28325     {
28326         var dom = ev.getTarget();
28327         
28328         //Roo.log(['enter',dom]);
28329         var el = Roo.fly(dom);
28330         if (this.currentEl) {
28331             //Roo.log(dom);
28332             //Roo.log(this.currentEl);
28333             //Roo.log(this.currentEl.contains(dom));
28334             if (this.currentEl == el) {
28335                 return;
28336             }
28337             if (dom != this.currentEl.dom && this.currentEl.contains(dom)) {
28338                 return;
28339             }
28340
28341         }
28342         
28343         if (this.currentTip.el) {
28344             this.currentTip.el.setVisibilityMode(Roo.Element.DISPLAY).hide(); // force hiding...
28345         }    
28346         //Roo.log(ev);
28347         
28348         if(!el || el.dom == document){
28349             return;
28350         }
28351         
28352         var bindEl = el;
28353         
28354         // you can not look for children, as if el is the body.. then everythign is the child..
28355         if (!el.attr('tooltip')) { //
28356             if (!el.select("[tooltip]").elements.length) {
28357                 return;
28358             }
28359             // is the mouse over this child...?
28360             bindEl = el.select("[tooltip]").first();
28361             var xy = ev.getXY();
28362             if (!bindEl.getRegion().contains( { top : xy[1] ,right : xy[0] , bottom : xy[1], left : xy[0]})) {
28363                 //Roo.log("not in region.");
28364                 return;
28365             }
28366             //Roo.log("child element over..");
28367             
28368         }
28369         this.currentEl = bindEl;
28370         this.currentTip.bind(bindEl);
28371         this.currentRegion = Roo.lib.Region.getRegion(dom);
28372         this.currentTip.enter();
28373         
28374     },
28375     leave : function(ev)
28376     {
28377         var dom = ev.getTarget();
28378         //Roo.log(['leave',dom]);
28379         if (!this.currentEl) {
28380             return;
28381         }
28382         
28383         
28384         if (dom != this.currentEl.dom) {
28385             return;
28386         }
28387         var xy = ev.getXY();
28388         if (this.currentRegion.contains( new Roo.lib.Region( xy[1], xy[0] ,xy[1], xy[0]  ))) {
28389             return;
28390         }
28391         // only activate leave if mouse cursor is outside... bounding box..
28392         
28393         
28394         
28395         
28396         if (this.currentTip) {
28397             this.currentTip.leave();
28398         }
28399         //Roo.log('clear currentEl');
28400         this.currentEl = false;
28401         
28402         
28403     },
28404     alignment : {
28405         'left' : ['r-l', [-2,0], 'right'],
28406         'right' : ['l-r', [2,0], 'left'],
28407         'bottom' : ['t-b', [0,2], 'top'],
28408         'top' : [ 'b-t', [0,-2], 'bottom']
28409     }
28410     
28411 });
28412
28413
28414 Roo.extend(Roo.bootstrap.Tooltip, Roo.bootstrap.Component,  {
28415     
28416     
28417     bindEl : false,
28418     
28419     delay : null, // can be { show : 300 , hide: 500}
28420     
28421     timeout : null,
28422     
28423     hoverState : null, //???
28424     
28425     placement : 'bottom', 
28426     
28427     alignment : false,
28428     
28429     getAutoCreate : function(){
28430     
28431         var cfg = {
28432            cls : 'tooltip',   
28433            role : 'tooltip',
28434            cn : [
28435                 {
28436                     cls : 'tooltip-arrow arrow'
28437                 },
28438                 {
28439                     cls : 'tooltip-inner'
28440                 }
28441            ]
28442         };
28443         
28444         return cfg;
28445     },
28446     bind : function(el)
28447     {
28448         this.bindEl = el;
28449     },
28450     
28451     initEvents : function()
28452     {
28453         this.arrowEl = this.el.select('.arrow', true).first();
28454         this.innerEl = this.el.select('.tooltip-inner', true).first();
28455     },
28456     
28457     enter : function () {
28458        
28459         if (this.timeout != null) {
28460             clearTimeout(this.timeout);
28461         }
28462         
28463         this.hoverState = 'in';
28464          //Roo.log("enter - show");
28465         if (!this.delay || !this.delay.show) {
28466             this.show();
28467             return;
28468         }
28469         var _t = this;
28470         this.timeout = setTimeout(function () {
28471             if (_t.hoverState == 'in') {
28472                 _t.show();
28473             }
28474         }, this.delay.show);
28475     },
28476     leave : function()
28477     {
28478         clearTimeout(this.timeout);
28479     
28480         this.hoverState = 'out';
28481          if (!this.delay || !this.delay.hide) {
28482             this.hide();
28483             return;
28484         }
28485        
28486         var _t = this;
28487         this.timeout = setTimeout(function () {
28488             //Roo.log("leave - timeout");
28489             
28490             if (_t.hoverState == 'out') {
28491                 _t.hide();
28492                 Roo.bootstrap.Tooltip.currentEl = false;
28493             }
28494         }, delay);
28495     },
28496     
28497     show : function (msg)
28498     {
28499         if (!this.el) {
28500             this.render(document.body);
28501         }
28502         // set content.
28503         //Roo.log([this.bindEl, this.bindEl.attr('tooltip')]);
28504         
28505         var tip = msg || this.bindEl.attr('tooltip') || this.bindEl.select("[tooltip]").first().attr('tooltip');
28506         
28507         this.el.select('.tooltip-inner',true).first().dom.innerHTML = tip;
28508         
28509         this.el.removeClass(['fade','top','bottom', 'left', 'right','in',
28510                              'bs-tooltip-top','bs-tooltip-bottom', 'bs-tooltip-left', 'bs-tooltip-right']);
28511         
28512         var placement = typeof this.placement == 'function' ?
28513             this.placement.call(this, this.el, on_el) :
28514             this.placement;
28515             
28516         var autoToken = /\s?auto?\s?/i;
28517         var autoPlace = autoToken.test(placement);
28518         if (autoPlace) {
28519             placement = placement.replace(autoToken, '') || 'top';
28520         }
28521         
28522         //this.el.detach()
28523         //this.el.setXY([0,0]);
28524         this.el.show();
28525         //this.el.dom.style.display='block';
28526         
28527         //this.el.appendTo(on_el);
28528         
28529         var p = this.getPosition();
28530         var box = this.el.getBox();
28531         
28532         if (autoPlace) {
28533             // fixme..
28534         }
28535         
28536         var align = this.alignment[placement];
28537         
28538         var xy = this.el.getAlignToXY(this.bindEl, align[0], align[1]);
28539         
28540         if(placement == 'top' || placement == 'bottom'){
28541             if(xy[0] < 0){
28542                 placement = 'right';
28543             }
28544             
28545             if(xy[0] + this.el.getWidth() > Roo.lib.Dom.getViewWidth()){
28546                 placement = 'left';
28547             }
28548             
28549             var scroll = Roo.select('body', true).first().getScroll();
28550             
28551             if(xy[1] > Roo.lib.Dom.getViewHeight() + scroll.top - this.el.getHeight()){
28552                 placement = 'top';
28553             }
28554             
28555             align = this.alignment[placement];
28556             
28557             this.arrowEl.setLeft((this.innerEl.getWidth()/2) - 5);
28558             
28559         }
28560         
28561         this.el.alignTo(this.bindEl, align[0],align[1]);
28562         //var arrow = this.el.select('.arrow',true).first();
28563         //arrow.set(align[2], 
28564         
28565         this.el.addClass(placement);
28566         this.el.addClass("bs-tooltip-"+ placement);
28567         
28568         this.el.addClass('in fade show');
28569         
28570         this.hoverState = null;
28571         
28572         if (this.el.hasClass('fade')) {
28573             // fade it?
28574         }
28575         
28576         
28577         
28578         
28579         
28580     },
28581     hide : function()
28582     {
28583          
28584         if (!this.el) {
28585             return;
28586         }
28587         //this.el.setXY([0,0]);
28588         this.el.removeClass(['show', 'in']);
28589         //this.el.hide();
28590         
28591     }
28592     
28593 });
28594  
28595
28596  /*
28597  * - LGPL
28598  *
28599  * Location Picker
28600  * 
28601  */
28602
28603 /**
28604  * @class Roo.bootstrap.LocationPicker
28605  * @extends Roo.bootstrap.Component
28606  * Bootstrap LocationPicker class
28607  * @cfg {Number} latitude Position when init default 0
28608  * @cfg {Number} longitude Position when init default 0
28609  * @cfg {Number} zoom default 15
28610  * @cfg {String} mapTypeId default google.maps.MapTypeId.ROADMAP
28611  * @cfg {Boolean} mapTypeControl default false
28612  * @cfg {Boolean} disableDoubleClickZoom default false
28613  * @cfg {Boolean} scrollwheel default true
28614  * @cfg {Boolean} streetViewControl default false
28615  * @cfg {Number} radius default 0
28616  * @cfg {String} locationName
28617  * @cfg {Boolean} draggable default true
28618  * @cfg {Boolean} enableAutocomplete default false
28619  * @cfg {Boolean} enableReverseGeocode default true
28620  * @cfg {String} markerTitle
28621  * 
28622  * @constructor
28623  * Create a new LocationPicker
28624  * @param {Object} config The config object
28625  */
28626
28627
28628 Roo.bootstrap.LocationPicker = function(config){
28629     
28630     Roo.bootstrap.LocationPicker.superclass.constructor.call(this, config);
28631     
28632     this.addEvents({
28633         /**
28634          * @event initial
28635          * Fires when the picker initialized.
28636          * @param {Roo.bootstrap.LocationPicker} this
28637          * @param {Google Location} location
28638          */
28639         initial : true,
28640         /**
28641          * @event positionchanged
28642          * Fires when the picker position changed.
28643          * @param {Roo.bootstrap.LocationPicker} this
28644          * @param {Google Location} location
28645          */
28646         positionchanged : true,
28647         /**
28648          * @event resize
28649          * Fires when the map resize.
28650          * @param {Roo.bootstrap.LocationPicker} this
28651          */
28652         resize : true,
28653         /**
28654          * @event show
28655          * Fires when the map show.
28656          * @param {Roo.bootstrap.LocationPicker} this
28657          */
28658         show : true,
28659         /**
28660          * @event hide
28661          * Fires when the map hide.
28662          * @param {Roo.bootstrap.LocationPicker} this
28663          */
28664         hide : true,
28665         /**
28666          * @event mapClick
28667          * Fires when click the map.
28668          * @param {Roo.bootstrap.LocationPicker} this
28669          * @param {Map event} e
28670          */
28671         mapClick : true,
28672         /**
28673          * @event mapRightClick
28674          * Fires when right click the map.
28675          * @param {Roo.bootstrap.LocationPicker} this
28676          * @param {Map event} e
28677          */
28678         mapRightClick : true,
28679         /**
28680          * @event markerClick
28681          * Fires when click the marker.
28682          * @param {Roo.bootstrap.LocationPicker} this
28683          * @param {Map event} e
28684          */
28685         markerClick : true,
28686         /**
28687          * @event markerRightClick
28688          * Fires when right click the marker.
28689          * @param {Roo.bootstrap.LocationPicker} this
28690          * @param {Map event} e
28691          */
28692         markerRightClick : true,
28693         /**
28694          * @event OverlayViewDraw
28695          * Fires when OverlayView Draw
28696          * @param {Roo.bootstrap.LocationPicker} this
28697          */
28698         OverlayViewDraw : true,
28699         /**
28700          * @event OverlayViewOnAdd
28701          * Fires when OverlayView Draw
28702          * @param {Roo.bootstrap.LocationPicker} this
28703          */
28704         OverlayViewOnAdd : true,
28705         /**
28706          * @event OverlayViewOnRemove
28707          * Fires when OverlayView Draw
28708          * @param {Roo.bootstrap.LocationPicker} this
28709          */
28710         OverlayViewOnRemove : true,
28711         /**
28712          * @event OverlayViewShow
28713          * Fires when OverlayView Draw
28714          * @param {Roo.bootstrap.LocationPicker} this
28715          * @param {Pixel} cpx
28716          */
28717         OverlayViewShow : true,
28718         /**
28719          * @event OverlayViewHide
28720          * Fires when OverlayView Draw
28721          * @param {Roo.bootstrap.LocationPicker} this
28722          */
28723         OverlayViewHide : true,
28724         /**
28725          * @event loadexception
28726          * Fires when load google lib failed.
28727          * @param {Roo.bootstrap.LocationPicker} this
28728          */
28729         loadexception : true
28730     });
28731         
28732 };
28733
28734 Roo.extend(Roo.bootstrap.LocationPicker, Roo.bootstrap.Component,  {
28735     
28736     gMapContext: false,
28737     
28738     latitude: 0,
28739     longitude: 0,
28740     zoom: 15,
28741     mapTypeId: false,
28742     mapTypeControl: false,
28743     disableDoubleClickZoom: false,
28744     scrollwheel: true,
28745     streetViewControl: false,
28746     radius: 0,
28747     locationName: '',
28748     draggable: true,
28749     enableAutocomplete: false,
28750     enableReverseGeocode: true,
28751     markerTitle: '',
28752     
28753     getAutoCreate: function()
28754     {
28755
28756         var cfg = {
28757             tag: 'div',
28758             cls: 'roo-location-picker'
28759         };
28760         
28761         return cfg
28762     },
28763     
28764     initEvents: function(ct, position)
28765     {       
28766         if(!this.el.getWidth() || this.isApplied()){
28767             return;
28768         }
28769         
28770         this.el.setVisibilityMode(Roo.Element.DISPLAY);
28771         
28772         this.initial();
28773     },
28774     
28775     initial: function()
28776     {
28777         if(typeof(google) == 'undefined' || typeof(google.maps) == 'undefined'){
28778             this.fireEvent('loadexception', this);
28779             return;
28780         }
28781         
28782         if(!this.mapTypeId){
28783             this.mapTypeId = google.maps.MapTypeId.ROADMAP;
28784         }
28785         
28786         this.gMapContext = this.GMapContext();
28787         
28788         this.initOverlayView();
28789         
28790         this.OverlayView = new Roo.bootstrap.LocationPicker.OverlayView(this.gMapContext.map);
28791         
28792         var _this = this;
28793                 
28794         google.maps.event.addListener(this.gMapContext.marker, "dragend", function(event) {
28795             _this.setPosition(_this.gMapContext.marker.position);
28796         });
28797         
28798         google.maps.event.addListener(this.gMapContext.map, 'click', function(event){
28799             _this.fireEvent('mapClick', this, event);
28800             
28801         });
28802
28803         google.maps.event.addListener(this.gMapContext.map, 'rightclick', function(event){
28804             _this.fireEvent('mapRightClick', this, event);
28805             
28806         });
28807         
28808         google.maps.event.addListener(this.gMapContext.marker, 'click', function(event){
28809             _this.fireEvent('markerClick', this, event);
28810             
28811         });
28812
28813         google.maps.event.addListener(this.gMapContext.marker, 'rightclick', function(event){
28814             _this.fireEvent('markerRightClick', this, event);
28815             
28816         });
28817         
28818         this.setPosition(this.gMapContext.location);
28819         
28820         this.fireEvent('initial', this, this.gMapContext.location);
28821     },
28822     
28823     initOverlayView: function()
28824     {
28825         var _this = this;
28826         
28827         Roo.bootstrap.LocationPicker.OverlayView.prototype = Roo.apply(new google.maps.OverlayView(), {
28828             
28829             draw: function()
28830             {
28831                 _this.fireEvent('OverlayViewDraw', _this);
28832             },
28833             
28834             onAdd: function()
28835             {
28836                 _this.fireEvent('OverlayViewOnAdd', _this);
28837             },
28838             
28839             onRemove: function()
28840             {
28841                 _this.fireEvent('OverlayViewOnRemove', _this);
28842             },
28843             
28844             show: function(cpx)
28845             {
28846                 _this.fireEvent('OverlayViewShow', _this, cpx);
28847             },
28848             
28849             hide: function()
28850             {
28851                 _this.fireEvent('OverlayViewHide', _this);
28852             }
28853             
28854         });
28855     },
28856     
28857     fromLatLngToContainerPixel: function(event)
28858     {
28859         return this.OverlayView.getProjection().fromLatLngToContainerPixel(event.latLng);
28860     },
28861     
28862     isApplied: function() 
28863     {
28864         return this.getGmapContext() == false ? false : true;
28865     },
28866     
28867     getGmapContext: function() 
28868     {
28869         return (typeof(this.gMapContext) == 'undefined') ? false : this.gMapContext;
28870     },
28871     
28872     GMapContext: function() 
28873     {
28874         var position = new google.maps.LatLng(this.latitude, this.longitude);
28875         
28876         var _map = new google.maps.Map(this.el.dom, {
28877             center: position,
28878             zoom: this.zoom,
28879             mapTypeId: this.mapTypeId,
28880             mapTypeControl: this.mapTypeControl,
28881             disableDoubleClickZoom: this.disableDoubleClickZoom,
28882             scrollwheel: this.scrollwheel,
28883             streetViewControl: this.streetViewControl,
28884             locationName: this.locationName,
28885             draggable: this.draggable,
28886             enableAutocomplete: this.enableAutocomplete,
28887             enableReverseGeocode: this.enableReverseGeocode
28888         });
28889         
28890         var _marker = new google.maps.Marker({
28891             position: position,
28892             map: _map,
28893             title: this.markerTitle,
28894             draggable: this.draggable
28895         });
28896         
28897         return {
28898             map: _map,
28899             marker: _marker,
28900             circle: null,
28901             location: position,
28902             radius: this.radius,
28903             locationName: this.locationName,
28904             addressComponents: {
28905                 formatted_address: null,
28906                 addressLine1: null,
28907                 addressLine2: null,
28908                 streetName: null,
28909                 streetNumber: null,
28910                 city: null,
28911                 district: null,
28912                 state: null,
28913                 stateOrProvince: null
28914             },
28915             settings: this,
28916             domContainer: this.el.dom,
28917             geodecoder: new google.maps.Geocoder()
28918         };
28919     },
28920     
28921     drawCircle: function(center, radius, options) 
28922     {
28923         if (this.gMapContext.circle != null) {
28924             this.gMapContext.circle.setMap(null);
28925         }
28926         if (radius > 0) {
28927             radius *= 1;
28928             options = Roo.apply({}, options, {
28929                 strokeColor: "#0000FF",
28930                 strokeOpacity: .35,
28931                 strokeWeight: 2,
28932                 fillColor: "#0000FF",
28933                 fillOpacity: .2
28934             });
28935             
28936             options.map = this.gMapContext.map;
28937             options.radius = radius;
28938             options.center = center;
28939             this.gMapContext.circle = new google.maps.Circle(options);
28940             return this.gMapContext.circle;
28941         }
28942         
28943         return null;
28944     },
28945     
28946     setPosition: function(location) 
28947     {
28948         this.gMapContext.location = location;
28949         this.gMapContext.marker.setPosition(location);
28950         this.gMapContext.map.panTo(location);
28951         this.drawCircle(location, this.gMapContext.radius, {});
28952         
28953         var _this = this;
28954         
28955         if (this.gMapContext.settings.enableReverseGeocode) {
28956             this.gMapContext.geodecoder.geocode({
28957                 latLng: this.gMapContext.location
28958             }, function(results, status) {
28959                 
28960                 if (status == google.maps.GeocoderStatus.OK && results.length > 0) {
28961                     _this.gMapContext.locationName = results[0].formatted_address;
28962                     _this.gMapContext.addressComponents = _this.address_component_from_google_geocode(results[0].address_components);
28963                     
28964                     _this.fireEvent('positionchanged', this, location);
28965                 }
28966             });
28967             
28968             return;
28969         }
28970         
28971         this.fireEvent('positionchanged', this, location);
28972     },
28973     
28974     resize: function()
28975     {
28976         google.maps.event.trigger(this.gMapContext.map, "resize");
28977         
28978         this.gMapContext.map.setCenter(this.gMapContext.marker.position);
28979         
28980         this.fireEvent('resize', this);
28981     },
28982     
28983     setPositionByLatLng: function(latitude, longitude)
28984     {
28985         this.setPosition(new google.maps.LatLng(latitude, longitude));
28986     },
28987     
28988     getCurrentPosition: function() 
28989     {
28990         return {
28991             latitude: this.gMapContext.location.lat(),
28992             longitude: this.gMapContext.location.lng()
28993         };
28994     },
28995     
28996     getAddressName: function() 
28997     {
28998         return this.gMapContext.locationName;
28999     },
29000     
29001     getAddressComponents: function() 
29002     {
29003         return this.gMapContext.addressComponents;
29004     },
29005     
29006     address_component_from_google_geocode: function(address_components) 
29007     {
29008         var result = {};
29009         
29010         for (var i = 0; i < address_components.length; i++) {
29011             var component = address_components[i];
29012             if (component.types.indexOf("postal_code") >= 0) {
29013                 result.postalCode = component.short_name;
29014             } else if (component.types.indexOf("street_number") >= 0) {
29015                 result.streetNumber = component.short_name;
29016             } else if (component.types.indexOf("route") >= 0) {
29017                 result.streetName = component.short_name;
29018             } else if (component.types.indexOf("neighborhood") >= 0) {
29019                 result.city = component.short_name;
29020             } else if (component.types.indexOf("locality") >= 0) {
29021                 result.city = component.short_name;
29022             } else if (component.types.indexOf("sublocality") >= 0) {
29023                 result.district = component.short_name;
29024             } else if (component.types.indexOf("administrative_area_level_1") >= 0) {
29025                 result.stateOrProvince = component.short_name;
29026             } else if (component.types.indexOf("country") >= 0) {
29027                 result.country = component.short_name;
29028             }
29029         }
29030         
29031         result.addressLine1 = [ result.streetNumber, result.streetName ].join(" ").trim();
29032         result.addressLine2 = "";
29033         return result;
29034     },
29035     
29036     setZoomLevel: function(zoom)
29037     {
29038         this.gMapContext.map.setZoom(zoom);
29039     },
29040     
29041     show: function()
29042     {
29043         if(!this.el){
29044             return;
29045         }
29046         
29047         this.el.show();
29048         
29049         this.resize();
29050         
29051         this.fireEvent('show', this);
29052     },
29053     
29054     hide: function()
29055     {
29056         if(!this.el){
29057             return;
29058         }
29059         
29060         this.el.hide();
29061         
29062         this.fireEvent('hide', this);
29063     }
29064     
29065 });
29066
29067 Roo.apply(Roo.bootstrap.LocationPicker, {
29068     
29069     OverlayView : function(map, options)
29070     {
29071         options = options || {};
29072         
29073         this.setMap(map);
29074     }
29075     
29076     
29077 });/**
29078  * @class Roo.bootstrap.Alert
29079  * @extends Roo.bootstrap.Component
29080  * Bootstrap Alert class - shows an alert area box
29081  * eg
29082  * <div class="alert alert-danger" role="alert"><span class="fa fa-exclamation-triangle"></span><span class="sr-only">Error:</span>
29083   Enter a valid email address
29084 </div>
29085  * @licence LGPL
29086  * @cfg {String} title The title of alert
29087  * @cfg {String} html The content of alert
29088  * @cfg {String} weight (  success | info | warning | danger )
29089  * @cfg {String} faicon font-awesomeicon
29090  * 
29091  * @constructor
29092  * Create a new alert
29093  * @param {Object} config The config object
29094  */
29095
29096
29097 Roo.bootstrap.Alert = function(config){
29098     Roo.bootstrap.Alert.superclass.constructor.call(this, config);
29099     
29100 };
29101
29102 Roo.extend(Roo.bootstrap.Alert, Roo.bootstrap.Component,  {
29103     
29104     title: '',
29105     html: '',
29106     weight: false,
29107     faicon: false,
29108     
29109     getAutoCreate : function()
29110     {
29111         
29112         var cfg = {
29113             tag : 'div',
29114             cls : 'alert',
29115             cn : [
29116                 {
29117                     tag : 'i',
29118                     cls : 'roo-alert-icon'
29119                     
29120                 },
29121                 {
29122                     tag : 'b',
29123                     cls : 'roo-alert-title',
29124                     html : this.title
29125                 },
29126                 {
29127                     tag : 'span',
29128                     cls : 'roo-alert-text',
29129                     html : this.html
29130                 }
29131             ]
29132         };
29133         
29134         if(this.faicon){
29135             cfg.cn[0].cls += ' fa ' + this.faicon;
29136         }
29137         
29138         if(this.weight){
29139             cfg.cls += ' alert-' + this.weight;
29140         }
29141         
29142         return cfg;
29143     },
29144     
29145     initEvents: function() 
29146     {
29147         this.el.setVisibilityMode(Roo.Element.DISPLAY);
29148     },
29149     
29150     setTitle : function(str)
29151     {
29152         this.el.select('.roo-alert-title',true).first().dom.innerHTML = str;
29153     },
29154     
29155     setText : function(str)
29156     {
29157         this.el.select('.roo-alert-text',true).first().dom.innerHTML = str;
29158     },
29159     
29160     setWeight : function(weight)
29161     {
29162         if(this.weight){
29163             this.el.select('.alert',true).first().removeClass('alert-' + this.weight);
29164         }
29165         
29166         this.weight = weight;
29167         
29168         this.el.select('.alert',true).first().addClass('alert-' + this.weight);
29169     },
29170     
29171     setIcon : function(icon)
29172     {
29173         if(this.faicon){
29174             this.el.select('.roo-alert-icon',true).first().removeClass(['fa', 'fa-' + this.faicon]);
29175         }
29176         
29177         this.faicon = icon;
29178         
29179         this.el.select('.roo-alert-icon',true).first().addClass(['fa', 'fa-' + this.faicon]);
29180     },
29181     
29182     hide: function() 
29183     {
29184         this.el.hide();   
29185     },
29186     
29187     show: function() 
29188     {  
29189         this.el.show();   
29190     }
29191     
29192 });
29193
29194  
29195 /*
29196 * Licence: LGPL
29197 */
29198
29199 /**
29200  * @class Roo.bootstrap.UploadCropbox
29201  * @extends Roo.bootstrap.Component
29202  * Bootstrap UploadCropbox class
29203  * @cfg {String} emptyText show when image has been loaded
29204  * @cfg {String} rotateNotify show when image too small to rotate
29205  * @cfg {Number} errorTimeout default 3000
29206  * @cfg {Number} minWidth default 300
29207  * @cfg {Number} minHeight default 300
29208  * @cfg {Array} buttons default ['rotateLeft', 'pictureBtn', 'rotateRight']
29209  * @cfg {Boolean} isDocument (true|false) default false
29210  * @cfg {String} url action url
29211  * @cfg {String} paramName default 'imageUpload'
29212  * @cfg {String} method default POST
29213  * @cfg {Boolean} loadMask (true|false) default true
29214  * @cfg {Boolean} loadingText default 'Loading...'
29215  * 
29216  * @constructor
29217  * Create a new UploadCropbox
29218  * @param {Object} config The config object
29219  */
29220
29221 Roo.bootstrap.UploadCropbox = function(config){
29222     Roo.bootstrap.UploadCropbox.superclass.constructor.call(this, config);
29223     
29224     this.addEvents({
29225         /**
29226          * @event beforeselectfile
29227          * Fire before select file
29228          * @param {Roo.bootstrap.UploadCropbox} this
29229          */
29230         "beforeselectfile" : true,
29231         /**
29232          * @event initial
29233          * Fire after initEvent
29234          * @param {Roo.bootstrap.UploadCropbox} this
29235          */
29236         "initial" : true,
29237         /**
29238          * @event crop
29239          * Fire after initEvent
29240          * @param {Roo.bootstrap.UploadCropbox} this
29241          * @param {String} data
29242          */
29243         "crop" : true,
29244         /**
29245          * @event prepare
29246          * Fire when preparing the file data
29247          * @param {Roo.bootstrap.UploadCropbox} this
29248          * @param {Object} file
29249          */
29250         "prepare" : true,
29251         /**
29252          * @event exception
29253          * Fire when get exception
29254          * @param {Roo.bootstrap.UploadCropbox} this
29255          * @param {XMLHttpRequest} xhr
29256          */
29257         "exception" : true,
29258         /**
29259          * @event beforeloadcanvas
29260          * Fire before load the canvas
29261          * @param {Roo.bootstrap.UploadCropbox} this
29262          * @param {String} src
29263          */
29264         "beforeloadcanvas" : true,
29265         /**
29266          * @event trash
29267          * Fire when trash image
29268          * @param {Roo.bootstrap.UploadCropbox} this
29269          */
29270         "trash" : true,
29271         /**
29272          * @event download
29273          * Fire when download the image
29274          * @param {Roo.bootstrap.UploadCropbox} this
29275          */
29276         "download" : true,
29277         /**
29278          * @event footerbuttonclick
29279          * Fire when footerbuttonclick
29280          * @param {Roo.bootstrap.UploadCropbox} this
29281          * @param {String} type
29282          */
29283         "footerbuttonclick" : true,
29284         /**
29285          * @event resize
29286          * Fire when resize
29287          * @param {Roo.bootstrap.UploadCropbox} this
29288          */
29289         "resize" : true,
29290         /**
29291          * @event rotate
29292          * Fire when rotate the image
29293          * @param {Roo.bootstrap.UploadCropbox} this
29294          * @param {String} pos
29295          */
29296         "rotate" : true,
29297         /**
29298          * @event inspect
29299          * Fire when inspect the file
29300          * @param {Roo.bootstrap.UploadCropbox} this
29301          * @param {Object} file
29302          */
29303         "inspect" : true,
29304         /**
29305          * @event upload
29306          * Fire when xhr upload the file
29307          * @param {Roo.bootstrap.UploadCropbox} this
29308          * @param {Object} data
29309          */
29310         "upload" : true,
29311         /**
29312          * @event arrange
29313          * Fire when arrange the file data
29314          * @param {Roo.bootstrap.UploadCropbox} this
29315          * @param {Object} formData
29316          */
29317         "arrange" : true
29318     });
29319     
29320     this.buttons = this.buttons || Roo.bootstrap.UploadCropbox.footer.STANDARD;
29321 };
29322
29323 Roo.extend(Roo.bootstrap.UploadCropbox, Roo.bootstrap.Component,  {
29324     
29325     emptyText : 'Click to upload image',
29326     rotateNotify : 'Image is too small to rotate',
29327     errorTimeout : 3000,
29328     scale : 0,
29329     baseScale : 1,
29330     rotate : 0,
29331     dragable : false,
29332     pinching : false,
29333     mouseX : 0,
29334     mouseY : 0,
29335     cropData : false,
29336     minWidth : 300,
29337     minHeight : 300,
29338     file : false,
29339     exif : {},
29340     baseRotate : 1,
29341     cropType : 'image/jpeg',
29342     buttons : false,
29343     canvasLoaded : false,
29344     isDocument : false,
29345     method : 'POST',
29346     paramName : 'imageUpload',
29347     loadMask : true,
29348     loadingText : 'Loading...',
29349     maskEl : false,
29350     
29351     getAutoCreate : function()
29352     {
29353         var cfg = {
29354             tag : 'div',
29355             cls : 'roo-upload-cropbox',
29356             cn : [
29357                 {
29358                     tag : 'input',
29359                     cls : 'roo-upload-cropbox-selector',
29360                     type : 'file'
29361                 },
29362                 {
29363                     tag : 'div',
29364                     cls : 'roo-upload-cropbox-body',
29365                     style : 'cursor:pointer',
29366                     cn : [
29367                         {
29368                             tag : 'div',
29369                             cls : 'roo-upload-cropbox-preview'
29370                         },
29371                         {
29372                             tag : 'div',
29373                             cls : 'roo-upload-cropbox-thumb'
29374                         },
29375                         {
29376                             tag : 'div',
29377                             cls : 'roo-upload-cropbox-empty-notify',
29378                             html : this.emptyText
29379                         },
29380                         {
29381                             tag : 'div',
29382                             cls : 'roo-upload-cropbox-error-notify alert alert-danger',
29383                             html : this.rotateNotify
29384                         }
29385                     ]
29386                 },
29387                 {
29388                     tag : 'div',
29389                     cls : 'roo-upload-cropbox-footer',
29390                     cn : {
29391                         tag : 'div',
29392                         cls : 'btn-group btn-group-justified roo-upload-cropbox-btn-group',
29393                         cn : []
29394                     }
29395                 }
29396             ]
29397         };
29398         
29399         return cfg;
29400     },
29401     
29402     onRender : function(ct, position)
29403     {
29404         Roo.bootstrap.UploadCropbox.superclass.onRender.call(this, ct, position);
29405         
29406         if (this.buttons.length) {
29407             
29408             Roo.each(this.buttons, function(bb) {
29409                 
29410                 var btn = this.el.select('.roo-upload-cropbox-footer div.roo-upload-cropbox-btn-group').first().createChild(bb);
29411                 
29412                 btn.on('click', this.onFooterButtonClick.createDelegate(this, [bb.action], true));
29413                 
29414             }, this);
29415         }
29416         
29417         if(this.loadMask){
29418             this.maskEl = this.el;
29419         }
29420     },
29421     
29422     initEvents : function()
29423     {
29424         this.urlAPI = (window.createObjectURL && window) || 
29425                                 (window.URL && URL.revokeObjectURL && URL) || 
29426                                 (window.webkitURL && webkitURL);
29427                         
29428         this.bodyEl = this.el.select('.roo-upload-cropbox-body', true).first();
29429         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29430         
29431         this.selectorEl = this.el.select('.roo-upload-cropbox-selector', true).first();
29432         this.selectorEl.hide();
29433         
29434         this.previewEl = this.el.select('.roo-upload-cropbox-preview', true).first();
29435         this.previewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29436         
29437         this.thumbEl = this.el.select('.roo-upload-cropbox-thumb', true).first();
29438         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29439         this.thumbEl.hide();
29440         
29441         this.notifyEl = this.el.select('.roo-upload-cropbox-empty-notify', true).first();
29442         this.notifyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29443         
29444         this.errorEl = this.el.select('.roo-upload-cropbox-error-notify', true).first();
29445         this.errorEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29446         this.errorEl.hide();
29447         
29448         this.footerEl = this.el.select('.roo-upload-cropbox-footer', true).first();
29449         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29450         this.footerEl.hide();
29451         
29452         this.setThumbBoxSize();
29453         
29454         this.bind();
29455         
29456         this.resize();
29457         
29458         this.fireEvent('initial', this);
29459     },
29460
29461     bind : function()
29462     {
29463         var _this = this;
29464         
29465         window.addEventListener("resize", function() { _this.resize(); } );
29466         
29467         this.bodyEl.on('click', this.beforeSelectFile, this);
29468         
29469         if(Roo.isTouch){
29470             this.bodyEl.on('touchstart', this.onTouchStart, this);
29471             this.bodyEl.on('touchmove', this.onTouchMove, this);
29472             this.bodyEl.on('touchend', this.onTouchEnd, this);
29473         }
29474         
29475         if(!Roo.isTouch){
29476             this.bodyEl.on('mousedown', this.onMouseDown, this);
29477             this.bodyEl.on('mousemove', this.onMouseMove, this);
29478             var mousewheel = (/Firefox/i.test(navigator.userAgent))? 'DOMMouseScroll' : 'mousewheel';
29479             this.bodyEl.on(mousewheel, this.onMouseWheel, this);
29480             Roo.get(document).on('mouseup', this.onMouseUp, this);
29481         }
29482         
29483         this.selectorEl.on('change', this.onFileSelected, this);
29484     },
29485     
29486     reset : function()
29487     {    
29488         this.scale = 0;
29489         this.baseScale = 1;
29490         this.rotate = 0;
29491         this.baseRotate = 1;
29492         this.dragable = false;
29493         this.pinching = false;
29494         this.mouseX = 0;
29495         this.mouseY = 0;
29496         this.cropData = false;
29497         this.notifyEl.dom.innerHTML = this.emptyText;
29498         
29499         this.selectorEl.dom.value = '';
29500         
29501     },
29502     
29503     resize : function()
29504     {
29505         if(this.fireEvent('resize', this) != false){
29506             this.setThumbBoxPosition();
29507             this.setCanvasPosition();
29508         }
29509     },
29510     
29511     onFooterButtonClick : function(e, el, o, type)
29512     {
29513         switch (type) {
29514             case 'rotate-left' :
29515                 this.onRotateLeft(e);
29516                 break;
29517             case 'rotate-right' :
29518                 this.onRotateRight(e);
29519                 break;
29520             case 'picture' :
29521                 this.beforeSelectFile(e);
29522                 break;
29523             case 'trash' :
29524                 this.trash(e);
29525                 break;
29526             case 'crop' :
29527                 this.crop(e);
29528                 break;
29529             case 'download' :
29530                 this.download(e);
29531                 break;
29532             default :
29533                 break;
29534         }
29535         
29536         this.fireEvent('footerbuttonclick', this, type);
29537     },
29538     
29539     beforeSelectFile : function(e)
29540     {
29541         e.preventDefault();
29542         
29543         if(this.fireEvent('beforeselectfile', this) != false){
29544             this.selectorEl.dom.click();
29545         }
29546     },
29547     
29548     onFileSelected : function(e)
29549     {
29550         e.preventDefault();
29551         
29552         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
29553             return;
29554         }
29555         
29556         var file = this.selectorEl.dom.files[0];
29557         
29558         if(this.fireEvent('inspect', this, file) != false){
29559             this.prepare(file);
29560         }
29561         
29562     },
29563     
29564     trash : function(e)
29565     {
29566         this.fireEvent('trash', this);
29567     },
29568     
29569     download : function(e)
29570     {
29571         this.fireEvent('download', this);
29572     },
29573     
29574     loadCanvas : function(src)
29575     {   
29576         if(this.fireEvent('beforeloadcanvas', this, src) != false){
29577             
29578             this.reset();
29579             
29580             this.imageEl = document.createElement('img');
29581             
29582             var _this = this;
29583             
29584             this.imageEl.addEventListener("load", function(){ _this.onLoadCanvas(); });
29585             
29586             this.imageEl.src = src;
29587         }
29588     },
29589     
29590     onLoadCanvas : function()
29591     {   
29592         this.imageEl.OriginWidth = this.imageEl.naturalWidth || this.imageEl.width;
29593         this.imageEl.OriginHeight = this.imageEl.naturalHeight || this.imageEl.height;
29594         
29595         this.bodyEl.un('click', this.beforeSelectFile, this);
29596         
29597         this.notifyEl.hide();
29598         this.thumbEl.show();
29599         this.footerEl.show();
29600         
29601         this.baseRotateLevel();
29602         
29603         if(this.isDocument){
29604             this.setThumbBoxSize();
29605         }
29606         
29607         this.setThumbBoxPosition();
29608         
29609         this.baseScaleLevel();
29610         
29611         this.draw();
29612         
29613         this.resize();
29614         
29615         this.canvasLoaded = true;
29616         
29617         if(this.loadMask){
29618             this.maskEl.unmask();
29619         }
29620         
29621     },
29622     
29623     setCanvasPosition : function()
29624     {   
29625         if(!this.canvasEl){
29626             return;
29627         }
29628         
29629         var pw = Math.ceil((this.bodyEl.getWidth() - this.canvasEl.width) / 2);
29630         var ph = Math.ceil((this.bodyEl.getHeight() - this.canvasEl.height) / 2);
29631         
29632         this.previewEl.setLeft(pw);
29633         this.previewEl.setTop(ph);
29634         
29635     },
29636     
29637     onMouseDown : function(e)
29638     {   
29639         e.stopEvent();
29640         
29641         this.dragable = true;
29642         this.pinching = false;
29643         
29644         if(this.isDocument && (this.canvasEl.width < this.thumbEl.getWidth() || this.canvasEl.height < this.thumbEl.getHeight())){
29645             this.dragable = false;
29646             return;
29647         }
29648         
29649         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
29650         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
29651         
29652     },
29653     
29654     onMouseMove : function(e)
29655     {   
29656         e.stopEvent();
29657         
29658         if(!this.canvasLoaded){
29659             return;
29660         }
29661         
29662         if (!this.dragable){
29663             return;
29664         }
29665         
29666         var minX = Math.ceil(this.thumbEl.getLeft(true));
29667         var minY = Math.ceil(this.thumbEl.getTop(true));
29668         
29669         var maxX = Math.ceil(minX + this.thumbEl.getWidth() - this.canvasEl.width);
29670         var maxY = Math.ceil(minY + this.thumbEl.getHeight() - this.canvasEl.height);
29671         
29672         var x = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
29673         var y = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
29674         
29675         x = x - this.mouseX;
29676         y = y - this.mouseY;
29677         
29678         var bgX = Math.ceil(x + this.previewEl.getLeft(true));
29679         var bgY = Math.ceil(y + this.previewEl.getTop(true));
29680         
29681         bgX = (minX < bgX) ? minX : ((maxX > bgX) ? maxX : bgX);
29682         bgY = (minY < bgY) ? minY : ((maxY > bgY) ? maxY : bgY);
29683         
29684         this.previewEl.setLeft(bgX);
29685         this.previewEl.setTop(bgY);
29686         
29687         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
29688         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
29689     },
29690     
29691     onMouseUp : function(e)
29692     {   
29693         e.stopEvent();
29694         
29695         this.dragable = false;
29696     },
29697     
29698     onMouseWheel : function(e)
29699     {   
29700         e.stopEvent();
29701         
29702         this.startScale = this.scale;
29703         
29704         this.scale = (e.getWheelDelta() == 1) ? (this.scale + 1) : (this.scale - 1);
29705         
29706         if(!this.zoomable()){
29707             this.scale = this.startScale;
29708             return;
29709         }
29710         
29711         this.draw();
29712         
29713         return;
29714     },
29715     
29716     zoomable : function()
29717     {
29718         var minScale = this.thumbEl.getWidth() / this.minWidth;
29719         
29720         if(this.minWidth < this.minHeight){
29721             minScale = this.thumbEl.getHeight() / this.minHeight;
29722         }
29723         
29724         var width = Math.ceil(this.imageEl.OriginWidth * this.getScaleLevel() / minScale);
29725         var height = Math.ceil(this.imageEl.OriginHeight * this.getScaleLevel() / minScale);
29726         
29727         if(
29728                 this.isDocument &&
29729                 (this.rotate == 0 || this.rotate == 180) && 
29730                 (
29731                     width > this.imageEl.OriginWidth || 
29732                     height > this.imageEl.OriginHeight ||
29733                     (width < this.minWidth && height < this.minHeight)
29734                 )
29735         ){
29736             return false;
29737         }
29738         
29739         if(
29740                 this.isDocument &&
29741                 (this.rotate == 90 || this.rotate == 270) && 
29742                 (
29743                     width > this.imageEl.OriginWidth || 
29744                     height > this.imageEl.OriginHeight ||
29745                     (width < this.minHeight && height < this.minWidth)
29746                 )
29747         ){
29748             return false;
29749         }
29750         
29751         if(
29752                 !this.isDocument &&
29753                 (this.rotate == 0 || this.rotate == 180) && 
29754                 (
29755                     width < this.minWidth || 
29756                     width > this.imageEl.OriginWidth || 
29757                     height < this.minHeight || 
29758                     height > this.imageEl.OriginHeight
29759                 )
29760         ){
29761             return false;
29762         }
29763         
29764         if(
29765                 !this.isDocument &&
29766                 (this.rotate == 90 || this.rotate == 270) && 
29767                 (
29768                     width < this.minHeight || 
29769                     width > this.imageEl.OriginWidth || 
29770                     height < this.minWidth || 
29771                     height > this.imageEl.OriginHeight
29772                 )
29773         ){
29774             return false;
29775         }
29776         
29777         return true;
29778         
29779     },
29780     
29781     onRotateLeft : function(e)
29782     {   
29783         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
29784             
29785             var minScale = this.thumbEl.getWidth() / this.minWidth;
29786             
29787             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
29788             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
29789             
29790             this.startScale = this.scale;
29791             
29792             while (this.getScaleLevel() < minScale){
29793             
29794                 this.scale = this.scale + 1;
29795                 
29796                 if(!this.zoomable()){
29797                     break;
29798                 }
29799                 
29800                 if(
29801                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
29802                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
29803                 ){
29804                     continue;
29805                 }
29806                 
29807                 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
29808
29809                 this.draw();
29810                 
29811                 return;
29812             }
29813             
29814             this.scale = this.startScale;
29815             
29816             this.onRotateFail();
29817             
29818             return false;
29819         }
29820         
29821         this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
29822
29823         if(this.isDocument){
29824             this.setThumbBoxSize();
29825             this.setThumbBoxPosition();
29826             this.setCanvasPosition();
29827         }
29828         
29829         this.draw();
29830         
29831         this.fireEvent('rotate', this, 'left');
29832         
29833     },
29834     
29835     onRotateRight : function(e)
29836     {
29837         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
29838             
29839             var minScale = this.thumbEl.getWidth() / this.minWidth;
29840         
29841             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
29842             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
29843             
29844             this.startScale = this.scale;
29845             
29846             while (this.getScaleLevel() < minScale){
29847             
29848                 this.scale = this.scale + 1;
29849                 
29850                 if(!this.zoomable()){
29851                     break;
29852                 }
29853                 
29854                 if(
29855                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
29856                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
29857                 ){
29858                     continue;
29859                 }
29860                 
29861                 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
29862
29863                 this.draw();
29864                 
29865                 return;
29866             }
29867             
29868             this.scale = this.startScale;
29869             
29870             this.onRotateFail();
29871             
29872             return false;
29873         }
29874         
29875         this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
29876
29877         if(this.isDocument){
29878             this.setThumbBoxSize();
29879             this.setThumbBoxPosition();
29880             this.setCanvasPosition();
29881         }
29882         
29883         this.draw();
29884         
29885         this.fireEvent('rotate', this, 'right');
29886     },
29887     
29888     onRotateFail : function()
29889     {
29890         this.errorEl.show(true);
29891         
29892         var _this = this;
29893         
29894         (function() { _this.errorEl.hide(true); }).defer(this.errorTimeout);
29895     },
29896     
29897     draw : function()
29898     {
29899         this.previewEl.dom.innerHTML = '';
29900         
29901         var canvasEl = document.createElement("canvas");
29902         
29903         var contextEl = canvasEl.getContext("2d");
29904         
29905         canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
29906         canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
29907         var center = this.imageEl.OriginWidth / 2;
29908         
29909         if(this.imageEl.OriginWidth < this.imageEl.OriginHeight){
29910             canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
29911             canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
29912             center = this.imageEl.OriginHeight / 2;
29913         }
29914         
29915         contextEl.scale(this.getScaleLevel(), this.getScaleLevel());
29916         
29917         contextEl.translate(center, center);
29918         contextEl.rotate(this.rotate * Math.PI / 180);
29919
29920         contextEl.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
29921         
29922         this.canvasEl = document.createElement("canvas");
29923         
29924         this.contextEl = this.canvasEl.getContext("2d");
29925         
29926         switch (this.rotate) {
29927             case 0 :
29928                 
29929                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
29930                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
29931                 
29932                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
29933                 
29934                 break;
29935             case 90 : 
29936                 
29937                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
29938                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
29939                 
29940                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
29941                     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);
29942                     break;
29943                 }
29944                 
29945                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
29946                 
29947                 break;
29948             case 180 :
29949                 
29950                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
29951                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
29952                 
29953                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
29954                     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);
29955                     break;
29956                 }
29957                 
29958                 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);
29959                 
29960                 break;
29961             case 270 :
29962                 
29963                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
29964                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
29965         
29966                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
29967                     this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
29968                     break;
29969                 }
29970                 
29971                 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);
29972                 
29973                 break;
29974             default : 
29975                 break;
29976         }
29977         
29978         this.previewEl.appendChild(this.canvasEl);
29979         
29980         this.setCanvasPosition();
29981     },
29982     
29983     crop : function()
29984     {
29985         if(!this.canvasLoaded){
29986             return;
29987         }
29988         
29989         var imageCanvas = document.createElement("canvas");
29990         
29991         var imageContext = imageCanvas.getContext("2d");
29992         
29993         imageCanvas.width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
29994         imageCanvas.height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
29995         
29996         var center = imageCanvas.width / 2;
29997         
29998         imageContext.translate(center, center);
29999         
30000         imageContext.rotate(this.rotate * Math.PI / 180);
30001         
30002         imageContext.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
30003         
30004         var canvas = document.createElement("canvas");
30005         
30006         var context = canvas.getContext("2d");
30007                 
30008         canvas.width = this.minWidth;
30009         canvas.height = this.minHeight;
30010
30011         switch (this.rotate) {
30012             case 0 :
30013                 
30014                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
30015                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
30016                 
30017                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
30018                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
30019                 
30020                 var targetWidth = this.minWidth - 2 * x;
30021                 var targetHeight = this.minHeight - 2 * y;
30022                 
30023                 var scale = 1;
30024                 
30025                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
30026                     scale = targetWidth / width;
30027                 }
30028                 
30029                 if(x > 0 && y == 0){
30030                     scale = targetHeight / height;
30031                 }
30032                 
30033                 if(x > 0 && y > 0){
30034                     scale = targetWidth / width;
30035                     
30036                     if(width < height){
30037                         scale = targetHeight / height;
30038                     }
30039                 }
30040                 
30041                 context.scale(scale, scale);
30042                 
30043                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
30044                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
30045
30046                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
30047                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
30048
30049                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
30050                 
30051                 break;
30052             case 90 : 
30053                 
30054                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
30055                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
30056                 
30057                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
30058                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
30059                 
30060                 var targetWidth = this.minWidth - 2 * x;
30061                 var targetHeight = this.minHeight - 2 * y;
30062                 
30063                 var scale = 1;
30064                 
30065                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
30066                     scale = targetWidth / width;
30067                 }
30068                 
30069                 if(x > 0 && y == 0){
30070                     scale = targetHeight / height;
30071                 }
30072                 
30073                 if(x > 0 && y > 0){
30074                     scale = targetWidth / width;
30075                     
30076                     if(width < height){
30077                         scale = targetHeight / height;
30078                     }
30079                 }
30080                 
30081                 context.scale(scale, scale);
30082                 
30083                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
30084                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
30085
30086                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
30087                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
30088                 
30089                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
30090                 
30091                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
30092                 
30093                 break;
30094             case 180 :
30095                 
30096                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
30097                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
30098                 
30099                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
30100                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
30101                 
30102                 var targetWidth = this.minWidth - 2 * x;
30103                 var targetHeight = this.minHeight - 2 * y;
30104                 
30105                 var scale = 1;
30106                 
30107                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
30108                     scale = targetWidth / width;
30109                 }
30110                 
30111                 if(x > 0 && y == 0){
30112                     scale = targetHeight / height;
30113                 }
30114                 
30115                 if(x > 0 && y > 0){
30116                     scale = targetWidth / width;
30117                     
30118                     if(width < height){
30119                         scale = targetHeight / height;
30120                     }
30121                 }
30122                 
30123                 context.scale(scale, scale);
30124                 
30125                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
30126                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
30127
30128                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
30129                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
30130
30131                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
30132                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
30133                 
30134                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
30135                 
30136                 break;
30137             case 270 :
30138                 
30139                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
30140                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
30141                 
30142                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
30143                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
30144                 
30145                 var targetWidth = this.minWidth - 2 * x;
30146                 var targetHeight = this.minHeight - 2 * y;
30147                 
30148                 var scale = 1;
30149                 
30150                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
30151                     scale = targetWidth / width;
30152                 }
30153                 
30154                 if(x > 0 && y == 0){
30155                     scale = targetHeight / height;
30156                 }
30157                 
30158                 if(x > 0 && y > 0){
30159                     scale = targetWidth / width;
30160                     
30161                     if(width < height){
30162                         scale = targetHeight / height;
30163                     }
30164                 }
30165                 
30166                 context.scale(scale, scale);
30167                 
30168                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
30169                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
30170
30171                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
30172                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
30173                 
30174                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
30175                 
30176                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
30177                 
30178                 break;
30179             default : 
30180                 break;
30181         }
30182         
30183         this.cropData = canvas.toDataURL(this.cropType);
30184         
30185         if(this.fireEvent('crop', this, this.cropData) !== false){
30186             this.process(this.file, this.cropData);
30187         }
30188         
30189         return;
30190         
30191     },
30192     
30193     setThumbBoxSize : function()
30194     {
30195         var width, height;
30196         
30197         if(this.isDocument && typeof(this.imageEl) != 'undefined'){
30198             width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.max(this.minWidth, this.minHeight) : Math.min(this.minWidth, this.minHeight);
30199             height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.min(this.minWidth, this.minHeight) : Math.max(this.minWidth, this.minHeight);
30200             
30201             this.minWidth = width;
30202             this.minHeight = height;
30203             
30204             if(this.rotate == 90 || this.rotate == 270){
30205                 this.minWidth = height;
30206                 this.minHeight = width;
30207             }
30208         }
30209         
30210         height = 300;
30211         width = Math.ceil(this.minWidth * height / this.minHeight);
30212         
30213         if(this.minWidth > this.minHeight){
30214             width = 300;
30215             height = Math.ceil(this.minHeight * width / this.minWidth);
30216         }
30217         
30218         this.thumbEl.setStyle({
30219             width : width + 'px',
30220             height : height + 'px'
30221         });
30222
30223         return;
30224             
30225     },
30226     
30227     setThumbBoxPosition : function()
30228     {
30229         var x = Math.ceil((this.bodyEl.getWidth() - this.thumbEl.getWidth()) / 2 );
30230         var y = Math.ceil((this.bodyEl.getHeight() - this.thumbEl.getHeight()) / 2);
30231         
30232         this.thumbEl.setLeft(x);
30233         this.thumbEl.setTop(y);
30234         
30235     },
30236     
30237     baseRotateLevel : function()
30238     {
30239         this.baseRotate = 1;
30240         
30241         if(
30242                 typeof(this.exif) != 'undefined' &&
30243                 typeof(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != 'undefined' &&
30244                 [1, 3, 6, 8].indexOf(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != -1
30245         ){
30246             this.baseRotate = this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']];
30247         }
30248         
30249         this.rotate = Roo.bootstrap.UploadCropbox['Orientation'][this.baseRotate];
30250         
30251     },
30252     
30253     baseScaleLevel : function()
30254     {
30255         var width, height;
30256         
30257         if(this.isDocument){
30258             
30259             if(this.baseRotate == 6 || this.baseRotate == 8){
30260             
30261                 height = this.thumbEl.getHeight();
30262                 this.baseScale = height / this.imageEl.OriginWidth;
30263
30264                 if(this.imageEl.OriginHeight * this.baseScale > this.thumbEl.getWidth()){
30265                     width = this.thumbEl.getWidth();
30266                     this.baseScale = width / this.imageEl.OriginHeight;
30267                 }
30268
30269                 return;
30270             }
30271
30272             height = this.thumbEl.getHeight();
30273             this.baseScale = height / this.imageEl.OriginHeight;
30274
30275             if(this.imageEl.OriginWidth * this.baseScale > this.thumbEl.getWidth()){
30276                 width = this.thumbEl.getWidth();
30277                 this.baseScale = width / this.imageEl.OriginWidth;
30278             }
30279
30280             return;
30281         }
30282         
30283         if(this.baseRotate == 6 || this.baseRotate == 8){
30284             
30285             width = this.thumbEl.getHeight();
30286             this.baseScale = width / this.imageEl.OriginHeight;
30287             
30288             if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getWidth()){
30289                 height = this.thumbEl.getWidth();
30290                 this.baseScale = height / this.imageEl.OriginHeight;
30291             }
30292             
30293             if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
30294                 height = this.thumbEl.getWidth();
30295                 this.baseScale = height / this.imageEl.OriginHeight;
30296                 
30297                 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getHeight()){
30298                     width = this.thumbEl.getHeight();
30299                     this.baseScale = width / this.imageEl.OriginWidth;
30300                 }
30301             }
30302             
30303             return;
30304         }
30305         
30306         width = this.thumbEl.getWidth();
30307         this.baseScale = width / this.imageEl.OriginWidth;
30308         
30309         if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getHeight()){
30310             height = this.thumbEl.getHeight();
30311             this.baseScale = height / this.imageEl.OriginHeight;
30312         }
30313         
30314         if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
30315             
30316             height = this.thumbEl.getHeight();
30317             this.baseScale = height / this.imageEl.OriginHeight;
30318             
30319             if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getWidth()){
30320                 width = this.thumbEl.getWidth();
30321                 this.baseScale = width / this.imageEl.OriginWidth;
30322             }
30323             
30324         }
30325         
30326         return;
30327     },
30328     
30329     getScaleLevel : function()
30330     {
30331         return this.baseScale * Math.pow(1.1, this.scale);
30332     },
30333     
30334     onTouchStart : function(e)
30335     {
30336         if(!this.canvasLoaded){
30337             this.beforeSelectFile(e);
30338             return;
30339         }
30340         
30341         var touches = e.browserEvent.touches;
30342         
30343         if(!touches){
30344             return;
30345         }
30346         
30347         if(touches.length == 1){
30348             this.onMouseDown(e);
30349             return;
30350         }
30351         
30352         if(touches.length != 2){
30353             return;
30354         }
30355         
30356         var coords = [];
30357         
30358         for(var i = 0, finger; finger = touches[i]; i++){
30359             coords.push(finger.pageX, finger.pageY);
30360         }
30361         
30362         var x = Math.pow(coords[0] - coords[2], 2);
30363         var y = Math.pow(coords[1] - coords[3], 2);
30364         
30365         this.startDistance = Math.sqrt(x + y);
30366         
30367         this.startScale = this.scale;
30368         
30369         this.pinching = true;
30370         this.dragable = false;
30371         
30372     },
30373     
30374     onTouchMove : function(e)
30375     {
30376         if(!this.pinching && !this.dragable){
30377             return;
30378         }
30379         
30380         var touches = e.browserEvent.touches;
30381         
30382         if(!touches){
30383             return;
30384         }
30385         
30386         if(this.dragable){
30387             this.onMouseMove(e);
30388             return;
30389         }
30390         
30391         var coords = [];
30392         
30393         for(var i = 0, finger; finger = touches[i]; i++){
30394             coords.push(finger.pageX, finger.pageY);
30395         }
30396         
30397         var x = Math.pow(coords[0] - coords[2], 2);
30398         var y = Math.pow(coords[1] - coords[3], 2);
30399         
30400         this.endDistance = Math.sqrt(x + y);
30401         
30402         this.scale = this.startScale + Math.floor(Math.log(this.endDistance / this.startDistance) / Math.log(1.1));
30403         
30404         if(!this.zoomable()){
30405             this.scale = this.startScale;
30406             return;
30407         }
30408         
30409         this.draw();
30410         
30411     },
30412     
30413     onTouchEnd : function(e)
30414     {
30415         this.pinching = false;
30416         this.dragable = false;
30417         
30418     },
30419     
30420     process : function(file, crop)
30421     {
30422         if(this.loadMask){
30423             this.maskEl.mask(this.loadingText);
30424         }
30425         
30426         this.xhr = new XMLHttpRequest();
30427         
30428         file.xhr = this.xhr;
30429
30430         this.xhr.open(this.method, this.url, true);
30431         
30432         var headers = {
30433             "Accept": "application/json",
30434             "Cache-Control": "no-cache",
30435             "X-Requested-With": "XMLHttpRequest"
30436         };
30437         
30438         for (var headerName in headers) {
30439             var headerValue = headers[headerName];
30440             if (headerValue) {
30441                 this.xhr.setRequestHeader(headerName, headerValue);
30442             }
30443         }
30444         
30445         var _this = this;
30446         
30447         this.xhr.onload = function()
30448         {
30449             _this.xhrOnLoad(_this.xhr);
30450         }
30451         
30452         this.xhr.onerror = function()
30453         {
30454             _this.xhrOnError(_this.xhr);
30455         }
30456         
30457         var formData = new FormData();
30458
30459         formData.append('returnHTML', 'NO');
30460         
30461         if(crop){
30462             formData.append('crop', crop);
30463         }
30464         
30465         if(typeof(file) != 'undefined' && (typeof(file.id) == 'undefined' || file.id * 1 < 1)){
30466             formData.append(this.paramName, file, file.name);
30467         }
30468         
30469         if(typeof(file.filename) != 'undefined'){
30470             formData.append('filename', file.filename);
30471         }
30472         
30473         if(typeof(file.mimetype) != 'undefined'){
30474             formData.append('mimetype', file.mimetype);
30475         }
30476         
30477         if(this.fireEvent('arrange', this, formData) != false){
30478             this.xhr.send(formData);
30479         };
30480     },
30481     
30482     xhrOnLoad : function(xhr)
30483     {
30484         if(this.loadMask){
30485             this.maskEl.unmask();
30486         }
30487         
30488         if (xhr.readyState !== 4) {
30489             this.fireEvent('exception', this, xhr);
30490             return;
30491         }
30492
30493         var response = Roo.decode(xhr.responseText);
30494         
30495         if(!response.success){
30496             this.fireEvent('exception', this, xhr);
30497             return;
30498         }
30499         
30500         var response = Roo.decode(xhr.responseText);
30501         
30502         this.fireEvent('upload', this, response);
30503         
30504     },
30505     
30506     xhrOnError : function()
30507     {
30508         if(this.loadMask){
30509             this.maskEl.unmask();
30510         }
30511         
30512         Roo.log('xhr on error');
30513         
30514         var response = Roo.decode(xhr.responseText);
30515           
30516         Roo.log(response);
30517         
30518     },
30519     
30520     prepare : function(file)
30521     {   
30522         if(this.loadMask){
30523             this.maskEl.mask(this.loadingText);
30524         }
30525         
30526         this.file = false;
30527         this.exif = {};
30528         
30529         if(typeof(file) === 'string'){
30530             this.loadCanvas(file);
30531             return;
30532         }
30533         
30534         if(!file || !this.urlAPI){
30535             return;
30536         }
30537         
30538         this.file = file;
30539         this.cropType = file.type;
30540         
30541         var _this = this;
30542         
30543         if(this.fireEvent('prepare', this, this.file) != false){
30544             
30545             var reader = new FileReader();
30546             
30547             reader.onload = function (e) {
30548                 if (e.target.error) {
30549                     Roo.log(e.target.error);
30550                     return;
30551                 }
30552                 
30553                 var buffer = e.target.result,
30554                     dataView = new DataView(buffer),
30555                     offset = 2,
30556                     maxOffset = dataView.byteLength - 4,
30557                     markerBytes,
30558                     markerLength;
30559                 
30560                 if (dataView.getUint16(0) === 0xffd8) {
30561                     while (offset < maxOffset) {
30562                         markerBytes = dataView.getUint16(offset);
30563                         
30564                         if ((markerBytes >= 0xffe0 && markerBytes <= 0xffef) || markerBytes === 0xfffe) {
30565                             markerLength = dataView.getUint16(offset + 2) + 2;
30566                             if (offset + markerLength > dataView.byteLength) {
30567                                 Roo.log('Invalid meta data: Invalid segment size.');
30568                                 break;
30569                             }
30570                             
30571                             if(markerBytes == 0xffe1){
30572                                 _this.parseExifData(
30573                                     dataView,
30574                                     offset,
30575                                     markerLength
30576                                 );
30577                             }
30578                             
30579                             offset += markerLength;
30580                             
30581                             continue;
30582                         }
30583                         
30584                         break;
30585                     }
30586                     
30587                 }
30588                 
30589                 var url = _this.urlAPI.createObjectURL(_this.file);
30590                 
30591                 _this.loadCanvas(url);
30592                 
30593                 return;
30594             }
30595             
30596             reader.readAsArrayBuffer(this.file);
30597             
30598         }
30599         
30600     },
30601     
30602     parseExifData : function(dataView, offset, length)
30603     {
30604         var tiffOffset = offset + 10,
30605             littleEndian,
30606             dirOffset;
30607     
30608         if (dataView.getUint32(offset + 4) !== 0x45786966) {
30609             // No Exif data, might be XMP data instead
30610             return;
30611         }
30612         
30613         // Check for the ASCII code for "Exif" (0x45786966):
30614         if (dataView.getUint32(offset + 4) !== 0x45786966) {
30615             // No Exif data, might be XMP data instead
30616             return;
30617         }
30618         if (tiffOffset + 8 > dataView.byteLength) {
30619             Roo.log('Invalid Exif data: Invalid segment size.');
30620             return;
30621         }
30622         // Check for the two null bytes:
30623         if (dataView.getUint16(offset + 8) !== 0x0000) {
30624             Roo.log('Invalid Exif data: Missing byte alignment offset.');
30625             return;
30626         }
30627         // Check the byte alignment:
30628         switch (dataView.getUint16(tiffOffset)) {
30629         case 0x4949:
30630             littleEndian = true;
30631             break;
30632         case 0x4D4D:
30633             littleEndian = false;
30634             break;
30635         default:
30636             Roo.log('Invalid Exif data: Invalid byte alignment marker.');
30637             return;
30638         }
30639         // Check for the TIFF tag marker (0x002A):
30640         if (dataView.getUint16(tiffOffset + 2, littleEndian) !== 0x002A) {
30641             Roo.log('Invalid Exif data: Missing TIFF marker.');
30642             return;
30643         }
30644         // Retrieve the directory offset bytes, usually 0x00000008 or 8 decimal:
30645         dirOffset = dataView.getUint32(tiffOffset + 4, littleEndian);
30646         
30647         this.parseExifTags(
30648             dataView,
30649             tiffOffset,
30650             tiffOffset + dirOffset,
30651             littleEndian
30652         );
30653     },
30654     
30655     parseExifTags : function(dataView, tiffOffset, dirOffset, littleEndian)
30656     {
30657         var tagsNumber,
30658             dirEndOffset,
30659             i;
30660         if (dirOffset + 6 > dataView.byteLength) {
30661             Roo.log('Invalid Exif data: Invalid directory offset.');
30662             return;
30663         }
30664         tagsNumber = dataView.getUint16(dirOffset, littleEndian);
30665         dirEndOffset = dirOffset + 2 + 12 * tagsNumber;
30666         if (dirEndOffset + 4 > dataView.byteLength) {
30667             Roo.log('Invalid Exif data: Invalid directory size.');
30668             return;
30669         }
30670         for (i = 0; i < tagsNumber; i += 1) {
30671             this.parseExifTag(
30672                 dataView,
30673                 tiffOffset,
30674                 dirOffset + 2 + 12 * i, // tag offset
30675                 littleEndian
30676             );
30677         }
30678         // Return the offset to the next directory:
30679         return dataView.getUint32(dirEndOffset, littleEndian);
30680     },
30681     
30682     parseExifTag : function (dataView, tiffOffset, offset, littleEndian) 
30683     {
30684         var tag = dataView.getUint16(offset, littleEndian);
30685         
30686         this.exif[tag] = this.getExifValue(
30687             dataView,
30688             tiffOffset,
30689             offset,
30690             dataView.getUint16(offset + 2, littleEndian), // tag type
30691             dataView.getUint32(offset + 4, littleEndian), // tag length
30692             littleEndian
30693         );
30694     },
30695     
30696     getExifValue : function (dataView, tiffOffset, offset, type, length, littleEndian)
30697     {
30698         var tagType = Roo.bootstrap.UploadCropbox.exifTagTypes[type],
30699             tagSize,
30700             dataOffset,
30701             values,
30702             i,
30703             str,
30704             c;
30705     
30706         if (!tagType) {
30707             Roo.log('Invalid Exif data: Invalid tag type.');
30708             return;
30709         }
30710         
30711         tagSize = tagType.size * length;
30712         // Determine if the value is contained in the dataOffset bytes,
30713         // or if the value at the dataOffset is a pointer to the actual data:
30714         dataOffset = tagSize > 4 ?
30715                 tiffOffset + dataView.getUint32(offset + 8, littleEndian) : (offset + 8);
30716         if (dataOffset + tagSize > dataView.byteLength) {
30717             Roo.log('Invalid Exif data: Invalid data offset.');
30718             return;
30719         }
30720         if (length === 1) {
30721             return tagType.getValue(dataView, dataOffset, littleEndian);
30722         }
30723         values = [];
30724         for (i = 0; i < length; i += 1) {
30725             values[i] = tagType.getValue(dataView, dataOffset + i * tagType.size, littleEndian);
30726         }
30727         
30728         if (tagType.ascii) {
30729             str = '';
30730             // Concatenate the chars:
30731             for (i = 0; i < values.length; i += 1) {
30732                 c = values[i];
30733                 // Ignore the terminating NULL byte(s):
30734                 if (c === '\u0000') {
30735                     break;
30736                 }
30737                 str += c;
30738             }
30739             return str;
30740         }
30741         return values;
30742     }
30743     
30744 });
30745
30746 Roo.apply(Roo.bootstrap.UploadCropbox, {
30747     tags : {
30748         'Orientation': 0x0112
30749     },
30750     
30751     Orientation: {
30752             1: 0, //'top-left',
30753 //            2: 'top-right',
30754             3: 180, //'bottom-right',
30755 //            4: 'bottom-left',
30756 //            5: 'left-top',
30757             6: 90, //'right-top',
30758 //            7: 'right-bottom',
30759             8: 270 //'left-bottom'
30760     },
30761     
30762     exifTagTypes : {
30763         // byte, 8-bit unsigned int:
30764         1: {
30765             getValue: function (dataView, dataOffset) {
30766                 return dataView.getUint8(dataOffset);
30767             },
30768             size: 1
30769         },
30770         // ascii, 8-bit byte:
30771         2: {
30772             getValue: function (dataView, dataOffset) {
30773                 return String.fromCharCode(dataView.getUint8(dataOffset));
30774             },
30775             size: 1,
30776             ascii: true
30777         },
30778         // short, 16 bit int:
30779         3: {
30780             getValue: function (dataView, dataOffset, littleEndian) {
30781                 return dataView.getUint16(dataOffset, littleEndian);
30782             },
30783             size: 2
30784         },
30785         // long, 32 bit int:
30786         4: {
30787             getValue: function (dataView, dataOffset, littleEndian) {
30788                 return dataView.getUint32(dataOffset, littleEndian);
30789             },
30790             size: 4
30791         },
30792         // rational = two long values, first is numerator, second is denominator:
30793         5: {
30794             getValue: function (dataView, dataOffset, littleEndian) {
30795                 return dataView.getUint32(dataOffset, littleEndian) /
30796                     dataView.getUint32(dataOffset + 4, littleEndian);
30797             },
30798             size: 8
30799         },
30800         // slong, 32 bit signed int:
30801         9: {
30802             getValue: function (dataView, dataOffset, littleEndian) {
30803                 return dataView.getInt32(dataOffset, littleEndian);
30804             },
30805             size: 4
30806         },
30807         // srational, two slongs, first is numerator, second is denominator:
30808         10: {
30809             getValue: function (dataView, dataOffset, littleEndian) {
30810                 return dataView.getInt32(dataOffset, littleEndian) /
30811                     dataView.getInt32(dataOffset + 4, littleEndian);
30812             },
30813             size: 8
30814         }
30815     },
30816     
30817     footer : {
30818         STANDARD : [
30819             {
30820                 tag : 'div',
30821                 cls : 'btn-group roo-upload-cropbox-rotate-left',
30822                 action : 'rotate-left',
30823                 cn : [
30824                     {
30825                         tag : 'button',
30826                         cls : 'btn btn-default',
30827                         html : '<i class="fa fa-undo"></i>'
30828                     }
30829                 ]
30830             },
30831             {
30832                 tag : 'div',
30833                 cls : 'btn-group roo-upload-cropbox-picture',
30834                 action : 'picture',
30835                 cn : [
30836                     {
30837                         tag : 'button',
30838                         cls : 'btn btn-default',
30839                         html : '<i class="fa fa-picture-o"></i>'
30840                     }
30841                 ]
30842             },
30843             {
30844                 tag : 'div',
30845                 cls : 'btn-group roo-upload-cropbox-rotate-right',
30846                 action : 'rotate-right',
30847                 cn : [
30848                     {
30849                         tag : 'button',
30850                         cls : 'btn btn-default',
30851                         html : '<i class="fa fa-repeat"></i>'
30852                     }
30853                 ]
30854             }
30855         ],
30856         DOCUMENT : [
30857             {
30858                 tag : 'div',
30859                 cls : 'btn-group roo-upload-cropbox-rotate-left',
30860                 action : 'rotate-left',
30861                 cn : [
30862                     {
30863                         tag : 'button',
30864                         cls : 'btn btn-default',
30865                         html : '<i class="fa fa-undo"></i>'
30866                     }
30867                 ]
30868             },
30869             {
30870                 tag : 'div',
30871                 cls : 'btn-group roo-upload-cropbox-download',
30872                 action : 'download',
30873                 cn : [
30874                     {
30875                         tag : 'button',
30876                         cls : 'btn btn-default',
30877                         html : '<i class="fa fa-download"></i>'
30878                     }
30879                 ]
30880             },
30881             {
30882                 tag : 'div',
30883                 cls : 'btn-group roo-upload-cropbox-crop',
30884                 action : 'crop',
30885                 cn : [
30886                     {
30887                         tag : 'button',
30888                         cls : 'btn btn-default',
30889                         html : '<i class="fa fa-crop"></i>'
30890                     }
30891                 ]
30892             },
30893             {
30894                 tag : 'div',
30895                 cls : 'btn-group roo-upload-cropbox-trash',
30896                 action : 'trash',
30897                 cn : [
30898                     {
30899                         tag : 'button',
30900                         cls : 'btn btn-default',
30901                         html : '<i class="fa fa-trash"></i>'
30902                     }
30903                 ]
30904             },
30905             {
30906                 tag : 'div',
30907                 cls : 'btn-group roo-upload-cropbox-rotate-right',
30908                 action : 'rotate-right',
30909                 cn : [
30910                     {
30911                         tag : 'button',
30912                         cls : 'btn btn-default',
30913                         html : '<i class="fa fa-repeat"></i>'
30914                     }
30915                 ]
30916             }
30917         ],
30918         ROTATOR : [
30919             {
30920                 tag : 'div',
30921                 cls : 'btn-group roo-upload-cropbox-rotate-left',
30922                 action : 'rotate-left',
30923                 cn : [
30924                     {
30925                         tag : 'button',
30926                         cls : 'btn btn-default',
30927                         html : '<i class="fa fa-undo"></i>'
30928                     }
30929                 ]
30930             },
30931             {
30932                 tag : 'div',
30933                 cls : 'btn-group roo-upload-cropbox-rotate-right',
30934                 action : 'rotate-right',
30935                 cn : [
30936                     {
30937                         tag : 'button',
30938                         cls : 'btn btn-default',
30939                         html : '<i class="fa fa-repeat"></i>'
30940                     }
30941                 ]
30942             }
30943         ]
30944     }
30945 });
30946
30947 /*
30948 * Licence: LGPL
30949 */
30950
30951 /**
30952  * @class Roo.bootstrap.DocumentManager
30953  * @extends Roo.bootstrap.Component
30954  * Bootstrap DocumentManager class
30955  * @cfg {String} paramName default 'imageUpload'
30956  * @cfg {String} toolTipName default 'filename'
30957  * @cfg {String} method default POST
30958  * @cfg {String} url action url
30959  * @cfg {Number} boxes number of boxes, 0 is no limit.. default 0
30960  * @cfg {Boolean} multiple multiple upload default true
30961  * @cfg {Number} thumbSize default 300
30962  * @cfg {String} fieldLabel
30963  * @cfg {Number} labelWidth default 4
30964  * @cfg {String} labelAlign (left|top) default left
30965  * @cfg {Boolean} editable (true|false) allow edit when upload a image default true
30966 * @cfg {Number} labellg set the width of label (1-12)
30967  * @cfg {Number} labelmd set the width of label (1-12)
30968  * @cfg {Number} labelsm set the width of label (1-12)
30969  * @cfg {Number} labelxs set the width of label (1-12)
30970  * 
30971  * @constructor
30972  * Create a new DocumentManager
30973  * @param {Object} config The config object
30974  */
30975
30976 Roo.bootstrap.DocumentManager = function(config){
30977     Roo.bootstrap.DocumentManager.superclass.constructor.call(this, config);
30978     
30979     this.files = [];
30980     this.delegates = [];
30981     
30982     this.addEvents({
30983         /**
30984          * @event initial
30985          * Fire when initial the DocumentManager
30986          * @param {Roo.bootstrap.DocumentManager} this
30987          */
30988         "initial" : true,
30989         /**
30990          * @event inspect
30991          * inspect selected file
30992          * @param {Roo.bootstrap.DocumentManager} this
30993          * @param {File} file
30994          */
30995         "inspect" : true,
30996         /**
30997          * @event exception
30998          * Fire when xhr load exception
30999          * @param {Roo.bootstrap.DocumentManager} this
31000          * @param {XMLHttpRequest} xhr
31001          */
31002         "exception" : true,
31003         /**
31004          * @event afterupload
31005          * Fire when xhr load exception
31006          * @param {Roo.bootstrap.DocumentManager} this
31007          * @param {XMLHttpRequest} xhr
31008          */
31009         "afterupload" : true,
31010         /**
31011          * @event prepare
31012          * prepare the form data
31013          * @param {Roo.bootstrap.DocumentManager} this
31014          * @param {Object} formData
31015          */
31016         "prepare" : true,
31017         /**
31018          * @event remove
31019          * Fire when remove the file
31020          * @param {Roo.bootstrap.DocumentManager} this
31021          * @param {Object} file
31022          */
31023         "remove" : true,
31024         /**
31025          * @event refresh
31026          * Fire after refresh the file
31027          * @param {Roo.bootstrap.DocumentManager} this
31028          */
31029         "refresh" : true,
31030         /**
31031          * @event click
31032          * Fire after click the image
31033          * @param {Roo.bootstrap.DocumentManager} this
31034          * @param {Object} file
31035          */
31036         "click" : true,
31037         /**
31038          * @event edit
31039          * Fire when upload a image and editable set to true
31040          * @param {Roo.bootstrap.DocumentManager} this
31041          * @param {Object} file
31042          */
31043         "edit" : true,
31044         /**
31045          * @event beforeselectfile
31046          * Fire before select file
31047          * @param {Roo.bootstrap.DocumentManager} this
31048          */
31049         "beforeselectfile" : true,
31050         /**
31051          * @event process
31052          * Fire before process file
31053          * @param {Roo.bootstrap.DocumentManager} this
31054          * @param {Object} file
31055          */
31056         "process" : true,
31057         /**
31058          * @event previewrendered
31059          * Fire when preview rendered
31060          * @param {Roo.bootstrap.DocumentManager} this
31061          * @param {Object} file
31062          */
31063         "previewrendered" : true,
31064         /**
31065          */
31066         "previewResize" : true
31067         
31068     });
31069 };
31070
31071 Roo.extend(Roo.bootstrap.DocumentManager, Roo.bootstrap.Component,  {
31072     
31073     boxes : 0,
31074     inputName : '',
31075     thumbSize : 300,
31076     multiple : true,
31077     files : false,
31078     method : 'POST',
31079     url : '',
31080     paramName : 'imageUpload',
31081     toolTipName : 'filename',
31082     fieldLabel : '',
31083     labelWidth : 4,
31084     labelAlign : 'left',
31085     editable : true,
31086     delegates : false,
31087     xhr : false, 
31088     
31089     labellg : 0,
31090     labelmd : 0,
31091     labelsm : 0,
31092     labelxs : 0,
31093     
31094     getAutoCreate : function()
31095     {   
31096         var managerWidget = {
31097             tag : 'div',
31098             cls : 'roo-document-manager',
31099             cn : [
31100                 {
31101                     tag : 'input',
31102                     cls : 'roo-document-manager-selector',
31103                     type : 'file'
31104                 },
31105                 {
31106                     tag : 'div',
31107                     cls : 'roo-document-manager-uploader',
31108                     cn : [
31109                         {
31110                             tag : 'div',
31111                             cls : 'roo-document-manager-upload-btn',
31112                             html : '<i class="fa fa-plus"></i>'
31113                         }
31114                     ]
31115                     
31116                 }
31117             ]
31118         };
31119         
31120         var content = [
31121             {
31122                 tag : 'div',
31123                 cls : 'column col-md-12',
31124                 cn : managerWidget
31125             }
31126         ];
31127         
31128         if(this.fieldLabel.length){
31129             
31130             content = [
31131                 {
31132                     tag : 'div',
31133                     cls : 'column col-md-12',
31134                     html : this.fieldLabel
31135                 },
31136                 {
31137                     tag : 'div',
31138                     cls : 'column col-md-12',
31139                     cn : managerWidget
31140                 }
31141             ];
31142
31143             if(this.labelAlign == 'left'){
31144                 content = [
31145                     {
31146                         tag : 'div',
31147                         cls : 'column',
31148                         html : this.fieldLabel
31149                     },
31150                     {
31151                         tag : 'div',
31152                         cls : 'column',
31153                         cn : managerWidget
31154                     }
31155                 ];
31156                 
31157                 if(this.labelWidth > 12){
31158                     content[0].style = "width: " + this.labelWidth + 'px';
31159                 }
31160
31161                 if(this.labelWidth < 13 && this.labelmd == 0){
31162                     this.labelmd = this.labelWidth;
31163                 }
31164
31165                 if(this.labellg > 0){
31166                     content[0].cls += ' col-lg-' + this.labellg;
31167                     content[1].cls += ' col-lg-' + (12 - this.labellg);
31168                 }
31169
31170                 if(this.labelmd > 0){
31171                     content[0].cls += ' col-md-' + this.labelmd;
31172                     content[1].cls += ' col-md-' + (12 - this.labelmd);
31173                 }
31174
31175                 if(this.labelsm > 0){
31176                     content[0].cls += ' col-sm-' + this.labelsm;
31177                     content[1].cls += ' col-sm-' + (12 - this.labelsm);
31178                 }
31179
31180                 if(this.labelxs > 0){
31181                     content[0].cls += ' col-xs-' + this.labelxs;
31182                     content[1].cls += ' col-xs-' + (12 - this.labelxs);
31183                 }
31184                 
31185             }
31186         }
31187         
31188         var cfg = {
31189             tag : 'div',
31190             cls : 'row clearfix',
31191             cn : content
31192         };
31193         
31194         return cfg;
31195         
31196     },
31197     
31198     initEvents : function()
31199     {
31200         this.managerEl = this.el.select('.roo-document-manager', true).first();
31201         this.managerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
31202         
31203         this.selectorEl = this.el.select('.roo-document-manager-selector', true).first();
31204         this.selectorEl.hide();
31205         
31206         if(this.multiple){
31207             this.selectorEl.attr('multiple', 'multiple');
31208         }
31209         
31210         this.selectorEl.on('change', this.onFileSelected, this);
31211         
31212         this.uploader = this.el.select('.roo-document-manager-uploader', true).first();
31213         this.uploader.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
31214         
31215         this.uploader.on('click', this.onUploaderClick, this);
31216         
31217         this.renderProgressDialog();
31218         
31219         var _this = this;
31220         
31221         window.addEventListener("resize", function() { _this.refresh(); } );
31222         
31223         this.fireEvent('initial', this);
31224     },
31225     
31226     renderProgressDialog : function()
31227     {
31228         var _this = this;
31229         
31230         this.progressDialog = new Roo.bootstrap.Modal({
31231             cls : 'roo-document-manager-progress-dialog',
31232             allow_close : false,
31233             animate : false,
31234             title : '',
31235             buttons : [
31236                 {
31237                     name  :'cancel',
31238                     weight : 'danger',
31239                     html : 'Cancel'
31240                 }
31241             ], 
31242             listeners : { 
31243                 btnclick : function() {
31244                     _this.uploadCancel();
31245                     this.hide();
31246                 }
31247             }
31248         });
31249          
31250         this.progressDialog.render(Roo.get(document.body));
31251          
31252         this.progress = new Roo.bootstrap.Progress({
31253             cls : 'roo-document-manager-progress',
31254             active : true,
31255             striped : true
31256         });
31257         
31258         this.progress.render(this.progressDialog.getChildContainer());
31259         
31260         this.progressBar = new Roo.bootstrap.ProgressBar({
31261             cls : 'roo-document-manager-progress-bar',
31262             aria_valuenow : 0,
31263             aria_valuemin : 0,
31264             aria_valuemax : 12,
31265             panel : 'success'
31266         });
31267         
31268         this.progressBar.render(this.progress.getChildContainer());
31269     },
31270     
31271     onUploaderClick : function(e)
31272     {
31273         e.preventDefault();
31274      
31275         if(this.fireEvent('beforeselectfile', this) != false){
31276             this.selectorEl.dom.click();
31277         }
31278         
31279     },
31280     
31281     onFileSelected : function(e)
31282     {
31283         e.preventDefault();
31284         
31285         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
31286             return;
31287         }
31288         
31289         Roo.each(this.selectorEl.dom.files, function(file){
31290             if(this.fireEvent('inspect', this, file) != false){
31291                 this.files.push(file);
31292             }
31293         }, this);
31294         
31295         this.queue();
31296         
31297     },
31298     
31299     queue : function()
31300     {
31301         this.selectorEl.dom.value = '';
31302         
31303         if(!this.files || !this.files.length){
31304             return;
31305         }
31306         
31307         if(this.boxes > 0 && this.files.length > this.boxes){
31308             this.files = this.files.slice(0, this.boxes);
31309         }
31310         
31311         this.uploader.show();
31312         
31313         if(this.boxes > 0 && this.files.length > this.boxes - 1){
31314             this.uploader.hide();
31315         }
31316         
31317         var _this = this;
31318         
31319         var files = [];
31320         
31321         var docs = [];
31322         
31323         Roo.each(this.files, function(file){
31324             
31325             if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
31326                 var f = this.renderPreview(file);
31327                 files.push(f);
31328                 return;
31329             }
31330             
31331             if(file.type.indexOf('image') != -1){
31332                 this.delegates.push(
31333                     (function(){
31334                         _this.process(file);
31335                     }).createDelegate(this)
31336                 );
31337         
31338                 return;
31339             }
31340             
31341             docs.push(
31342                 (function(){
31343                     _this.process(file);
31344                 }).createDelegate(this)
31345             );
31346             
31347         }, this);
31348         
31349         this.files = files;
31350         
31351         this.delegates = this.delegates.concat(docs);
31352         
31353         if(!this.delegates.length){
31354             this.refresh();
31355             return;
31356         }
31357         
31358         this.progressBar.aria_valuemax = this.delegates.length;
31359         
31360         this.arrange();
31361         
31362         return;
31363     },
31364     
31365     arrange : function()
31366     {
31367         if(!this.delegates.length){
31368             this.progressDialog.hide();
31369             this.refresh();
31370             return;
31371         }
31372         
31373         var delegate = this.delegates.shift();
31374         
31375         this.progressDialog.show();
31376         
31377         this.progressDialog.setTitle((this.progressBar.aria_valuemax - this.delegates.length) + ' / ' + this.progressBar.aria_valuemax);
31378         
31379         this.progressBar.update(this.progressBar.aria_valuemax - this.delegates.length);
31380         
31381         delegate();
31382     },
31383     
31384     refresh : function()
31385     {
31386         this.uploader.show();
31387         
31388         if(this.boxes > 0 && this.files.length > this.boxes - 1){
31389             this.uploader.hide();
31390         }
31391         
31392         Roo.isTouch ? this.closable(false) : this.closable(true);
31393         
31394         this.fireEvent('refresh', this);
31395     },
31396     
31397     onRemove : function(e, el, o)
31398     {
31399         e.preventDefault();
31400         
31401         this.fireEvent('remove', this, o);
31402         
31403     },
31404     
31405     remove : function(o)
31406     {
31407         var files = [];
31408         
31409         Roo.each(this.files, function(file){
31410             if(typeof(file.id) == 'undefined' || file.id * 1 < 1 || file.id != o.id){
31411                 files.push(file);
31412                 return;
31413             }
31414
31415             o.target.remove();
31416
31417         }, this);
31418         
31419         this.files = files;
31420         
31421         this.refresh();
31422     },
31423     
31424     clear : function()
31425     {
31426         Roo.each(this.files, function(file){
31427             if(!file.target){
31428                 return;
31429             }
31430             
31431             file.target.remove();
31432
31433         }, this);
31434         
31435         this.files = [];
31436         
31437         this.refresh();
31438     },
31439     
31440     onClick : function(e, el, o)
31441     {
31442         e.preventDefault();
31443         
31444         this.fireEvent('click', this, o);
31445         
31446     },
31447     
31448     closable : function(closable)
31449     {
31450         Roo.each(this.managerEl.select('.roo-document-manager-preview > button.close', true).elements, function(el){
31451             
31452             el.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
31453             
31454             if(closable){
31455                 el.show();
31456                 return;
31457             }
31458             
31459             el.hide();
31460             
31461         }, this);
31462     },
31463     
31464     xhrOnLoad : function(xhr)
31465     {
31466         Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
31467             el.remove();
31468         }, this);
31469         
31470         if (xhr.readyState !== 4) {
31471             this.arrange();
31472             this.fireEvent('exception', this, xhr);
31473             return;
31474         }
31475
31476         var response = Roo.decode(xhr.responseText);
31477         
31478         if(!response.success){
31479             this.arrange();
31480             this.fireEvent('exception', this, xhr);
31481             return;
31482         }
31483         
31484         var file = this.renderPreview(response.data);
31485         
31486         this.files.push(file);
31487         
31488         this.arrange();
31489         
31490         this.fireEvent('afterupload', this, xhr);
31491         
31492     },
31493     
31494     xhrOnError : function(xhr)
31495     {
31496         Roo.log('xhr on error');
31497         
31498         var response = Roo.decode(xhr.responseText);
31499           
31500         Roo.log(response);
31501         
31502         this.arrange();
31503     },
31504     
31505     process : function(file)
31506     {
31507         if(this.fireEvent('process', this, file) !== false){
31508             if(this.editable && file.type.indexOf('image') != -1){
31509                 this.fireEvent('edit', this, file);
31510                 return;
31511             }
31512
31513             this.uploadStart(file, false);
31514
31515             return;
31516         }
31517         
31518     },
31519     
31520     uploadStart : function(file, crop)
31521     {
31522         this.xhr = new XMLHttpRequest();
31523         
31524         if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
31525             this.arrange();
31526             return;
31527         }
31528         
31529         file.xhr = this.xhr;
31530             
31531         this.managerEl.createChild({
31532             tag : 'div',
31533             cls : 'roo-document-manager-loading',
31534             cn : [
31535                 {
31536                     tag : 'div',
31537                     tooltip : file.name,
31538                     cls : 'roo-document-manager-thumb',
31539                     html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
31540                 }
31541             ]
31542
31543         });
31544
31545         this.xhr.open(this.method, this.url, true);
31546         
31547         var headers = {
31548             "Accept": "application/json",
31549             "Cache-Control": "no-cache",
31550             "X-Requested-With": "XMLHttpRequest"
31551         };
31552         
31553         for (var headerName in headers) {
31554             var headerValue = headers[headerName];
31555             if (headerValue) {
31556                 this.xhr.setRequestHeader(headerName, headerValue);
31557             }
31558         }
31559         
31560         var _this = this;
31561         
31562         this.xhr.onload = function()
31563         {
31564             _this.xhrOnLoad(_this.xhr);
31565         }
31566         
31567         this.xhr.onerror = function()
31568         {
31569             _this.xhrOnError(_this.xhr);
31570         }
31571         
31572         var formData = new FormData();
31573
31574         formData.append('returnHTML', 'NO');
31575         
31576         if(crop){
31577             formData.append('crop', crop);
31578         }
31579         
31580         formData.append(this.paramName, file, file.name);
31581         
31582         var options = {
31583             file : file, 
31584             manually : false
31585         };
31586         
31587         if(this.fireEvent('prepare', this, formData, options) != false){
31588             
31589             if(options.manually){
31590                 return;
31591             }
31592             
31593             this.xhr.send(formData);
31594             return;
31595         };
31596         
31597         this.uploadCancel();
31598     },
31599     
31600     uploadCancel : function()
31601     {
31602         if (this.xhr) {
31603             this.xhr.abort();
31604         }
31605         
31606         this.delegates = [];
31607         
31608         Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
31609             el.remove();
31610         }, this);
31611         
31612         this.arrange();
31613     },
31614     
31615     renderPreview : function(file)
31616     {
31617         if(typeof(file.target) != 'undefined' && file.target){
31618             return file;
31619         }
31620         
31621         var img_src = encodeURI(baseURL +'/Images/Thumb/' + this.thumbSize + '/' + file.id + '/' + file.filename);
31622         
31623         var previewEl = this.managerEl.createChild({
31624             tag : 'div',
31625             cls : 'roo-document-manager-preview',
31626             cn : [
31627                 {
31628                     tag : 'div',
31629                     tooltip : file[this.toolTipName],
31630                     cls : 'roo-document-manager-thumb',
31631                     html : '<img tooltip="' + file[this.toolTipName] + '" src="' + img_src + '">'
31632                 },
31633                 {
31634                     tag : 'button',
31635                     cls : 'close',
31636                     html : '<i class="fa fa-times-circle"></i>'
31637                 }
31638             ]
31639         });
31640
31641         var close = previewEl.select('button.close', true).first();
31642
31643         close.on('click', this.onRemove, this, file);
31644
31645         file.target = previewEl;
31646
31647         var image = previewEl.select('img', true).first();
31648         
31649         var _this = this;
31650         
31651         image.dom.addEventListener("load", function(){ _this.onPreviewLoad(file, image); });
31652         
31653         image.on('click', this.onClick, this, file);
31654         
31655         this.fireEvent('previewrendered', this, file);
31656         
31657         return file;
31658         
31659     },
31660     
31661     onPreviewLoad : function(file, image)
31662     {
31663         if(typeof(file.target) == 'undefined' || !file.target){
31664             return;
31665         }
31666         
31667         var width = image.dom.naturalWidth || image.dom.width;
31668         var height = image.dom.naturalHeight || image.dom.height;
31669         
31670         if(!this.previewResize) {
31671             return;
31672         }
31673         
31674         if(width > height){
31675             file.target.addClass('wide');
31676             return;
31677         }
31678         
31679         file.target.addClass('tall');
31680         return;
31681         
31682     },
31683     
31684     uploadFromSource : function(file, crop)
31685     {
31686         this.xhr = new XMLHttpRequest();
31687         
31688         this.managerEl.createChild({
31689             tag : 'div',
31690             cls : 'roo-document-manager-loading',
31691             cn : [
31692                 {
31693                     tag : 'div',
31694                     tooltip : file.name,
31695                     cls : 'roo-document-manager-thumb',
31696                     html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
31697                 }
31698             ]
31699
31700         });
31701
31702         this.xhr.open(this.method, this.url, true);
31703         
31704         var headers = {
31705             "Accept": "application/json",
31706             "Cache-Control": "no-cache",
31707             "X-Requested-With": "XMLHttpRequest"
31708         };
31709         
31710         for (var headerName in headers) {
31711             var headerValue = headers[headerName];
31712             if (headerValue) {
31713                 this.xhr.setRequestHeader(headerName, headerValue);
31714             }
31715         }
31716         
31717         var _this = this;
31718         
31719         this.xhr.onload = function()
31720         {
31721             _this.xhrOnLoad(_this.xhr);
31722         }
31723         
31724         this.xhr.onerror = function()
31725         {
31726             _this.xhrOnError(_this.xhr);
31727         }
31728         
31729         var formData = new FormData();
31730
31731         formData.append('returnHTML', 'NO');
31732         
31733         formData.append('crop', crop);
31734         
31735         if(typeof(file.filename) != 'undefined'){
31736             formData.append('filename', file.filename);
31737         }
31738         
31739         if(typeof(file.mimetype) != 'undefined'){
31740             formData.append('mimetype', file.mimetype);
31741         }
31742         
31743         Roo.log(formData);
31744         
31745         if(this.fireEvent('prepare', this, formData) != false){
31746             this.xhr.send(formData);
31747         };
31748     }
31749 });
31750
31751 /*
31752 * Licence: LGPL
31753 */
31754
31755 /**
31756  * @class Roo.bootstrap.DocumentViewer
31757  * @extends Roo.bootstrap.Component
31758  * Bootstrap DocumentViewer class
31759  * @cfg {Boolean} showDownload (true|false) show download button (default true)
31760  * @cfg {Boolean} showTrash (true|false) show trash button (default true)
31761  * 
31762  * @constructor
31763  * Create a new DocumentViewer
31764  * @param {Object} config The config object
31765  */
31766
31767 Roo.bootstrap.DocumentViewer = function(config){
31768     Roo.bootstrap.DocumentViewer.superclass.constructor.call(this, config);
31769     
31770     this.addEvents({
31771         /**
31772          * @event initial
31773          * Fire after initEvent
31774          * @param {Roo.bootstrap.DocumentViewer} this
31775          */
31776         "initial" : true,
31777         /**
31778          * @event click
31779          * Fire after click
31780          * @param {Roo.bootstrap.DocumentViewer} this
31781          */
31782         "click" : true,
31783         /**
31784          * @event download
31785          * Fire after download button
31786          * @param {Roo.bootstrap.DocumentViewer} this
31787          */
31788         "download" : true,
31789         /**
31790          * @event trash
31791          * Fire after trash button
31792          * @param {Roo.bootstrap.DocumentViewer} this
31793          */
31794         "trash" : true
31795         
31796     });
31797 };
31798
31799 Roo.extend(Roo.bootstrap.DocumentViewer, Roo.bootstrap.Component,  {
31800     
31801     showDownload : true,
31802     
31803     showTrash : true,
31804     
31805     getAutoCreate : function()
31806     {
31807         var cfg = {
31808             tag : 'div',
31809             cls : 'roo-document-viewer',
31810             cn : [
31811                 {
31812                     tag : 'div',
31813                     cls : 'roo-document-viewer-body',
31814                     cn : [
31815                         {
31816                             tag : 'div',
31817                             cls : 'roo-document-viewer-thumb',
31818                             cn : [
31819                                 {
31820                                     tag : 'img',
31821                                     cls : 'roo-document-viewer-image'
31822                                 }
31823                             ]
31824                         }
31825                     ]
31826                 },
31827                 {
31828                     tag : 'div',
31829                     cls : 'roo-document-viewer-footer',
31830                     cn : {
31831                         tag : 'div',
31832                         cls : 'btn-group btn-group-justified roo-document-viewer-btn-group',
31833                         cn : [
31834                             {
31835                                 tag : 'div',
31836                                 cls : 'btn-group roo-document-viewer-download',
31837                                 cn : [
31838                                     {
31839                                         tag : 'button',
31840                                         cls : 'btn btn-default',
31841                                         html : '<i class="fa fa-download"></i>'
31842                                     }
31843                                 ]
31844                             },
31845                             {
31846                                 tag : 'div',
31847                                 cls : 'btn-group roo-document-viewer-trash',
31848                                 cn : [
31849                                     {
31850                                         tag : 'button',
31851                                         cls : 'btn btn-default',
31852                                         html : '<i class="fa fa-trash"></i>'
31853                                     }
31854                                 ]
31855                             }
31856                         ]
31857                     }
31858                 }
31859             ]
31860         };
31861         
31862         return cfg;
31863     },
31864     
31865     initEvents : function()
31866     {
31867         this.bodyEl = this.el.select('.roo-document-viewer-body', true).first();
31868         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
31869         
31870         this.thumbEl = this.el.select('.roo-document-viewer-thumb', true).first();
31871         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
31872         
31873         this.imageEl = this.el.select('.roo-document-viewer-image', true).first();
31874         this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
31875         
31876         this.footerEl = this.el.select('.roo-document-viewer-footer', true).first();
31877         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY);
31878         
31879         this.downloadBtn = this.el.select('.roo-document-viewer-download', true).first();
31880         this.downloadBtn.setVisibilityMode(Roo.Element.DISPLAY);
31881         
31882         this.trashBtn = this.el.select('.roo-document-viewer-trash', true).first();
31883         this.trashBtn.setVisibilityMode(Roo.Element.DISPLAY);
31884         
31885         this.bodyEl.on('click', this.onClick, this);
31886         this.downloadBtn.on('click', this.onDownload, this);
31887         this.trashBtn.on('click', this.onTrash, this);
31888         
31889         this.downloadBtn.hide();
31890         this.trashBtn.hide();
31891         
31892         if(this.showDownload){
31893             this.downloadBtn.show();
31894         }
31895         
31896         if(this.showTrash){
31897             this.trashBtn.show();
31898         }
31899         
31900         if(!this.showDownload && !this.showTrash) {
31901             this.footerEl.hide();
31902         }
31903         
31904     },
31905     
31906     initial : function()
31907     {
31908         this.fireEvent('initial', this);
31909         
31910     },
31911     
31912     onClick : function(e)
31913     {
31914         e.preventDefault();
31915         
31916         this.fireEvent('click', this);
31917     },
31918     
31919     onDownload : function(e)
31920     {
31921         e.preventDefault();
31922         
31923         this.fireEvent('download', this);
31924     },
31925     
31926     onTrash : function(e)
31927     {
31928         e.preventDefault();
31929         
31930         this.fireEvent('trash', this);
31931     }
31932     
31933 });
31934 /*
31935  * - LGPL
31936  *
31937  * nav progress bar
31938  * 
31939  */
31940
31941 /**
31942  * @class Roo.bootstrap.NavProgressBar
31943  * @extends Roo.bootstrap.Component
31944  * Bootstrap NavProgressBar class
31945  * 
31946  * @constructor
31947  * Create a new nav progress bar
31948  * @param {Object} config The config object
31949  */
31950
31951 Roo.bootstrap.NavProgressBar = function(config){
31952     Roo.bootstrap.NavProgressBar.superclass.constructor.call(this, config);
31953
31954     this.bullets = this.bullets || [];
31955    
31956 //    Roo.bootstrap.NavProgressBar.register(this);
31957      this.addEvents({
31958         /**
31959              * @event changed
31960              * Fires when the active item changes
31961              * @param {Roo.bootstrap.NavProgressBar} this
31962              * @param {Roo.bootstrap.NavProgressItem} selected The item selected
31963              * @param {Roo.bootstrap.NavProgressItem} prev The previously selected item 
31964          */
31965         'changed': true
31966      });
31967     
31968 };
31969
31970 Roo.extend(Roo.bootstrap.NavProgressBar, Roo.bootstrap.Component,  {
31971     
31972     bullets : [],
31973     barItems : [],
31974     
31975     getAutoCreate : function()
31976     {
31977         var cfg = Roo.apply({}, Roo.bootstrap.NavProgressBar.superclass.getAutoCreate.call(this));
31978         
31979         cfg = {
31980             tag : 'div',
31981             cls : 'roo-navigation-bar-group',
31982             cn : [
31983                 {
31984                     tag : 'div',
31985                     cls : 'roo-navigation-top-bar'
31986                 },
31987                 {
31988                     tag : 'div',
31989                     cls : 'roo-navigation-bullets-bar',
31990                     cn : [
31991                         {
31992                             tag : 'ul',
31993                             cls : 'roo-navigation-bar'
31994                         }
31995                     ]
31996                 },
31997                 
31998                 {
31999                     tag : 'div',
32000                     cls : 'roo-navigation-bottom-bar'
32001                 }
32002             ]
32003             
32004         };
32005         
32006         return cfg;
32007         
32008     },
32009     
32010     initEvents: function() 
32011     {
32012         
32013     },
32014     
32015     onRender : function(ct, position) 
32016     {
32017         Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
32018         
32019         if(this.bullets.length){
32020             Roo.each(this.bullets, function(b){
32021                this.addItem(b);
32022             }, this);
32023         }
32024         
32025         this.format();
32026         
32027     },
32028     
32029     addItem : function(cfg)
32030     {
32031         var item = new Roo.bootstrap.NavProgressItem(cfg);
32032         
32033         item.parentId = this.id;
32034         item.render(this.el.select('.roo-navigation-bar', true).first(), null);
32035         
32036         if(cfg.html){
32037             var top = new Roo.bootstrap.Element({
32038                 tag : 'div',
32039                 cls : 'roo-navigation-bar-text'
32040             });
32041             
32042             var bottom = new Roo.bootstrap.Element({
32043                 tag : 'div',
32044                 cls : 'roo-navigation-bar-text'
32045             });
32046             
32047             top.onRender(this.el.select('.roo-navigation-top-bar', true).first(), null);
32048             bottom.onRender(this.el.select('.roo-navigation-bottom-bar', true).first(), null);
32049             
32050             var topText = new Roo.bootstrap.Element({
32051                 tag : 'span',
32052                 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? cfg.html : ''
32053             });
32054             
32055             var bottomText = new Roo.bootstrap.Element({
32056                 tag : 'span',
32057                 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? '' : cfg.html
32058             });
32059             
32060             topText.onRender(top.el, null);
32061             bottomText.onRender(bottom.el, null);
32062             
32063             item.topEl = top;
32064             item.bottomEl = bottom;
32065         }
32066         
32067         this.barItems.push(item);
32068         
32069         return item;
32070     },
32071     
32072     getActive : function()
32073     {
32074         var active = false;
32075         
32076         Roo.each(this.barItems, function(v){
32077             
32078             if (!v.isActive()) {
32079                 return;
32080             }
32081             
32082             active = v;
32083             return false;
32084             
32085         });
32086         
32087         return active;
32088     },
32089     
32090     setActiveItem : function(item)
32091     {
32092         var prev = false;
32093         
32094         Roo.each(this.barItems, function(v){
32095             if (v.rid == item.rid) {
32096                 return ;
32097             }
32098             
32099             if (v.isActive()) {
32100                 v.setActive(false);
32101                 prev = v;
32102             }
32103         });
32104
32105         item.setActive(true);
32106         
32107         this.fireEvent('changed', this, item, prev);
32108     },
32109     
32110     getBarItem: function(rid)
32111     {
32112         var ret = false;
32113         
32114         Roo.each(this.barItems, function(e) {
32115             if (e.rid != rid) {
32116                 return;
32117             }
32118             
32119             ret =  e;
32120             return false;
32121         });
32122         
32123         return ret;
32124     },
32125     
32126     indexOfItem : function(item)
32127     {
32128         var index = false;
32129         
32130         Roo.each(this.barItems, function(v, i){
32131             
32132             if (v.rid != item.rid) {
32133                 return;
32134             }
32135             
32136             index = i;
32137             return false
32138         });
32139         
32140         return index;
32141     },
32142     
32143     setActiveNext : function()
32144     {
32145         var i = this.indexOfItem(this.getActive());
32146         
32147         if (i > this.barItems.length) {
32148             return;
32149         }
32150         
32151         this.setActiveItem(this.barItems[i+1]);
32152     },
32153     
32154     setActivePrev : function()
32155     {
32156         var i = this.indexOfItem(this.getActive());
32157         
32158         if (i  < 1) {
32159             return;
32160         }
32161         
32162         this.setActiveItem(this.barItems[i-1]);
32163     },
32164     
32165     format : function()
32166     {
32167         if(!this.barItems.length){
32168             return;
32169         }
32170      
32171         var width = 100 / this.barItems.length;
32172         
32173         Roo.each(this.barItems, function(i){
32174             i.el.setStyle('width', width + '%');
32175             i.topEl.el.setStyle('width', width + '%');
32176             i.bottomEl.el.setStyle('width', width + '%');
32177         }, this);
32178         
32179     }
32180     
32181 });
32182 /*
32183  * - LGPL
32184  *
32185  * Nav Progress Item
32186  * 
32187  */
32188
32189 /**
32190  * @class Roo.bootstrap.NavProgressItem
32191  * @extends Roo.bootstrap.Component
32192  * Bootstrap NavProgressItem class
32193  * @cfg {String} rid the reference id
32194  * @cfg {Boolean} active (true|false) Is item active default false
32195  * @cfg {Boolean} disabled (true|false) Is item active default false
32196  * @cfg {String} html
32197  * @cfg {String} position (top|bottom) text position default bottom
32198  * @cfg {String} icon show icon instead of number
32199  * 
32200  * @constructor
32201  * Create a new NavProgressItem
32202  * @param {Object} config The config object
32203  */
32204 Roo.bootstrap.NavProgressItem = function(config){
32205     Roo.bootstrap.NavProgressItem.superclass.constructor.call(this, config);
32206     this.addEvents({
32207         // raw events
32208         /**
32209          * @event click
32210          * The raw click event for the entire grid.
32211          * @param {Roo.bootstrap.NavProgressItem} this
32212          * @param {Roo.EventObject} e
32213          */
32214         "click" : true
32215     });
32216    
32217 };
32218
32219 Roo.extend(Roo.bootstrap.NavProgressItem, Roo.bootstrap.Component,  {
32220     
32221     rid : '',
32222     active : false,
32223     disabled : false,
32224     html : '',
32225     position : 'bottom',
32226     icon : false,
32227     
32228     getAutoCreate : function()
32229     {
32230         var iconCls = 'roo-navigation-bar-item-icon';
32231         
32232         iconCls += ((this.icon) ? (' ' + this.icon) : (' step-number')) ;
32233         
32234         var cfg = {
32235             tag: 'li',
32236             cls: 'roo-navigation-bar-item',
32237             cn : [
32238                 {
32239                     tag : 'i',
32240                     cls : iconCls
32241                 }
32242             ]
32243         };
32244         
32245         if(this.active){
32246             cfg.cls += ' active';
32247         }
32248         if(this.disabled){
32249             cfg.cls += ' disabled';
32250         }
32251         
32252         return cfg;
32253     },
32254     
32255     disable : function()
32256     {
32257         this.setDisabled(true);
32258     },
32259     
32260     enable : function()
32261     {
32262         this.setDisabled(false);
32263     },
32264     
32265     initEvents: function() 
32266     {
32267         this.iconEl = this.el.select('.roo-navigation-bar-item-icon', true).first();
32268         
32269         this.iconEl.on('click', this.onClick, this);
32270     },
32271     
32272     onClick : function(e)
32273     {
32274         e.preventDefault();
32275         
32276         if(this.disabled){
32277             return;
32278         }
32279         
32280         if(this.fireEvent('click', this, e) === false){
32281             return;
32282         };
32283         
32284         this.parent().setActiveItem(this);
32285     },
32286     
32287     isActive: function () 
32288     {
32289         return this.active;
32290     },
32291     
32292     setActive : function(state)
32293     {
32294         if(this.active == state){
32295             return;
32296         }
32297         
32298         this.active = state;
32299         
32300         if (state) {
32301             this.el.addClass('active');
32302             return;
32303         }
32304         
32305         this.el.removeClass('active');
32306         
32307         return;
32308     },
32309     
32310     setDisabled : function(state)
32311     {
32312         if(this.disabled == state){
32313             return;
32314         }
32315         
32316         this.disabled = state;
32317         
32318         if (state) {
32319             this.el.addClass('disabled');
32320             return;
32321         }
32322         
32323         this.el.removeClass('disabled');
32324     },
32325     
32326     tooltipEl : function()
32327     {
32328         return this.el.select('.roo-navigation-bar-item-icon', true).first();;
32329     }
32330 });
32331  
32332
32333  /*
32334  * - LGPL
32335  *
32336  * FieldLabel
32337  * 
32338  */
32339
32340 /**
32341  * @class Roo.bootstrap.FieldLabel
32342  * @extends Roo.bootstrap.Component
32343  * Bootstrap FieldLabel class
32344  * @cfg {String} html contents of the element
32345  * @cfg {String} tag tag of the element default label
32346  * @cfg {String} cls class of the element
32347  * @cfg {String} target label target 
32348  * @cfg {Boolean} allowBlank (true|false) target allowBlank default true
32349  * @cfg {String} invalidClass DEPRICATED - BS4 uses is-invalid
32350  * @cfg {String} validClass DEPRICATED - BS4 uses is-valid
32351  * @cfg {String} iconTooltip default "This field is required"
32352  * @cfg {String} indicatorpos (left|right) default left
32353  * 
32354  * @constructor
32355  * Create a new FieldLabel
32356  * @param {Object} config The config object
32357  */
32358
32359 Roo.bootstrap.FieldLabel = function(config){
32360     Roo.bootstrap.Element.superclass.constructor.call(this, config);
32361     
32362     this.addEvents({
32363             /**
32364              * @event invalid
32365              * Fires after the field has been marked as invalid.
32366              * @param {Roo.form.FieldLabel} this
32367              * @param {String} msg The validation message
32368              */
32369             invalid : true,
32370             /**
32371              * @event valid
32372              * Fires after the field has been validated with no errors.
32373              * @param {Roo.form.FieldLabel} this
32374              */
32375             valid : true
32376         });
32377 };
32378
32379 Roo.extend(Roo.bootstrap.FieldLabel, Roo.bootstrap.Component,  {
32380     
32381     tag: 'label',
32382     cls: '',
32383     html: '',
32384     target: '',
32385     allowBlank : true,
32386     invalidClass : 'has-warning',
32387     validClass : 'has-success',
32388     iconTooltip : 'This field is required',
32389     indicatorpos : 'left',
32390     
32391     getAutoCreate : function(){
32392         
32393         var cls = "";
32394         if (!this.allowBlank) {
32395             cls  = "visible";
32396         }
32397         
32398         var cfg = {
32399             tag : this.tag,
32400             cls : 'roo-bootstrap-field-label ' + this.cls,
32401             for : this.target,
32402             cn : [
32403                 {
32404                     tag : 'i',
32405                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star ' + cls,
32406                     tooltip : this.iconTooltip
32407                 },
32408                 {
32409                     tag : 'span',
32410                     html : this.html
32411                 }
32412             ] 
32413         };
32414         
32415         if(this.indicatorpos == 'right'){
32416             var cfg = {
32417                 tag : this.tag,
32418                 cls : 'roo-bootstrap-field-label ' + this.cls,
32419                 for : this.target,
32420                 cn : [
32421                     {
32422                         tag : 'span',
32423                         html : this.html
32424                     },
32425                     {
32426                         tag : 'i',
32427                         cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star '+ cls,
32428                         tooltip : this.iconTooltip
32429                     }
32430                 ] 
32431             };
32432         }
32433         
32434         return cfg;
32435     },
32436     
32437     initEvents: function() 
32438     {
32439         Roo.bootstrap.Element.superclass.initEvents.call(this);
32440         
32441         this.indicator = this.indicatorEl();
32442         
32443         if(this.indicator){
32444             this.indicator.removeClass('visible');
32445             this.indicator.addClass('invisible');
32446         }
32447         
32448         Roo.bootstrap.FieldLabel.register(this);
32449     },
32450     
32451     indicatorEl : function()
32452     {
32453         var indicator = this.el.select('i.roo-required-indicator',true).first();
32454         
32455         if(!indicator){
32456             return false;
32457         }
32458         
32459         return indicator;
32460         
32461     },
32462     
32463     /**
32464      * Mark this field as valid
32465      */
32466     markValid : function()
32467     {
32468         if(this.indicator){
32469             this.indicator.removeClass('visible');
32470             this.indicator.addClass('invisible');
32471         }
32472         if (Roo.bootstrap.version == 3) {
32473             this.el.removeClass(this.invalidClass);
32474             this.el.addClass(this.validClass);
32475         } else {
32476             this.el.removeClass('is-invalid');
32477             this.el.addClass('is-valid');
32478         }
32479         
32480         
32481         this.fireEvent('valid', this);
32482     },
32483     
32484     /**
32485      * Mark this field as invalid
32486      * @param {String} msg The validation message
32487      */
32488     markInvalid : function(msg)
32489     {
32490         if(this.indicator){
32491             this.indicator.removeClass('invisible');
32492             this.indicator.addClass('visible');
32493         }
32494           if (Roo.bootstrap.version == 3) {
32495             this.el.removeClass(this.validClass);
32496             this.el.addClass(this.invalidClass);
32497         } else {
32498             this.el.removeClass('is-valid');
32499             this.el.addClass('is-invalid');
32500         }
32501         
32502         
32503         this.fireEvent('invalid', this, msg);
32504     }
32505     
32506    
32507 });
32508
32509 Roo.apply(Roo.bootstrap.FieldLabel, {
32510     
32511     groups: {},
32512     
32513      /**
32514     * register a FieldLabel Group
32515     * @param {Roo.bootstrap.FieldLabel} the FieldLabel to add
32516     */
32517     register : function(label)
32518     {
32519         if(this.groups.hasOwnProperty(label.target)){
32520             return;
32521         }
32522      
32523         this.groups[label.target] = label;
32524         
32525     },
32526     /**
32527     * fetch a FieldLabel Group based on the target
32528     * @param {string} target
32529     * @returns {Roo.bootstrap.FieldLabel} the CheckBox group
32530     */
32531     get: function(target) {
32532         if (typeof(this.groups[target]) == 'undefined') {
32533             return false;
32534         }
32535         
32536         return this.groups[target] ;
32537     }
32538 });
32539
32540  
32541
32542  /*
32543  * - LGPL
32544  *
32545  * page DateSplitField.
32546  * 
32547  */
32548
32549
32550 /**
32551  * @class Roo.bootstrap.DateSplitField
32552  * @extends Roo.bootstrap.Component
32553  * Bootstrap DateSplitField class
32554  * @cfg {string} fieldLabel - the label associated
32555  * @cfg {Number} labelWidth set the width of label (0-12)
32556  * @cfg {String} labelAlign (top|left)
32557  * @cfg {Boolean} dayAllowBlank (true|false) default false
32558  * @cfg {Boolean} monthAllowBlank (true|false) default false
32559  * @cfg {Boolean} yearAllowBlank (true|false) default false
32560  * @cfg {string} dayPlaceholder 
32561  * @cfg {string} monthPlaceholder
32562  * @cfg {string} yearPlaceholder
32563  * @cfg {string} dayFormat default 'd'
32564  * @cfg {string} monthFormat default 'm'
32565  * @cfg {string} yearFormat default 'Y'
32566  * @cfg {Number} labellg set the width of label (1-12)
32567  * @cfg {Number} labelmd set the width of label (1-12)
32568  * @cfg {Number} labelsm set the width of label (1-12)
32569  * @cfg {Number} labelxs set the width of label (1-12)
32570
32571  *     
32572  * @constructor
32573  * Create a new DateSplitField
32574  * @param {Object} config The config object
32575  */
32576
32577 Roo.bootstrap.DateSplitField = function(config){
32578     Roo.bootstrap.DateSplitField.superclass.constructor.call(this, config);
32579     
32580     this.addEvents({
32581         // raw events
32582          /**
32583          * @event years
32584          * getting the data of years
32585          * @param {Roo.bootstrap.DateSplitField} this
32586          * @param {Object} years
32587          */
32588         "years" : true,
32589         /**
32590          * @event days
32591          * getting the data of days
32592          * @param {Roo.bootstrap.DateSplitField} this
32593          * @param {Object} days
32594          */
32595         "days" : true,
32596         /**
32597          * @event invalid
32598          * Fires after the field has been marked as invalid.
32599          * @param {Roo.form.Field} this
32600          * @param {String} msg The validation message
32601          */
32602         invalid : true,
32603        /**
32604          * @event valid
32605          * Fires after the field has been validated with no errors.
32606          * @param {Roo.form.Field} this
32607          */
32608         valid : true
32609     });
32610 };
32611
32612 Roo.extend(Roo.bootstrap.DateSplitField, Roo.bootstrap.Component,  {
32613     
32614     fieldLabel : '',
32615     labelAlign : 'top',
32616     labelWidth : 3,
32617     dayAllowBlank : false,
32618     monthAllowBlank : false,
32619     yearAllowBlank : false,
32620     dayPlaceholder : '',
32621     monthPlaceholder : '',
32622     yearPlaceholder : '',
32623     dayFormat : 'd',
32624     monthFormat : 'm',
32625     yearFormat : 'Y',
32626     isFormField : true,
32627     labellg : 0,
32628     labelmd : 0,
32629     labelsm : 0,
32630     labelxs : 0,
32631     
32632     getAutoCreate : function()
32633     {
32634         var cfg = {
32635             tag : 'div',
32636             cls : 'row roo-date-split-field-group',
32637             cn : [
32638                 {
32639                     tag : 'input',
32640                     type : 'hidden',
32641                     cls : 'form-hidden-field roo-date-split-field-group-value',
32642                     name : this.name
32643                 }
32644             ]
32645         };
32646         
32647         var labelCls = 'col-md-12';
32648         var contentCls = 'col-md-4';
32649         
32650         if(this.fieldLabel){
32651             
32652             var label = {
32653                 tag : 'div',
32654                 cls : 'column roo-date-split-field-label col-md-' + ((this.labelAlign == 'top') ? '12' : this.labelWidth),
32655                 cn : [
32656                     {
32657                         tag : 'label',
32658                         html : this.fieldLabel
32659                     }
32660                 ]
32661             };
32662             
32663             if(this.labelAlign == 'left'){
32664             
32665                 if(this.labelWidth > 12){
32666                     label.style = "width: " + this.labelWidth + 'px';
32667                 }
32668
32669                 if(this.labelWidth < 13 && this.labelmd == 0){
32670                     this.labelmd = this.labelWidth;
32671                 }
32672
32673                 if(this.labellg > 0){
32674                     labelCls = ' col-lg-' + this.labellg;
32675                     contentCls = ' col-lg-' + ((12 - this.labellg) / 3);
32676                 }
32677
32678                 if(this.labelmd > 0){
32679                     labelCls = ' col-md-' + this.labelmd;
32680                     contentCls = ' col-md-' + ((12 - this.labelmd) / 3);
32681                 }
32682
32683                 if(this.labelsm > 0){
32684                     labelCls = ' col-sm-' + this.labelsm;
32685                     contentCls = ' col-sm-' + ((12 - this.labelsm) / 3);
32686                 }
32687
32688                 if(this.labelxs > 0){
32689                     labelCls = ' col-xs-' + this.labelxs;
32690                     contentCls = ' col-xs-' + ((12 - this.labelxs) / 3);
32691                 }
32692             }
32693             
32694             label.cls += ' ' + labelCls;
32695             
32696             cfg.cn.push(label);
32697         }
32698         
32699         Roo.each(['day', 'month', 'year'], function(t){
32700             cfg.cn.push({
32701                 tag : 'div',
32702                 cls : 'column roo-date-split-field-' + t + ' ' + contentCls
32703             });
32704         }, this);
32705         
32706         return cfg;
32707     },
32708     
32709     inputEl: function ()
32710     {
32711         return this.el.select('.roo-date-split-field-group-value', true).first();
32712     },
32713     
32714     onRender : function(ct, position) 
32715     {
32716         var _this = this;
32717         
32718         Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
32719         
32720         this.inputEl = this.el.select('.roo-date-split-field-group-value', true).first();
32721         
32722         this.dayField = new Roo.bootstrap.ComboBox({
32723             allowBlank : this.dayAllowBlank,
32724             alwaysQuery : true,
32725             displayField : 'value',
32726             editable : false,
32727             fieldLabel : '',
32728             forceSelection : true,
32729             mode : 'local',
32730             placeholder : this.dayPlaceholder,
32731             selectOnFocus : true,
32732             tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
32733             triggerAction : 'all',
32734             typeAhead : true,
32735             valueField : 'value',
32736             store : new Roo.data.SimpleStore({
32737                 data : (function() {    
32738                     var days = [];
32739                     _this.fireEvent('days', _this, days);
32740                     return days;
32741                 })(),
32742                 fields : [ 'value' ]
32743             }),
32744             listeners : {
32745                 select : function (_self, record, index)
32746                 {
32747                     _this.setValue(_this.getValue());
32748                 }
32749             }
32750         });
32751
32752         this.dayField.render(this.el.select('.roo-date-split-field-day', true).first(), null);
32753         
32754         this.monthField = new Roo.bootstrap.MonthField({
32755             after : '<i class=\"fa fa-calendar\"></i>',
32756             allowBlank : this.monthAllowBlank,
32757             placeholder : this.monthPlaceholder,
32758             readOnly : true,
32759             listeners : {
32760                 render : function (_self)
32761                 {
32762                     this.el.select('span.input-group-addon', true).first().on('click', function(e){
32763                         e.preventDefault();
32764                         _self.focus();
32765                     });
32766                 },
32767                 select : function (_self, oldvalue, newvalue)
32768                 {
32769                     _this.setValue(_this.getValue());
32770                 }
32771             }
32772         });
32773         
32774         this.monthField.render(this.el.select('.roo-date-split-field-month', true).first(), null);
32775         
32776         this.yearField = new Roo.bootstrap.ComboBox({
32777             allowBlank : this.yearAllowBlank,
32778             alwaysQuery : true,
32779             displayField : 'value',
32780             editable : false,
32781             fieldLabel : '',
32782             forceSelection : true,
32783             mode : 'local',
32784             placeholder : this.yearPlaceholder,
32785             selectOnFocus : true,
32786             tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
32787             triggerAction : 'all',
32788             typeAhead : true,
32789             valueField : 'value',
32790             store : new Roo.data.SimpleStore({
32791                 data : (function() {
32792                     var years = [];
32793                     _this.fireEvent('years', _this, years);
32794                     return years;
32795                 })(),
32796                 fields : [ 'value' ]
32797             }),
32798             listeners : {
32799                 select : function (_self, record, index)
32800                 {
32801                     _this.setValue(_this.getValue());
32802                 }
32803             }
32804         });
32805
32806         this.yearField.render(this.el.select('.roo-date-split-field-year', true).first(), null);
32807     },
32808     
32809     setValue : function(v, format)
32810     {
32811         this.inputEl.dom.value = v;
32812         
32813         var f = format || (this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat);
32814         
32815         var d = Date.parseDate(v, f);
32816         
32817         if(!d){
32818             this.validate();
32819             return;
32820         }
32821         
32822         this.setDay(d.format(this.dayFormat));
32823         this.setMonth(d.format(this.monthFormat));
32824         this.setYear(d.format(this.yearFormat));
32825         
32826         this.validate();
32827         
32828         return;
32829     },
32830     
32831     setDay : function(v)
32832     {
32833         this.dayField.setValue(v);
32834         this.inputEl.dom.value = this.getValue();
32835         this.validate();
32836         return;
32837     },
32838     
32839     setMonth : function(v)
32840     {
32841         this.monthField.setValue(v, true);
32842         this.inputEl.dom.value = this.getValue();
32843         this.validate();
32844         return;
32845     },
32846     
32847     setYear : function(v)
32848     {
32849         this.yearField.setValue(v);
32850         this.inputEl.dom.value = this.getValue();
32851         this.validate();
32852         return;
32853     },
32854     
32855     getDay : function()
32856     {
32857         return this.dayField.getValue();
32858     },
32859     
32860     getMonth : function()
32861     {
32862         return this.monthField.getValue();
32863     },
32864     
32865     getYear : function()
32866     {
32867         return this.yearField.getValue();
32868     },
32869     
32870     getValue : function()
32871     {
32872         var f = this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat;
32873         
32874         var date = this.yearField.getValue() + '-' + this.monthField.getValue() + '-' + this.dayField.getValue();
32875         
32876         return date;
32877     },
32878     
32879     reset : function()
32880     {
32881         this.setDay('');
32882         this.setMonth('');
32883         this.setYear('');
32884         this.inputEl.dom.value = '';
32885         this.validate();
32886         return;
32887     },
32888     
32889     validate : function()
32890     {
32891         var d = this.dayField.validate();
32892         var m = this.monthField.validate();
32893         var y = this.yearField.validate();
32894         
32895         var valid = true;
32896         
32897         if(
32898                 (!this.dayAllowBlank && !d) ||
32899                 (!this.monthAllowBlank && !m) ||
32900                 (!this.yearAllowBlank && !y)
32901         ){
32902             valid = false;
32903         }
32904         
32905         if(this.dayAllowBlank && this.monthAllowBlank && this.yearAllowBlank){
32906             return valid;
32907         }
32908         
32909         if(valid){
32910             this.markValid();
32911             return valid;
32912         }
32913         
32914         this.markInvalid();
32915         
32916         return valid;
32917     },
32918     
32919     markValid : function()
32920     {
32921         
32922         var label = this.el.select('label', true).first();
32923         var icon = this.el.select('i.fa-star', true).first();
32924
32925         if(label && icon){
32926             icon.remove();
32927         }
32928         
32929         this.fireEvent('valid', this);
32930     },
32931     
32932      /**
32933      * Mark this field as invalid
32934      * @param {String} msg The validation message
32935      */
32936     markInvalid : function(msg)
32937     {
32938         
32939         var label = this.el.select('label', true).first();
32940         var icon = this.el.select('i.fa-star', true).first();
32941
32942         if(label && !icon){
32943             this.el.select('.roo-date-split-field-label', true).createChild({
32944                 tag : 'i',
32945                 cls : 'text-danger fa fa-lg fa-star',
32946                 tooltip : 'This field is required',
32947                 style : 'margin-right:5px;'
32948             }, label, true);
32949         }
32950         
32951         this.fireEvent('invalid', this, msg);
32952     },
32953     
32954     clearInvalid : function()
32955     {
32956         var label = this.el.select('label', true).first();
32957         var icon = this.el.select('i.fa-star', true).first();
32958
32959         if(label && icon){
32960             icon.remove();
32961         }
32962         
32963         this.fireEvent('valid', this);
32964     },
32965     
32966     getName: function()
32967     {
32968         return this.name;
32969     }
32970     
32971 });
32972
32973  /**
32974  *
32975  * This is based on 
32976  * http://masonry.desandro.com
32977  *
32978  * The idea is to render all the bricks based on vertical width...
32979  *
32980  * The original code extends 'outlayer' - we might need to use that....
32981  * 
32982  */
32983
32984
32985 /**
32986  * @class Roo.bootstrap.LayoutMasonry
32987  * @extends Roo.bootstrap.Component
32988  * Bootstrap Layout Masonry class
32989  * 
32990  * @constructor
32991  * Create a new Element
32992  * @param {Object} config The config object
32993  */
32994
32995 Roo.bootstrap.LayoutMasonry = function(config){
32996     
32997     Roo.bootstrap.LayoutMasonry.superclass.constructor.call(this, config);
32998     
32999     this.bricks = [];
33000     
33001     Roo.bootstrap.LayoutMasonry.register(this);
33002     
33003     this.addEvents({
33004         // raw events
33005         /**
33006          * @event layout
33007          * Fire after layout the items
33008          * @param {Roo.bootstrap.LayoutMasonry} this
33009          * @param {Roo.EventObject} e
33010          */
33011         "layout" : true
33012     });
33013     
33014 };
33015
33016 Roo.extend(Roo.bootstrap.LayoutMasonry, Roo.bootstrap.Component,  {
33017     
33018     /**
33019      * @cfg {Boolean} isLayoutInstant = no animation?
33020      */   
33021     isLayoutInstant : false, // needed?
33022    
33023     /**
33024      * @cfg {Number} boxWidth  width of the columns
33025      */   
33026     boxWidth : 450,
33027     
33028       /**
33029      * @cfg {Number} boxHeight  - 0 for square, or fix it at a certian height
33030      */   
33031     boxHeight : 0,
33032     
33033     /**
33034      * @cfg {Number} padWidth padding below box..
33035      */   
33036     padWidth : 10, 
33037     
33038     /**
33039      * @cfg {Number} gutter gutter width..
33040      */   
33041     gutter : 10,
33042     
33043      /**
33044      * @cfg {Number} maxCols maximum number of columns
33045      */   
33046     
33047     maxCols: 0,
33048     
33049     /**
33050      * @cfg {Boolean} isAutoInitial defalut true
33051      */   
33052     isAutoInitial : true, 
33053     
33054     containerWidth: 0,
33055     
33056     /**
33057      * @cfg {Boolean} isHorizontal defalut false
33058      */   
33059     isHorizontal : false, 
33060
33061     currentSize : null,
33062     
33063     tag: 'div',
33064     
33065     cls: '',
33066     
33067     bricks: null, //CompositeElement
33068     
33069     cols : 1,
33070     
33071     _isLayoutInited : false,
33072     
33073 //    isAlternative : false, // only use for vertical layout...
33074     
33075     /**
33076      * @cfg {Number} alternativePadWidth padding below box..
33077      */   
33078     alternativePadWidth : 50,
33079     
33080     selectedBrick : [],
33081     
33082     getAutoCreate : function(){
33083         
33084         var cfg = Roo.apply({}, Roo.bootstrap.LayoutMasonry.superclass.getAutoCreate.call(this));
33085         
33086         var cfg = {
33087             tag: this.tag,
33088             cls: 'blog-masonary-wrapper ' + this.cls,
33089             cn : {
33090                 cls : 'mas-boxes masonary'
33091             }
33092         };
33093         
33094         return cfg;
33095     },
33096     
33097     getChildContainer: function( )
33098     {
33099         if (this.boxesEl) {
33100             return this.boxesEl;
33101         }
33102         
33103         this.boxesEl = this.el.select('.mas-boxes').first();
33104         
33105         return this.boxesEl;
33106     },
33107     
33108     
33109     initEvents : function()
33110     {
33111         var _this = this;
33112         
33113         if(this.isAutoInitial){
33114             Roo.log('hook children rendered');
33115             this.on('childrenrendered', function() {
33116                 Roo.log('children rendered');
33117                 _this.initial();
33118             } ,this);
33119         }
33120     },
33121     
33122     initial : function()
33123     {
33124         this.selectedBrick = [];
33125         
33126         this.currentSize = this.el.getBox(true);
33127         
33128         Roo.EventManager.onWindowResize(this.resize, this); 
33129
33130         if(!this.isAutoInitial){
33131             this.layout();
33132             return;
33133         }
33134         
33135         this.layout();
33136         
33137         return;
33138         //this.layout.defer(500,this);
33139         
33140     },
33141     
33142     resize : function()
33143     {
33144         var cs = this.el.getBox(true);
33145         
33146         if (
33147                 this.currentSize.width == cs.width && 
33148                 this.currentSize.x == cs.x && 
33149                 this.currentSize.height == cs.height && 
33150                 this.currentSize.y == cs.y 
33151         ) {
33152             Roo.log("no change in with or X or Y");
33153             return;
33154         }
33155         
33156         this.currentSize = cs;
33157         
33158         this.layout();
33159         
33160     },
33161     
33162     layout : function()
33163     {   
33164         this._resetLayout();
33165         
33166         var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
33167         
33168         this.layoutItems( isInstant );
33169       
33170         this._isLayoutInited = true;
33171         
33172         this.fireEvent('layout', this);
33173         
33174     },
33175     
33176     _resetLayout : function()
33177     {
33178         if(this.isHorizontal){
33179             this.horizontalMeasureColumns();
33180             return;
33181         }
33182         
33183         this.verticalMeasureColumns();
33184         
33185     },
33186     
33187     verticalMeasureColumns : function()
33188     {
33189         this.getContainerWidth();
33190         
33191 //        if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
33192 //            this.colWidth = Math.floor(this.containerWidth * 0.8);
33193 //            return;
33194 //        }
33195         
33196         var boxWidth = this.boxWidth + this.padWidth;
33197         
33198         if(this.containerWidth < this.boxWidth){
33199             boxWidth = this.containerWidth
33200         }
33201         
33202         var containerWidth = this.containerWidth;
33203         
33204         var cols = Math.floor(containerWidth / boxWidth);
33205         
33206         this.cols = Math.max( cols, 1 );
33207         
33208         this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
33209         
33210         var totalBoxWidth = this.cols * boxWidth - this.padWidth;
33211         
33212         var avail = Math.floor((containerWidth - totalBoxWidth) / this.cols);
33213         
33214         this.colWidth = boxWidth + avail - this.padWidth;
33215         
33216         this.unitWidth = Math.round((this.colWidth - (this.gutter * 2)) / 3);
33217         this.unitHeight = this.boxHeight > 0 ? this.boxHeight  : this.unitWidth;
33218     },
33219     
33220     horizontalMeasureColumns : function()
33221     {
33222         this.getContainerWidth();
33223         
33224         var boxWidth = this.boxWidth;
33225         
33226         if(this.containerWidth < boxWidth){
33227             boxWidth = this.containerWidth;
33228         }
33229         
33230         this.unitWidth = Math.floor((boxWidth - (this.gutter * 2)) / 3);
33231         
33232         this.el.setHeight(boxWidth);
33233         
33234     },
33235     
33236     getContainerWidth : function()
33237     {
33238         this.containerWidth = this.el.getBox(true).width;  //maybe use getComputedWidth
33239     },
33240     
33241     layoutItems : function( isInstant )
33242     {
33243         Roo.log(this.bricks);
33244         
33245         var items = Roo.apply([], this.bricks);
33246         
33247         if(this.isHorizontal){
33248             this._horizontalLayoutItems( items , isInstant );
33249             return;
33250         }
33251         
33252 //        if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
33253 //            this._verticalAlternativeLayoutItems( items , isInstant );
33254 //            return;
33255 //        }
33256         
33257         this._verticalLayoutItems( items , isInstant );
33258         
33259     },
33260     
33261     _verticalLayoutItems : function ( items , isInstant)
33262     {
33263         if ( !items || !items.length ) {
33264             return;
33265         }
33266         
33267         var standard = [
33268             ['xs', 'xs', 'xs', 'tall'],
33269             ['xs', 'xs', 'tall'],
33270             ['xs', 'xs', 'sm'],
33271             ['xs', 'xs', 'xs'],
33272             ['xs', 'tall'],
33273             ['xs', 'sm'],
33274             ['xs', 'xs'],
33275             ['xs'],
33276             
33277             ['sm', 'xs', 'xs'],
33278             ['sm', 'xs'],
33279             ['sm'],
33280             
33281             ['tall', 'xs', 'xs', 'xs'],
33282             ['tall', 'xs', 'xs'],
33283             ['tall', 'xs'],
33284             ['tall']
33285             
33286         ];
33287         
33288         var queue = [];
33289         
33290         var boxes = [];
33291         
33292         var box = [];
33293         
33294         Roo.each(items, function(item, k){
33295             
33296             switch (item.size) {
33297                 // these layouts take up a full box,
33298                 case 'md' :
33299                 case 'md-left' :
33300                 case 'md-right' :
33301                 case 'wide' :
33302                     
33303                     if(box.length){
33304                         boxes.push(box);
33305                         box = [];
33306                     }
33307                     
33308                     boxes.push([item]);
33309                     
33310                     break;
33311                     
33312                 case 'xs' :
33313                 case 'sm' :
33314                 case 'tall' :
33315                     
33316                     box.push(item);
33317                     
33318                     break;
33319                 default :
33320                     break;
33321                     
33322             }
33323             
33324         }, this);
33325         
33326         if(box.length){
33327             boxes.push(box);
33328             box = [];
33329         }
33330         
33331         var filterPattern = function(box, length)
33332         {
33333             if(!box.length){
33334                 return;
33335             }
33336             
33337             var match = false;
33338             
33339             var pattern = box.slice(0, length);
33340             
33341             var format = [];
33342             
33343             Roo.each(pattern, function(i){
33344                 format.push(i.size);
33345             }, this);
33346             
33347             Roo.each(standard, function(s){
33348                 
33349                 if(String(s) != String(format)){
33350                     return;
33351                 }
33352                 
33353                 match = true;
33354                 return false;
33355                 
33356             }, this);
33357             
33358             if(!match && length == 1){
33359                 return;
33360             }
33361             
33362             if(!match){
33363                 filterPattern(box, length - 1);
33364                 return;
33365             }
33366                 
33367             queue.push(pattern);
33368
33369             box = box.slice(length, box.length);
33370
33371             filterPattern(box, 4);
33372
33373             return;
33374             
33375         }
33376         
33377         Roo.each(boxes, function(box, k){
33378             
33379             if(!box.length){
33380                 return;
33381             }
33382             
33383             if(box.length == 1){
33384                 queue.push(box);
33385                 return;
33386             }
33387             
33388             filterPattern(box, 4);
33389             
33390         }, this);
33391         
33392         this._processVerticalLayoutQueue( queue, isInstant );
33393         
33394     },
33395     
33396 //    _verticalAlternativeLayoutItems : function( items , isInstant )
33397 //    {
33398 //        if ( !items || !items.length ) {
33399 //            return;
33400 //        }
33401 //
33402 //        this._processVerticalAlternativeLayoutQueue( items, isInstant );
33403 //        
33404 //    },
33405     
33406     _horizontalLayoutItems : function ( items , isInstant)
33407     {
33408         if ( !items || !items.length || items.length < 3) {
33409             return;
33410         }
33411         
33412         items.reverse();
33413         
33414         var eItems = items.slice(0, 3);
33415         
33416         items = items.slice(3, items.length);
33417         
33418         var standard = [
33419             ['xs', 'xs', 'xs', 'wide'],
33420             ['xs', 'xs', 'wide'],
33421             ['xs', 'xs', 'sm'],
33422             ['xs', 'xs', 'xs'],
33423             ['xs', 'wide'],
33424             ['xs', 'sm'],
33425             ['xs', 'xs'],
33426             ['xs'],
33427             
33428             ['sm', 'xs', 'xs'],
33429             ['sm', 'xs'],
33430             ['sm'],
33431             
33432             ['wide', 'xs', 'xs', 'xs'],
33433             ['wide', 'xs', 'xs'],
33434             ['wide', 'xs'],
33435             ['wide'],
33436             
33437             ['wide-thin']
33438         ];
33439         
33440         var queue = [];
33441         
33442         var boxes = [];
33443         
33444         var box = [];
33445         
33446         Roo.each(items, function(item, k){
33447             
33448             switch (item.size) {
33449                 case 'md' :
33450                 case 'md-left' :
33451                 case 'md-right' :
33452                 case 'tall' :
33453                     
33454                     if(box.length){
33455                         boxes.push(box);
33456                         box = [];
33457                     }
33458                     
33459                     boxes.push([item]);
33460                     
33461                     break;
33462                     
33463                 case 'xs' :
33464                 case 'sm' :
33465                 case 'wide' :
33466                 case 'wide-thin' :
33467                     
33468                     box.push(item);
33469                     
33470                     break;
33471                 default :
33472                     break;
33473                     
33474             }
33475             
33476         }, this);
33477         
33478         if(box.length){
33479             boxes.push(box);
33480             box = [];
33481         }
33482         
33483         var filterPattern = function(box, length)
33484         {
33485             if(!box.length){
33486                 return;
33487             }
33488             
33489             var match = false;
33490             
33491             var pattern = box.slice(0, length);
33492             
33493             var format = [];
33494             
33495             Roo.each(pattern, function(i){
33496                 format.push(i.size);
33497             }, this);
33498             
33499             Roo.each(standard, function(s){
33500                 
33501                 if(String(s) != String(format)){
33502                     return;
33503                 }
33504                 
33505                 match = true;
33506                 return false;
33507                 
33508             }, this);
33509             
33510             if(!match && length == 1){
33511                 return;
33512             }
33513             
33514             if(!match){
33515                 filterPattern(box, length - 1);
33516                 return;
33517             }
33518                 
33519             queue.push(pattern);
33520
33521             box = box.slice(length, box.length);
33522
33523             filterPattern(box, 4);
33524
33525             return;
33526             
33527         }
33528         
33529         Roo.each(boxes, function(box, k){
33530             
33531             if(!box.length){
33532                 return;
33533             }
33534             
33535             if(box.length == 1){
33536                 queue.push(box);
33537                 return;
33538             }
33539             
33540             filterPattern(box, 4);
33541             
33542         }, this);
33543         
33544         
33545         var prune = [];
33546         
33547         var pos = this.el.getBox(true);
33548         
33549         var minX = pos.x;
33550         
33551         var maxX = pos.right - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
33552         
33553         var hit_end = false;
33554         
33555         Roo.each(queue, function(box){
33556             
33557             if(hit_end){
33558                 
33559                 Roo.each(box, function(b){
33560                 
33561                     b.el.setVisibilityMode(Roo.Element.DISPLAY);
33562                     b.el.hide();
33563
33564                 }, this);
33565
33566                 return;
33567             }
33568             
33569             var mx = 0;
33570             
33571             Roo.each(box, function(b){
33572                 
33573                 b.el.setVisibilityMode(Roo.Element.DISPLAY);
33574                 b.el.show();
33575
33576                 mx = Math.max(mx, b.x);
33577                 
33578             }, this);
33579             
33580             maxX = maxX - this.unitWidth * mx - this.gutter * (mx - 1) - this.padWidth;
33581             
33582             if(maxX < minX){
33583                 
33584                 Roo.each(box, function(b){
33585                 
33586                     b.el.setVisibilityMode(Roo.Element.DISPLAY);
33587                     b.el.hide();
33588                     
33589                 }, this);
33590                 
33591                 hit_end = true;
33592                 
33593                 return;
33594             }
33595             
33596             prune.push(box);
33597             
33598         }, this);
33599         
33600         this._processHorizontalLayoutQueue( prune, eItems, isInstant );
33601     },
33602     
33603     /** Sets position of item in DOM
33604     * @param {Element} item
33605     * @param {Number} x - horizontal position
33606     * @param {Number} y - vertical position
33607     * @param {Boolean} isInstant - disables transitions
33608     */
33609     _processVerticalLayoutQueue : function( queue, isInstant )
33610     {
33611         var pos = this.el.getBox(true);
33612         var x = pos.x;
33613         var y = pos.y;
33614         var maxY = [];
33615         
33616         for (var i = 0; i < this.cols; i++){
33617             maxY[i] = pos.y;
33618         }
33619         
33620         Roo.each(queue, function(box, k){
33621             
33622             var col = k % this.cols;
33623             
33624             Roo.each(box, function(b,kk){
33625                 
33626                 b.el.position('absolute');
33627                 
33628                 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
33629                 var height = Math.floor(this.unitHeight * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
33630                 
33631                 if(b.size == 'md-left' || b.size == 'md-right'){
33632                     width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
33633                     height = Math.floor(this.unitHeight * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
33634                 }
33635                 
33636                 b.el.setWidth(width);
33637                 b.el.setHeight(height);
33638                 // iframe?
33639                 b.el.select('iframe',true).setSize(width,height);
33640                 
33641             }, this);
33642             
33643             for (var i = 0; i < this.cols; i++){
33644                 
33645                 if(maxY[i] < maxY[col]){
33646                     col = i;
33647                     continue;
33648                 }
33649                 
33650                 col = Math.min(col, i);
33651                 
33652             }
33653             
33654             x = pos.x + col * (this.colWidth + this.padWidth);
33655             
33656             y = maxY[col];
33657             
33658             var positions = [];
33659             
33660             switch (box.length){
33661                 case 1 :
33662                     positions = this.getVerticalOneBoxColPositions(x, y, box);
33663                     break;
33664                 case 2 :
33665                     positions = this.getVerticalTwoBoxColPositions(x, y, box);
33666                     break;
33667                 case 3 :
33668                     positions = this.getVerticalThreeBoxColPositions(x, y, box);
33669                     break;
33670                 case 4 :
33671                     positions = this.getVerticalFourBoxColPositions(x, y, box);
33672                     break;
33673                 default :
33674                     break;
33675             }
33676             
33677             Roo.each(box, function(b,kk){
33678                 
33679                 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
33680                 
33681                 var sz = b.el.getSize();
33682                 
33683                 maxY[col] = Math.max(maxY[col], positions[kk].y + sz.height + this.padWidth);
33684                 
33685             }, this);
33686             
33687         }, this);
33688         
33689         var mY = 0;
33690         
33691         for (var i = 0; i < this.cols; i++){
33692             mY = Math.max(mY, maxY[i]);
33693         }
33694         
33695         this.el.setHeight(mY - pos.y);
33696         
33697     },
33698     
33699 //    _processVerticalAlternativeLayoutQueue : function( items, isInstant )
33700 //    {
33701 //        var pos = this.el.getBox(true);
33702 //        var x = pos.x;
33703 //        var y = pos.y;
33704 //        var maxX = pos.right;
33705 //        
33706 //        var maxHeight = 0;
33707 //        
33708 //        Roo.each(items, function(item, k){
33709 //            
33710 //            var c = k % 2;
33711 //            
33712 //            item.el.position('absolute');
33713 //                
33714 //            var width = Math.floor(this.colWidth + item.el.getPadding('lr'));
33715 //
33716 //            item.el.setWidth(width);
33717 //
33718 //            var height = Math.floor(this.colWidth * item.y / item.x + item.el.getPadding('tb'));
33719 //
33720 //            item.el.setHeight(height);
33721 //            
33722 //            if(c == 0){
33723 //                item.el.setXY([x, y], isInstant ? false : true);
33724 //            } else {
33725 //                item.el.setXY([maxX - width, y], isInstant ? false : true);
33726 //            }
33727 //            
33728 //            y = y + height + this.alternativePadWidth;
33729 //            
33730 //            maxHeight = maxHeight + height + this.alternativePadWidth;
33731 //            
33732 //        }, this);
33733 //        
33734 //        this.el.setHeight(maxHeight);
33735 //        
33736 //    },
33737     
33738     _processHorizontalLayoutQueue : function( queue, eItems, isInstant )
33739     {
33740         var pos = this.el.getBox(true);
33741         
33742         var minX = pos.x;
33743         var minY = pos.y;
33744         
33745         var maxX = pos.right;
33746         
33747         this._processHorizontalEndItem(eItems, maxX, minX, minY, isInstant);
33748         
33749         var maxX = maxX - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
33750         
33751         Roo.each(queue, function(box, k){
33752             
33753             Roo.each(box, function(b, kk){
33754                 
33755                 b.el.position('absolute');
33756                 
33757                 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
33758                 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
33759                 
33760                 if(b.size == 'md-left' || b.size == 'md-right'){
33761                     width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
33762                     height = Math.floor(this.unitWidth * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
33763                 }
33764                 
33765                 b.el.setWidth(width);
33766                 b.el.setHeight(height);
33767                 
33768             }, this);
33769             
33770             if(!box.length){
33771                 return;
33772             }
33773             
33774             var positions = [];
33775             
33776             switch (box.length){
33777                 case 1 :
33778                     positions = this.getHorizontalOneBoxColPositions(maxX, minY, box);
33779                     break;
33780                 case 2 :
33781                     positions = this.getHorizontalTwoBoxColPositions(maxX, minY, box);
33782                     break;
33783                 case 3 :
33784                     positions = this.getHorizontalThreeBoxColPositions(maxX, minY, box);
33785                     break;
33786                 case 4 :
33787                     positions = this.getHorizontalFourBoxColPositions(maxX, minY, box);
33788                     break;
33789                 default :
33790                     break;
33791             }
33792             
33793             Roo.each(box, function(b,kk){
33794                 
33795                 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
33796                 
33797                 maxX = Math.min(maxX, positions[kk].x - this.padWidth);
33798                 
33799             }, this);
33800             
33801         }, this);
33802         
33803     },
33804     
33805     _processHorizontalEndItem : function(eItems, maxX, minX, minY, isInstant)
33806     {
33807         Roo.each(eItems, function(b,k){
33808             
33809             b.size = (k == 0) ? 'sm' : 'xs';
33810             b.x = (k == 0) ? 2 : 1;
33811             b.y = (k == 0) ? 2 : 1;
33812             
33813             b.el.position('absolute');
33814             
33815             var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
33816                 
33817             b.el.setWidth(width);
33818             
33819             var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
33820             
33821             b.el.setHeight(height);
33822             
33823         }, this);
33824
33825         var positions = [];
33826         
33827         positions.push({
33828             x : maxX - this.unitWidth * 2 - this.gutter,
33829             y : minY
33830         });
33831         
33832         positions.push({
33833             x : maxX - this.unitWidth,
33834             y : minY + (this.unitWidth + this.gutter) * 2
33835         });
33836         
33837         positions.push({
33838             x : maxX - this.unitWidth * 3 - this.gutter * 2,
33839             y : minY
33840         });
33841         
33842         Roo.each(eItems, function(b,k){
33843             
33844             b.el.setXY([positions[k].x, positions[k].y], isInstant ? false : true);
33845
33846         }, this);
33847         
33848     },
33849     
33850     getVerticalOneBoxColPositions : function(x, y, box)
33851     {
33852         var pos = [];
33853         
33854         var rand = Math.floor(Math.random() * ((4 - box[0].x)));
33855         
33856         if(box[0].size == 'md-left'){
33857             rand = 0;
33858         }
33859         
33860         if(box[0].size == 'md-right'){
33861             rand = 1;
33862         }
33863         
33864         pos.push({
33865             x : x + (this.unitWidth + this.gutter) * rand,
33866             y : y
33867         });
33868         
33869         return pos;
33870     },
33871     
33872     getVerticalTwoBoxColPositions : function(x, y, box)
33873     {
33874         var pos = [];
33875         
33876         if(box[0].size == 'xs'){
33877             
33878             pos.push({
33879                 x : x,
33880                 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[1].y))
33881             });
33882
33883             pos.push({
33884                 x : x + (this.unitWidth + this.gutter) * (3 - box[1].x),
33885                 y : y
33886             });
33887             
33888             return pos;
33889             
33890         }
33891         
33892         pos.push({
33893             x : x,
33894             y : y
33895         });
33896
33897         pos.push({
33898             x : x + (this.unitWidth + this.gutter) * 2,
33899             y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[0].y))
33900         });
33901         
33902         return pos;
33903         
33904     },
33905     
33906     getVerticalThreeBoxColPositions : function(x, y, box)
33907     {
33908         var pos = [];
33909         
33910         if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
33911             
33912             pos.push({
33913                 x : x,
33914                 y : y
33915             });
33916
33917             pos.push({
33918                 x : x + (this.unitWidth + this.gutter) * 1,
33919                 y : y
33920             });
33921             
33922             pos.push({
33923                 x : x + (this.unitWidth + this.gutter) * 2,
33924                 y : y
33925             });
33926             
33927             return pos;
33928             
33929         }
33930         
33931         if(box[0].size == 'xs' && box[1].size == 'xs'){
33932             
33933             pos.push({
33934                 x : x,
33935                 y : y
33936             });
33937
33938             pos.push({
33939                 x : x,
33940                 y : y + ((this.unitHeight + this.gutter) * (box[2].y - 1))
33941             });
33942             
33943             pos.push({
33944                 x : x + (this.unitWidth + this.gutter) * 1,
33945                 y : y
33946             });
33947             
33948             return pos;
33949             
33950         }
33951         
33952         pos.push({
33953             x : x,
33954             y : y
33955         });
33956
33957         pos.push({
33958             x : x + (this.unitWidth + this.gutter) * 2,
33959             y : y
33960         });
33961
33962         pos.push({
33963             x : x + (this.unitWidth + this.gutter) * 2,
33964             y : y + (this.unitHeight + this.gutter) * (box[0].y - 1)
33965         });
33966             
33967         return pos;
33968         
33969     },
33970     
33971     getVerticalFourBoxColPositions : function(x, y, box)
33972     {
33973         var pos = [];
33974         
33975         if(box[0].size == 'xs'){
33976             
33977             pos.push({
33978                 x : x,
33979                 y : y
33980             });
33981
33982             pos.push({
33983                 x : x,
33984                 y : y + (this.unitHeight + this.gutter) * 1
33985             });
33986             
33987             pos.push({
33988                 x : x,
33989                 y : y + (this.unitHeight + this.gutter) * 2
33990             });
33991             
33992             pos.push({
33993                 x : x + (this.unitWidth + this.gutter) * 1,
33994                 y : y
33995             });
33996             
33997             return pos;
33998             
33999         }
34000         
34001         pos.push({
34002             x : x,
34003             y : y
34004         });
34005
34006         pos.push({
34007             x : x + (this.unitWidth + this.gutter) * 2,
34008             y : y
34009         });
34010
34011         pos.push({
34012             x : x + (this.unitHeightunitWidth + this.gutter) * 2,
34013             y : y + (this.unitHeight + this.gutter) * 1
34014         });
34015
34016         pos.push({
34017             x : x + (this.unitWidth + this.gutter) * 2,
34018             y : y + (this.unitWidth + this.gutter) * 2
34019         });
34020
34021         return pos;
34022         
34023     },
34024     
34025     getHorizontalOneBoxColPositions : function(maxX, minY, box)
34026     {
34027         var pos = [];
34028         
34029         if(box[0].size == 'md-left'){
34030             pos.push({
34031                 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
34032                 y : minY
34033             });
34034             
34035             return pos;
34036         }
34037         
34038         if(box[0].size == 'md-right'){
34039             pos.push({
34040                 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
34041                 y : minY + (this.unitWidth + this.gutter) * 1
34042             });
34043             
34044             return pos;
34045         }
34046         
34047         var rand = Math.floor(Math.random() * (4 - box[0].y));
34048         
34049         pos.push({
34050             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34051             y : minY + (this.unitWidth + this.gutter) * rand
34052         });
34053         
34054         return pos;
34055         
34056     },
34057     
34058     getHorizontalTwoBoxColPositions : function(maxX, minY, box)
34059     {
34060         var pos = [];
34061         
34062         if(box[0].size == 'xs'){
34063             
34064             pos.push({
34065                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34066                 y : minY
34067             });
34068
34069             pos.push({
34070                 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34071                 y : minY + (this.unitWidth + this.gutter) * (3 - box[1].y)
34072             });
34073             
34074             return pos;
34075             
34076         }
34077         
34078         pos.push({
34079             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34080             y : minY
34081         });
34082
34083         pos.push({
34084             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34085             y : minY + (this.unitWidth + this.gutter) * 2
34086         });
34087         
34088         return pos;
34089         
34090     },
34091     
34092     getHorizontalThreeBoxColPositions : function(maxX, minY, box)
34093     {
34094         var pos = [];
34095         
34096         if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
34097             
34098             pos.push({
34099                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34100                 y : minY
34101             });
34102
34103             pos.push({
34104                 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34105                 y : minY + (this.unitWidth + this.gutter) * 1
34106             });
34107             
34108             pos.push({
34109                 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
34110                 y : minY + (this.unitWidth + this.gutter) * 2
34111             });
34112             
34113             return pos;
34114             
34115         }
34116         
34117         if(box[0].size == 'xs' && box[1].size == 'xs'){
34118             
34119             pos.push({
34120                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34121                 y : minY
34122             });
34123
34124             pos.push({
34125                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34126                 y : minY
34127             });
34128             
34129             pos.push({
34130                 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
34131                 y : minY + (this.unitWidth + this.gutter) * 1
34132             });
34133             
34134             return pos;
34135             
34136         }
34137         
34138         pos.push({
34139             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34140             y : minY
34141         });
34142
34143         pos.push({
34144             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34145             y : minY + (this.unitWidth + this.gutter) * 2
34146         });
34147
34148         pos.push({
34149             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
34150             y : minY + (this.unitWidth + this.gutter) * 2
34151         });
34152             
34153         return pos;
34154         
34155     },
34156     
34157     getHorizontalFourBoxColPositions : function(maxX, minY, box)
34158     {
34159         var pos = [];
34160         
34161         if(box[0].size == 'xs'){
34162             
34163             pos.push({
34164                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34165                 y : minY
34166             });
34167
34168             pos.push({
34169                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34170                 y : minY
34171             });
34172             
34173             pos.push({
34174                 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),
34175                 y : minY
34176             });
34177             
34178             pos.push({
34179                 x : maxX - this.unitWidth * box[3].x - this.gutter * (box[3].x - 1),
34180                 y : minY + (this.unitWidth + this.gutter) * 1
34181             });
34182             
34183             return pos;
34184             
34185         }
34186         
34187         pos.push({
34188             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34189             y : minY
34190         });
34191         
34192         pos.push({
34193             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34194             y : minY + (this.unitWidth + this.gutter) * 2
34195         });
34196         
34197         pos.push({
34198             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
34199             y : minY + (this.unitWidth + this.gutter) * 2
34200         });
34201         
34202         pos.push({
34203             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),
34204             y : minY + (this.unitWidth + this.gutter) * 2
34205         });
34206
34207         return pos;
34208         
34209     },
34210     
34211     /**
34212     * remove a Masonry Brick
34213     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to remove
34214     */
34215     removeBrick : function(brick_id)
34216     {
34217         if (!brick_id) {
34218             return;
34219         }
34220         
34221         for (var i = 0; i<this.bricks.length; i++) {
34222             if (this.bricks[i].id == brick_id) {
34223                 this.bricks.splice(i,1);
34224                 this.el.dom.removeChild(Roo.get(brick_id).dom);
34225                 this.initial();
34226             }
34227         }
34228     },
34229     
34230     /**
34231     * adds a Masonry Brick
34232     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
34233     */
34234     addBrick : function(cfg)
34235     {
34236         var cn = new Roo.bootstrap.MasonryBrick(cfg);
34237         //this.register(cn);
34238         cn.parentId = this.id;
34239         cn.render(this.el);
34240         return cn;
34241     },
34242     
34243     /**
34244     * register a Masonry Brick
34245     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
34246     */
34247     
34248     register : function(brick)
34249     {
34250         this.bricks.push(brick);
34251         brick.masonryId = this.id;
34252     },
34253     
34254     /**
34255     * clear all the Masonry Brick
34256     */
34257     clearAll : function()
34258     {
34259         this.bricks = [];
34260         //this.getChildContainer().dom.innerHTML = "";
34261         this.el.dom.innerHTML = '';
34262     },
34263     
34264     getSelected : function()
34265     {
34266         if (!this.selectedBrick) {
34267             return false;
34268         }
34269         
34270         return this.selectedBrick;
34271     }
34272 });
34273
34274 Roo.apply(Roo.bootstrap.LayoutMasonry, {
34275     
34276     groups: {},
34277      /**
34278     * register a Masonry Layout
34279     * @param {Roo.bootstrap.LayoutMasonry} the masonry layout to add
34280     */
34281     
34282     register : function(layout)
34283     {
34284         this.groups[layout.id] = layout;
34285     },
34286     /**
34287     * fetch a  Masonry Layout based on the masonry layout ID
34288     * @param {string} the masonry layout to add
34289     * @returns {Roo.bootstrap.LayoutMasonry} the masonry layout
34290     */
34291     
34292     get: function(layout_id) {
34293         if (typeof(this.groups[layout_id]) == 'undefined') {
34294             return false;
34295         }
34296         return this.groups[layout_id] ;
34297     }
34298     
34299     
34300     
34301 });
34302
34303  
34304
34305  /**
34306  *
34307  * This is based on 
34308  * http://masonry.desandro.com
34309  *
34310  * The idea is to render all the bricks based on vertical width...
34311  *
34312  * The original code extends 'outlayer' - we might need to use that....
34313  * 
34314  */
34315
34316
34317 /**
34318  * @class Roo.bootstrap.LayoutMasonryAuto
34319  * @extends Roo.bootstrap.Component
34320  * Bootstrap Layout Masonry class
34321  * 
34322  * @constructor
34323  * Create a new Element
34324  * @param {Object} config The config object
34325  */
34326
34327 Roo.bootstrap.LayoutMasonryAuto = function(config){
34328     Roo.bootstrap.LayoutMasonryAuto.superclass.constructor.call(this, config);
34329 };
34330
34331 Roo.extend(Roo.bootstrap.LayoutMasonryAuto, Roo.bootstrap.Component,  {
34332     
34333       /**
34334      * @cfg {Boolean} isFitWidth  - resize the width..
34335      */   
34336     isFitWidth : false,  // options..
34337     /**
34338      * @cfg {Boolean} isOriginLeft = left align?
34339      */   
34340     isOriginLeft : true,
34341     /**
34342      * @cfg {Boolean} isOriginTop = top align?
34343      */   
34344     isOriginTop : false,
34345     /**
34346      * @cfg {Boolean} isLayoutInstant = no animation?
34347      */   
34348     isLayoutInstant : false, // needed?
34349     /**
34350      * @cfg {Boolean} isResizingContainer = not sure if this is used..
34351      */   
34352     isResizingContainer : true,
34353     /**
34354      * @cfg {Number} columnWidth  width of the columns 
34355      */   
34356     
34357     columnWidth : 0,
34358     
34359     /**
34360      * @cfg {Number} maxCols maximum number of columns
34361      */   
34362     
34363     maxCols: 0,
34364     /**
34365      * @cfg {Number} padHeight padding below box..
34366      */   
34367     
34368     padHeight : 10, 
34369     
34370     /**
34371      * @cfg {Boolean} isAutoInitial defalut true
34372      */   
34373     
34374     isAutoInitial : true, 
34375     
34376     // private?
34377     gutter : 0,
34378     
34379     containerWidth: 0,
34380     initialColumnWidth : 0,
34381     currentSize : null,
34382     
34383     colYs : null, // array.
34384     maxY : 0,
34385     padWidth: 10,
34386     
34387     
34388     tag: 'div',
34389     cls: '',
34390     bricks: null, //CompositeElement
34391     cols : 0, // array?
34392     // element : null, // wrapped now this.el
34393     _isLayoutInited : null, 
34394     
34395     
34396     getAutoCreate : function(){
34397         
34398         var cfg = {
34399             tag: this.tag,
34400             cls: 'blog-masonary-wrapper ' + this.cls,
34401             cn : {
34402                 cls : 'mas-boxes masonary'
34403             }
34404         };
34405         
34406         return cfg;
34407     },
34408     
34409     getChildContainer: function( )
34410     {
34411         if (this.boxesEl) {
34412             return this.boxesEl;
34413         }
34414         
34415         this.boxesEl = this.el.select('.mas-boxes').first();
34416         
34417         return this.boxesEl;
34418     },
34419     
34420     
34421     initEvents : function()
34422     {
34423         var _this = this;
34424         
34425         if(this.isAutoInitial){
34426             Roo.log('hook children rendered');
34427             this.on('childrenrendered', function() {
34428                 Roo.log('children rendered');
34429                 _this.initial();
34430             } ,this);
34431         }
34432         
34433     },
34434     
34435     initial : function()
34436     {
34437         this.reloadItems();
34438
34439         this.currentSize = this.el.getBox(true);
34440
34441         /// was window resize... - let's see if this works..
34442         Roo.EventManager.onWindowResize(this.resize, this); 
34443
34444         if(!this.isAutoInitial){
34445             this.layout();
34446             return;
34447         }
34448         
34449         this.layout.defer(500,this);
34450     },
34451     
34452     reloadItems: function()
34453     {
34454         this.bricks = this.el.select('.masonry-brick', true);
34455         
34456         this.bricks.each(function(b) {
34457             //Roo.log(b.getSize());
34458             if (!b.attr('originalwidth')) {
34459                 b.attr('originalwidth',  b.getSize().width);
34460             }
34461             
34462         });
34463         
34464         Roo.log(this.bricks.elements.length);
34465     },
34466     
34467     resize : function()
34468     {
34469         Roo.log('resize');
34470         var cs = this.el.getBox(true);
34471         
34472         if (this.currentSize.width == cs.width && this.currentSize.x == cs.x ) {
34473             Roo.log("no change in with or X");
34474             return;
34475         }
34476         this.currentSize = cs;
34477         this.layout();
34478     },
34479     
34480     layout : function()
34481     {
34482          Roo.log('layout');
34483         this._resetLayout();
34484         //this._manageStamps();
34485       
34486         // don't animate first layout
34487         var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
34488         this.layoutItems( isInstant );
34489       
34490         // flag for initalized
34491         this._isLayoutInited = true;
34492     },
34493     
34494     layoutItems : function( isInstant )
34495     {
34496         //var items = this._getItemsForLayout( this.items );
34497         // original code supports filtering layout items.. we just ignore it..
34498         
34499         this._layoutItems( this.bricks , isInstant );
34500       
34501         this._postLayout();
34502     },
34503     _layoutItems : function ( items , isInstant)
34504     {
34505        //this.fireEvent( 'layout', this, items );
34506     
34507
34508         if ( !items || !items.elements.length ) {
34509           // no items, emit event with empty array
34510             return;
34511         }
34512
34513         var queue = [];
34514         items.each(function(item) {
34515             Roo.log("layout item");
34516             Roo.log(item);
34517             // get x/y object from method
34518             var position = this._getItemLayoutPosition( item );
34519             // enqueue
34520             position.item = item;
34521             position.isInstant = isInstant; // || item.isLayoutInstant; << not set yet...
34522             queue.push( position );
34523         }, this);
34524       
34525         this._processLayoutQueue( queue );
34526     },
34527     /** Sets position of item in DOM
34528     * @param {Element} item
34529     * @param {Number} x - horizontal position
34530     * @param {Number} y - vertical position
34531     * @param {Boolean} isInstant - disables transitions
34532     */
34533     _processLayoutQueue : function( queue )
34534     {
34535         for ( var i=0, len = queue.length; i < len; i++ ) {
34536             var obj = queue[i];
34537             obj.item.position('absolute');
34538             obj.item.setXY([obj.x,obj.y], obj.isInstant ? false : true);
34539         }
34540     },
34541       
34542     
34543     /**
34544     * Any logic you want to do after each layout,
34545     * i.e. size the container
34546     */
34547     _postLayout : function()
34548     {
34549         this.resizeContainer();
34550     },
34551     
34552     resizeContainer : function()
34553     {
34554         if ( !this.isResizingContainer ) {
34555             return;
34556         }
34557         var size = this._getContainerSize();
34558         if ( size ) {
34559             this.el.setSize(size.width,size.height);
34560             this.boxesEl.setSize(size.width,size.height);
34561         }
34562     },
34563     
34564     
34565     
34566     _resetLayout : function()
34567     {
34568         //this.getSize();  // -- does not really do anything.. it probably applies left/right etc. to obuject but not used
34569         this.colWidth = this.el.getWidth();
34570         //this.gutter = this.el.getWidth(); 
34571         
34572         this.measureColumns();
34573
34574         // reset column Y
34575         var i = this.cols;
34576         this.colYs = [];
34577         while (i--) {
34578             this.colYs.push( 0 );
34579         }
34580     
34581         this.maxY = 0;
34582     },
34583
34584     measureColumns : function()
34585     {
34586         this.getContainerWidth();
34587       // if columnWidth is 0, default to outerWidth of first item
34588         if ( !this.columnWidth ) {
34589             var firstItem = this.bricks.first();
34590             Roo.log(firstItem);
34591             this.columnWidth  = this.containerWidth;
34592             if (firstItem && firstItem.attr('originalwidth') ) {
34593                 this.columnWidth = 1* (firstItem.attr('originalwidth') || firstItem.getWidth());
34594             }
34595             // columnWidth fall back to item of first element
34596             Roo.log("set column width?");
34597                         this.initialColumnWidth = this.columnWidth  ;
34598
34599             // if first elem has no width, default to size of container
34600             
34601         }
34602         
34603         
34604         if (this.initialColumnWidth) {
34605             this.columnWidth = this.initialColumnWidth;
34606         }
34607         
34608         
34609             
34610         // column width is fixed at the top - however if container width get's smaller we should
34611         // reduce it...
34612         
34613         // this bit calcs how man columns..
34614             
34615         var columnWidth = this.columnWidth += this.gutter;
34616       
34617         // calculate columns
34618         var containerWidth = this.containerWidth + this.gutter;
34619         
34620         var cols = (containerWidth - this.padWidth) / (columnWidth - this.padWidth);
34621         // fix rounding errors, typically with gutters
34622         var excess = columnWidth - containerWidth % columnWidth;
34623         
34624         
34625         // if overshoot is less than a pixel, round up, otherwise floor it
34626         var mathMethod = excess && excess < 1 ? 'round' : 'floor';
34627         cols = Math[ mathMethod ]( cols );
34628         this.cols = Math.max( cols, 1 );
34629         this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
34630         
34631          // padding positioning..
34632         var totalColWidth = this.cols * this.columnWidth;
34633         var padavail = this.containerWidth - totalColWidth;
34634         // so for 2 columns - we need 3 'pads'
34635         
34636         var padNeeded = (1+this.cols) * this.padWidth;
34637         
34638         var padExtra = Math.floor((padavail - padNeeded) / this.cols);
34639         
34640         this.columnWidth += padExtra
34641         //this.padWidth = Math.floor(padavail /  ( this.cols));
34642         
34643         // adjust colum width so that padding is fixed??
34644         
34645         // we have 3 columns ... total = width * 3
34646         // we have X left over... that should be used by 
34647         
34648         //if (this.expandC) {
34649             
34650         //}
34651         
34652         
34653         
34654     },
34655     
34656     getContainerWidth : function()
34657     {
34658        /* // container is parent if fit width
34659         var container = this.isFitWidth ? this.element.parentNode : this.element;
34660         // check that this.size and size are there
34661         // IE8 triggers resize on body size change, so they might not be
34662         
34663         var size = getSize( container );  //FIXME
34664         this.containerWidth = size && size.innerWidth; //FIXME
34665         */
34666          
34667         this.containerWidth = this.el.getBox(true).width;  //maybe use getComputedWidth
34668         
34669     },
34670     
34671     _getItemLayoutPosition : function( item )  // what is item?
34672     {
34673         // we resize the item to our columnWidth..
34674       
34675         item.setWidth(this.columnWidth);
34676         item.autoBoxAdjust  = false;
34677         
34678         var sz = item.getSize();
34679  
34680         // how many columns does this brick span
34681         var remainder = this.containerWidth % this.columnWidth;
34682         
34683         var mathMethod = remainder && remainder < 1 ? 'round' : 'ceil';
34684         // round if off by 1 pixel, otherwise use ceil
34685         var colSpan = Math[ mathMethod ]( sz.width  / this.columnWidth );
34686         colSpan = Math.min( colSpan, this.cols );
34687         
34688         // normally this should be '1' as we dont' currently allow multi width columns..
34689         
34690         var colGroup = this._getColGroup( colSpan );
34691         // get the minimum Y value from the columns
34692         var minimumY = Math.min.apply( Math, colGroup );
34693         Roo.log([ 'setHeight',  minimumY, sz.height, setHeight ]);
34694         
34695         var shortColIndex = colGroup.indexOf(  minimumY ); // broken on ie8..?? probably...
34696          
34697         // position the brick
34698         var position = {
34699             x: this.currentSize.x + (this.padWidth /2) + ((this.columnWidth + this.padWidth )* shortColIndex),
34700             y: this.currentSize.y + minimumY + this.padHeight
34701         };
34702         
34703         Roo.log(position);
34704         // apply setHeight to necessary columns
34705         var setHeight = minimumY + sz.height + this.padHeight;
34706         //Roo.log([ 'setHeight',  minimumY, sz.height, setHeight ]);
34707         
34708         var setSpan = this.cols + 1 - colGroup.length;
34709         for ( var i = 0; i < setSpan; i++ ) {
34710           this.colYs[ shortColIndex + i ] = setHeight ;
34711         }
34712       
34713         return position;
34714     },
34715     
34716     /**
34717      * @param {Number} colSpan - number of columns the element spans
34718      * @returns {Array} colGroup
34719      */
34720     _getColGroup : function( colSpan )
34721     {
34722         if ( colSpan < 2 ) {
34723           // if brick spans only one column, use all the column Ys
34724           return this.colYs;
34725         }
34726       
34727         var colGroup = [];
34728         // how many different places could this brick fit horizontally
34729         var groupCount = this.cols + 1 - colSpan;
34730         // for each group potential horizontal position
34731         for ( var i = 0; i < groupCount; i++ ) {
34732           // make an array of colY values for that one group
34733           var groupColYs = this.colYs.slice( i, i + colSpan );
34734           // and get the max value of the array
34735           colGroup[i] = Math.max.apply( Math, groupColYs );
34736         }
34737         return colGroup;
34738     },
34739     /*
34740     _manageStamp : function( stamp )
34741     {
34742         var stampSize =  stamp.getSize();
34743         var offset = stamp.getBox();
34744         // get the columns that this stamp affects
34745         var firstX = this.isOriginLeft ? offset.x : offset.right;
34746         var lastX = firstX + stampSize.width;
34747         var firstCol = Math.floor( firstX / this.columnWidth );
34748         firstCol = Math.max( 0, firstCol );
34749         
34750         var lastCol = Math.floor( lastX / this.columnWidth );
34751         // lastCol should not go over if multiple of columnWidth #425
34752         lastCol -= lastX % this.columnWidth ? 0 : 1;
34753         lastCol = Math.min( this.cols - 1, lastCol );
34754         
34755         // set colYs to bottom of the stamp
34756         var stampMaxY = ( this.isOriginTop ? offset.y : offset.bottom ) +
34757             stampSize.height;
34758             
34759         for ( var i = firstCol; i <= lastCol; i++ ) {
34760           this.colYs[i] = Math.max( stampMaxY, this.colYs[i] );
34761         }
34762     },
34763     */
34764     
34765     _getContainerSize : function()
34766     {
34767         this.maxY = Math.max.apply( Math, this.colYs );
34768         var size = {
34769             height: this.maxY
34770         };
34771       
34772         if ( this.isFitWidth ) {
34773             size.width = this._getContainerFitWidth();
34774         }
34775       
34776         return size;
34777     },
34778     
34779     _getContainerFitWidth : function()
34780     {
34781         var unusedCols = 0;
34782         // count unused columns
34783         var i = this.cols;
34784         while ( --i ) {
34785           if ( this.colYs[i] !== 0 ) {
34786             break;
34787           }
34788           unusedCols++;
34789         }
34790         // fit container to columns that have been used
34791         return ( this.cols - unusedCols ) * this.columnWidth - this.gutter;
34792     },
34793     
34794     needsResizeLayout : function()
34795     {
34796         var previousWidth = this.containerWidth;
34797         this.getContainerWidth();
34798         return previousWidth !== this.containerWidth;
34799     }
34800  
34801 });
34802
34803  
34804
34805  /*
34806  * - LGPL
34807  *
34808  * element
34809  * 
34810  */
34811
34812 /**
34813  * @class Roo.bootstrap.MasonryBrick
34814  * @extends Roo.bootstrap.Component
34815  * Bootstrap MasonryBrick class
34816  * 
34817  * @constructor
34818  * Create a new MasonryBrick
34819  * @param {Object} config The config object
34820  */
34821
34822 Roo.bootstrap.MasonryBrick = function(config){
34823     
34824     Roo.bootstrap.MasonryBrick.superclass.constructor.call(this, config);
34825     
34826     Roo.bootstrap.MasonryBrick.register(this);
34827     
34828     this.addEvents({
34829         // raw events
34830         /**
34831          * @event click
34832          * When a MasonryBrick is clcik
34833          * @param {Roo.bootstrap.MasonryBrick} this
34834          * @param {Roo.EventObject} e
34835          */
34836         "click" : true
34837     });
34838 };
34839
34840 Roo.extend(Roo.bootstrap.MasonryBrick, Roo.bootstrap.Component,  {
34841     
34842     /**
34843      * @cfg {String} title
34844      */   
34845     title : '',
34846     /**
34847      * @cfg {String} html
34848      */   
34849     html : '',
34850     /**
34851      * @cfg {String} bgimage
34852      */   
34853     bgimage : '',
34854     /**
34855      * @cfg {String} videourl
34856      */   
34857     videourl : '',
34858     /**
34859      * @cfg {String} cls
34860      */   
34861     cls : '',
34862     /**
34863      * @cfg {String} href
34864      */   
34865     href : '',
34866     /**
34867      * @cfg {String} size (xs|sm|md|md-left|md-right|tall|wide)
34868      */   
34869     size : 'xs',
34870     
34871     /**
34872      * @cfg {String} placetitle (center|bottom)
34873      */   
34874     placetitle : '',
34875     
34876     /**
34877      * @cfg {Boolean} isFitContainer defalut true
34878      */   
34879     isFitContainer : true, 
34880     
34881     /**
34882      * @cfg {Boolean} preventDefault defalut false
34883      */   
34884     preventDefault : false, 
34885     
34886     /**
34887      * @cfg {Boolean} inverse defalut false
34888      */   
34889     maskInverse : false, 
34890     
34891     getAutoCreate : function()
34892     {
34893         if(!this.isFitContainer){
34894             return this.getSplitAutoCreate();
34895         }
34896         
34897         var cls = 'masonry-brick masonry-brick-full';
34898         
34899         if(this.href.length){
34900             cls += ' masonry-brick-link';
34901         }
34902         
34903         if(this.bgimage.length){
34904             cls += ' masonry-brick-image';
34905         }
34906         
34907         if(this.maskInverse){
34908             cls += ' mask-inverse';
34909         }
34910         
34911         if(!this.html.length && !this.maskInverse && !this.videourl.length){
34912             cls += ' enable-mask';
34913         }
34914         
34915         if(this.size){
34916             cls += ' masonry-' + this.size + '-brick';
34917         }
34918         
34919         if(this.placetitle.length){
34920             
34921             switch (this.placetitle) {
34922                 case 'center' :
34923                     cls += ' masonry-center-title';
34924                     break;
34925                 case 'bottom' :
34926                     cls += ' masonry-bottom-title';
34927                     break;
34928                 default:
34929                     break;
34930             }
34931             
34932         } else {
34933             if(!this.html.length && !this.bgimage.length){
34934                 cls += ' masonry-center-title';
34935             }
34936
34937             if(!this.html.length && this.bgimage.length){
34938                 cls += ' masonry-bottom-title';
34939             }
34940         }
34941         
34942         if(this.cls){
34943             cls += ' ' + this.cls;
34944         }
34945         
34946         var cfg = {
34947             tag: (this.href.length) ? 'a' : 'div',
34948             cls: cls,
34949             cn: [
34950                 {
34951                     tag: 'div',
34952                     cls: 'masonry-brick-mask'
34953                 },
34954                 {
34955                     tag: 'div',
34956                     cls: 'masonry-brick-paragraph',
34957                     cn: []
34958                 }
34959             ]
34960         };
34961         
34962         if(this.href.length){
34963             cfg.href = this.href;
34964         }
34965         
34966         var cn = cfg.cn[1].cn;
34967         
34968         if(this.title.length){
34969             cn.push({
34970                 tag: 'h4',
34971                 cls: 'masonry-brick-title',
34972                 html: this.title
34973             });
34974         }
34975         
34976         if(this.html.length){
34977             cn.push({
34978                 tag: 'p',
34979                 cls: 'masonry-brick-text',
34980                 html: this.html
34981             });
34982         }
34983         
34984         if (!this.title.length && !this.html.length) {
34985             cfg.cn[1].cls += ' hide';
34986         }
34987         
34988         if(this.bgimage.length){
34989             cfg.cn.push({
34990                 tag: 'img',
34991                 cls: 'masonry-brick-image-view',
34992                 src: this.bgimage
34993             });
34994         }
34995         
34996         if(this.videourl.length){
34997             var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
34998             // youtube support only?
34999             cfg.cn.push({
35000                 tag: 'iframe',
35001                 cls: 'masonry-brick-image-view',
35002                 src: vurl,
35003                 frameborder : 0,
35004                 allowfullscreen : true
35005             });
35006         }
35007         
35008         return cfg;
35009         
35010     },
35011     
35012     getSplitAutoCreate : function()
35013     {
35014         var cls = 'masonry-brick masonry-brick-split';
35015         
35016         if(this.href.length){
35017             cls += ' masonry-brick-link';
35018         }
35019         
35020         if(this.bgimage.length){
35021             cls += ' masonry-brick-image';
35022         }
35023         
35024         if(this.size){
35025             cls += ' masonry-' + this.size + '-brick';
35026         }
35027         
35028         switch (this.placetitle) {
35029             case 'center' :
35030                 cls += ' masonry-center-title';
35031                 break;
35032             case 'bottom' :
35033                 cls += ' masonry-bottom-title';
35034                 break;
35035             default:
35036                 if(!this.bgimage.length){
35037                     cls += ' masonry-center-title';
35038                 }
35039
35040                 if(this.bgimage.length){
35041                     cls += ' masonry-bottom-title';
35042                 }
35043                 break;
35044         }
35045         
35046         if(this.cls){
35047             cls += ' ' + this.cls;
35048         }
35049         
35050         var cfg = {
35051             tag: (this.href.length) ? 'a' : 'div',
35052             cls: cls,
35053             cn: [
35054                 {
35055                     tag: 'div',
35056                     cls: 'masonry-brick-split-head',
35057                     cn: [
35058                         {
35059                             tag: 'div',
35060                             cls: 'masonry-brick-paragraph',
35061                             cn: []
35062                         }
35063                     ]
35064                 },
35065                 {
35066                     tag: 'div',
35067                     cls: 'masonry-brick-split-body',
35068                     cn: []
35069                 }
35070             ]
35071         };
35072         
35073         if(this.href.length){
35074             cfg.href = this.href;
35075         }
35076         
35077         if(this.title.length){
35078             cfg.cn[0].cn[0].cn.push({
35079                 tag: 'h4',
35080                 cls: 'masonry-brick-title',
35081                 html: this.title
35082             });
35083         }
35084         
35085         if(this.html.length){
35086             cfg.cn[1].cn.push({
35087                 tag: 'p',
35088                 cls: 'masonry-brick-text',
35089                 html: this.html
35090             });
35091         }
35092
35093         if(this.bgimage.length){
35094             cfg.cn[0].cn.push({
35095                 tag: 'img',
35096                 cls: 'masonry-brick-image-view',
35097                 src: this.bgimage
35098             });
35099         }
35100         
35101         if(this.videourl.length){
35102             var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
35103             // youtube support only?
35104             cfg.cn[0].cn.cn.push({
35105                 tag: 'iframe',
35106                 cls: 'masonry-brick-image-view',
35107                 src: vurl,
35108                 frameborder : 0,
35109                 allowfullscreen : true
35110             });
35111         }
35112         
35113         return cfg;
35114     },
35115     
35116     initEvents: function() 
35117     {
35118         switch (this.size) {
35119             case 'xs' :
35120                 this.x = 1;
35121                 this.y = 1;
35122                 break;
35123             case 'sm' :
35124                 this.x = 2;
35125                 this.y = 2;
35126                 break;
35127             case 'md' :
35128             case 'md-left' :
35129             case 'md-right' :
35130                 this.x = 3;
35131                 this.y = 3;
35132                 break;
35133             case 'tall' :
35134                 this.x = 2;
35135                 this.y = 3;
35136                 break;
35137             case 'wide' :
35138                 this.x = 3;
35139                 this.y = 2;
35140                 break;
35141             case 'wide-thin' :
35142                 this.x = 3;
35143                 this.y = 1;
35144                 break;
35145                         
35146             default :
35147                 break;
35148         }
35149         
35150         if(Roo.isTouch){
35151             this.el.on('touchstart', this.onTouchStart, this);
35152             this.el.on('touchmove', this.onTouchMove, this);
35153             this.el.on('touchend', this.onTouchEnd, this);
35154             this.el.on('contextmenu', this.onContextMenu, this);
35155         } else {
35156             this.el.on('mouseenter'  ,this.enter, this);
35157             this.el.on('mouseleave', this.leave, this);
35158             this.el.on('click', this.onClick, this);
35159         }
35160         
35161         if (typeof(this.parent().bricks) == 'object' && this.parent().bricks != null) {
35162             this.parent().bricks.push(this);   
35163         }
35164         
35165     },
35166     
35167     onClick: function(e, el)
35168     {
35169         var time = this.endTimer - this.startTimer;
35170         // Roo.log(e.preventDefault());
35171         if(Roo.isTouch){
35172             if(time > 1000){
35173                 e.preventDefault();
35174                 return;
35175             }
35176         }
35177         
35178         if(!this.preventDefault){
35179             return;
35180         }
35181         
35182         e.preventDefault();
35183         
35184         if (this.activeClass != '') {
35185             this.selectBrick();
35186         }
35187         
35188         this.fireEvent('click', this, e);
35189     },
35190     
35191     enter: function(e, el)
35192     {
35193         e.preventDefault();
35194         
35195         if(!this.isFitContainer || this.maskInverse || this.videourl.length){
35196             return;
35197         }
35198         
35199         if(this.bgimage.length && this.html.length){
35200             this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
35201         }
35202     },
35203     
35204     leave: function(e, el)
35205     {
35206         e.preventDefault();
35207         
35208         if(!this.isFitContainer || this.maskInverse  || this.videourl.length){
35209             return;
35210         }
35211         
35212         if(this.bgimage.length && this.html.length){
35213             this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
35214         }
35215     },
35216     
35217     onTouchStart: function(e, el)
35218     {
35219 //        e.preventDefault();
35220         
35221         this.touchmoved = false;
35222         
35223         if(!this.isFitContainer){
35224             return;
35225         }
35226         
35227         if(!this.bgimage.length || !this.html.length){
35228             return;
35229         }
35230         
35231         this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
35232         
35233         this.timer = new Date().getTime();
35234         
35235     },
35236     
35237     onTouchMove: function(e, el)
35238     {
35239         this.touchmoved = true;
35240     },
35241     
35242     onContextMenu : function(e,el)
35243     {
35244         e.preventDefault();
35245         e.stopPropagation();
35246         return false;
35247     },
35248     
35249     onTouchEnd: function(e, el)
35250     {
35251 //        e.preventDefault();
35252         
35253         if((new Date().getTime() - this.timer > 1000) || !this.href.length || this.touchmoved){
35254         
35255             this.leave(e,el);
35256             
35257             return;
35258         }
35259         
35260         if(!this.bgimage.length || !this.html.length){
35261             
35262             if(this.href.length){
35263                 window.location.href = this.href;
35264             }
35265             
35266             return;
35267         }
35268         
35269         if(!this.isFitContainer){
35270             return;
35271         }
35272         
35273         this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
35274         
35275         window.location.href = this.href;
35276     },
35277     
35278     //selection on single brick only
35279     selectBrick : function() {
35280         
35281         if (!this.parentId) {
35282             return;
35283         }
35284         
35285         var m = Roo.bootstrap.LayoutMasonry.get(this.parentId);
35286         var index = m.selectedBrick.indexOf(this.id);
35287         
35288         if ( index > -1) {
35289             m.selectedBrick.splice(index,1);
35290             this.el.removeClass(this.activeClass);
35291             return;
35292         }
35293         
35294         for(var i = 0; i < m.selectedBrick.length; i++) {
35295             var b = Roo.bootstrap.MasonryBrick.get(m.selectedBrick[i]);
35296             b.el.removeClass(b.activeClass);
35297         }
35298         
35299         m.selectedBrick = [];
35300         
35301         m.selectedBrick.push(this.id);
35302         this.el.addClass(this.activeClass);
35303         return;
35304     },
35305     
35306     isSelected : function(){
35307         return this.el.hasClass(this.activeClass);
35308         
35309     }
35310 });
35311
35312 Roo.apply(Roo.bootstrap.MasonryBrick, {
35313     
35314     //groups: {},
35315     groups : new Roo.util.MixedCollection(false, function(o) { return o.el.id; }),
35316      /**
35317     * register a Masonry Brick
35318     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
35319     */
35320     
35321     register : function(brick)
35322     {
35323         //this.groups[brick.id] = brick;
35324         this.groups.add(brick.id, brick);
35325     },
35326     /**
35327     * fetch a  masonry brick based on the masonry brick ID
35328     * @param {string} the masonry brick to add
35329     * @returns {Roo.bootstrap.MasonryBrick} the masonry brick
35330     */
35331     
35332     get: function(brick_id) 
35333     {
35334         // if (typeof(this.groups[brick_id]) == 'undefined') {
35335         //     return false;
35336         // }
35337         // return this.groups[brick_id] ;
35338         
35339         if(this.groups.key(brick_id)) {
35340             return this.groups.key(brick_id);
35341         }
35342         
35343         return false;
35344     }
35345     
35346     
35347     
35348 });
35349
35350  /*
35351  * - LGPL
35352  *
35353  * element
35354  * 
35355  */
35356
35357 /**
35358  * @class Roo.bootstrap.Brick
35359  * @extends Roo.bootstrap.Component
35360  * Bootstrap Brick class
35361  * 
35362  * @constructor
35363  * Create a new Brick
35364  * @param {Object} config The config object
35365  */
35366
35367 Roo.bootstrap.Brick = function(config){
35368     Roo.bootstrap.Brick.superclass.constructor.call(this, config);
35369     
35370     this.addEvents({
35371         // raw events
35372         /**
35373          * @event click
35374          * When a Brick is click
35375          * @param {Roo.bootstrap.Brick} this
35376          * @param {Roo.EventObject} e
35377          */
35378         "click" : true
35379     });
35380 };
35381
35382 Roo.extend(Roo.bootstrap.Brick, Roo.bootstrap.Component,  {
35383     
35384     /**
35385      * @cfg {String} title
35386      */   
35387     title : '',
35388     /**
35389      * @cfg {String} html
35390      */   
35391     html : '',
35392     /**
35393      * @cfg {String} bgimage
35394      */   
35395     bgimage : '',
35396     /**
35397      * @cfg {String} cls
35398      */   
35399     cls : '',
35400     /**
35401      * @cfg {String} href
35402      */   
35403     href : '',
35404     /**
35405      * @cfg {String} video
35406      */   
35407     video : '',
35408     /**
35409      * @cfg {Boolean} square
35410      */   
35411     square : true,
35412     
35413     getAutoCreate : function()
35414     {
35415         var cls = 'roo-brick';
35416         
35417         if(this.href.length){
35418             cls += ' roo-brick-link';
35419         }
35420         
35421         if(this.bgimage.length){
35422             cls += ' roo-brick-image';
35423         }
35424         
35425         if(!this.html.length && !this.bgimage.length){
35426             cls += ' roo-brick-center-title';
35427         }
35428         
35429         if(!this.html.length && this.bgimage.length){
35430             cls += ' roo-brick-bottom-title';
35431         }
35432         
35433         if(this.cls){
35434             cls += ' ' + this.cls;
35435         }
35436         
35437         var cfg = {
35438             tag: (this.href.length) ? 'a' : 'div',
35439             cls: cls,
35440             cn: [
35441                 {
35442                     tag: 'div',
35443                     cls: 'roo-brick-paragraph',
35444                     cn: []
35445                 }
35446             ]
35447         };
35448         
35449         if(this.href.length){
35450             cfg.href = this.href;
35451         }
35452         
35453         var cn = cfg.cn[0].cn;
35454         
35455         if(this.title.length){
35456             cn.push({
35457                 tag: 'h4',
35458                 cls: 'roo-brick-title',
35459                 html: this.title
35460             });
35461         }
35462         
35463         if(this.html.length){
35464             cn.push({
35465                 tag: 'p',
35466                 cls: 'roo-brick-text',
35467                 html: this.html
35468             });
35469         } else {
35470             cn.cls += ' hide';
35471         }
35472         
35473         if(this.bgimage.length){
35474             cfg.cn.push({
35475                 tag: 'img',
35476                 cls: 'roo-brick-image-view',
35477                 src: this.bgimage
35478             });
35479         }
35480         
35481         return cfg;
35482     },
35483     
35484     initEvents: function() 
35485     {
35486         if(this.title.length || this.html.length){
35487             this.el.on('mouseenter'  ,this.enter, this);
35488             this.el.on('mouseleave', this.leave, this);
35489         }
35490         
35491         Roo.EventManager.onWindowResize(this.resize, this); 
35492         
35493         if(this.bgimage.length){
35494             this.imageEl = this.el.select('.roo-brick-image-view', true).first();
35495             this.imageEl.on('load', this.onImageLoad, this);
35496             return;
35497         }
35498         
35499         this.resize();
35500     },
35501     
35502     onImageLoad : function()
35503     {
35504         this.resize();
35505     },
35506     
35507     resize : function()
35508     {
35509         var paragraph = this.el.select('.roo-brick-paragraph', true).first();
35510         
35511         paragraph.setHeight(paragraph.getWidth() + paragraph.getPadding('tb'));
35512         
35513         if(this.bgimage.length){
35514             var image = this.el.select('.roo-brick-image-view', true).first();
35515             
35516             image.setWidth(paragraph.getWidth());
35517             
35518             if(this.square){
35519                 image.setHeight(paragraph.getWidth());
35520             }
35521             
35522             this.el.setHeight(image.getHeight());
35523             paragraph.setHeight(image.getHeight());
35524             
35525         }
35526         
35527     },
35528     
35529     enter: function(e, el)
35530     {
35531         e.preventDefault();
35532         
35533         if(this.bgimage.length){
35534             this.el.select('.roo-brick-paragraph', true).first().setOpacity(0.9, true);
35535             this.el.select('.roo-brick-image-view', true).first().setOpacity(0.1, true);
35536         }
35537     },
35538     
35539     leave: function(e, el)
35540     {
35541         e.preventDefault();
35542         
35543         if(this.bgimage.length){
35544             this.el.select('.roo-brick-paragraph', true).first().setOpacity(0, true);
35545             this.el.select('.roo-brick-image-view', true).first().setOpacity(1, true);
35546         }
35547     }
35548     
35549 });
35550
35551  
35552
35553  /*
35554  * - LGPL
35555  *
35556  * Number field 
35557  */
35558
35559 /**
35560  * @class Roo.bootstrap.NumberField
35561  * @extends Roo.bootstrap.Input
35562  * Bootstrap NumberField class
35563  * 
35564  * 
35565  * 
35566  * 
35567  * @constructor
35568  * Create a new NumberField
35569  * @param {Object} config The config object
35570  */
35571
35572 Roo.bootstrap.NumberField = function(config){
35573     Roo.bootstrap.NumberField.superclass.constructor.call(this, config);
35574 };
35575
35576 Roo.extend(Roo.bootstrap.NumberField, Roo.bootstrap.Input, {
35577     
35578     /**
35579      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
35580      */
35581     allowDecimals : true,
35582     /**
35583      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
35584      */
35585     decimalSeparator : ".",
35586     /**
35587      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
35588      */
35589     decimalPrecision : 2,
35590     /**
35591      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
35592      */
35593     allowNegative : true,
35594     
35595     /**
35596      * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
35597      */
35598     allowZero: true,
35599     /**
35600      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
35601      */
35602     minValue : Number.NEGATIVE_INFINITY,
35603     /**
35604      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
35605      */
35606     maxValue : Number.MAX_VALUE,
35607     /**
35608      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
35609      */
35610     minText : "The minimum value for this field is {0}",
35611     /**
35612      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
35613      */
35614     maxText : "The maximum value for this field is {0}",
35615     /**
35616      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
35617      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
35618      */
35619     nanText : "{0} is not a valid number",
35620     /**
35621      * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
35622      */
35623     thousandsDelimiter : false,
35624     /**
35625      * @cfg {String} valueAlign alignment of value
35626      */
35627     valueAlign : "left",
35628
35629     getAutoCreate : function()
35630     {
35631         var hiddenInput = {
35632             tag: 'input',
35633             type: 'hidden',
35634             id: Roo.id(),
35635             cls: 'hidden-number-input'
35636         };
35637         
35638         if (this.name) {
35639             hiddenInput.name = this.name;
35640         }
35641         
35642         this.name = '';
35643         
35644         var cfg = Roo.bootstrap.NumberField.superclass.getAutoCreate.call(this);
35645         
35646         this.name = hiddenInput.name;
35647         
35648         if(cfg.cn.length > 0) {
35649             cfg.cn.push(hiddenInput);
35650         }
35651         
35652         return cfg;
35653     },
35654
35655     // private
35656     initEvents : function()
35657     {   
35658         Roo.bootstrap.NumberField.superclass.initEvents.call(this);
35659         
35660         var allowed = "0123456789";
35661         
35662         if(this.allowDecimals){
35663             allowed += this.decimalSeparator;
35664         }
35665         
35666         if(this.allowNegative){
35667             allowed += "-";
35668         }
35669         
35670         if(this.thousandsDelimiter) {
35671             allowed += ",";
35672         }
35673         
35674         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
35675         
35676         var keyPress = function(e){
35677             
35678             var k = e.getKey();
35679             
35680             var c = e.getCharCode();
35681             
35682             if(
35683                     (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
35684                     allowed.indexOf(String.fromCharCode(c)) === -1
35685             ){
35686                 e.stopEvent();
35687                 return;
35688             }
35689             
35690             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
35691                 return;
35692             }
35693             
35694             if(allowed.indexOf(String.fromCharCode(c)) === -1){
35695                 e.stopEvent();
35696             }
35697         };
35698         
35699         this.el.on("keypress", keyPress, this);
35700     },
35701     
35702     validateValue : function(value)
35703     {
35704         
35705         if(!Roo.bootstrap.NumberField.superclass.validateValue.call(this, value)){
35706             return false;
35707         }
35708         
35709         var num = this.parseValue(value);
35710         
35711         if(isNaN(num)){
35712             this.markInvalid(String.format(this.nanText, value));
35713             return false;
35714         }
35715         
35716         if(num < this.minValue){
35717             this.markInvalid(String.format(this.minText, this.minValue));
35718             return false;
35719         }
35720         
35721         if(num > this.maxValue){
35722             this.markInvalid(String.format(this.maxText, this.maxValue));
35723             return false;
35724         }
35725         
35726         return true;
35727     },
35728
35729     getValue : function()
35730     {
35731         var v = this.hiddenEl().getValue();
35732         
35733         return this.fixPrecision(this.parseValue(v));
35734     },
35735
35736     parseValue : function(value)
35737     {
35738         if(this.thousandsDelimiter) {
35739             value += "";
35740             r = new RegExp(",", "g");
35741             value = value.replace(r, "");
35742         }
35743         
35744         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
35745         return isNaN(value) ? '' : value;
35746     },
35747
35748     fixPrecision : function(value)
35749     {
35750         if(this.thousandsDelimiter) {
35751             value += "";
35752             r = new RegExp(",", "g");
35753             value = value.replace(r, "");
35754         }
35755         
35756         var nan = isNaN(value);
35757         
35758         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
35759             return nan ? '' : value;
35760         }
35761         return parseFloat(value).toFixed(this.decimalPrecision);
35762     },
35763
35764     setValue : function(v)
35765     {
35766         v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
35767         
35768         this.value = v;
35769         
35770         if(this.rendered){
35771             
35772             this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
35773             
35774             this.inputEl().dom.value = (v == '') ? '' :
35775                 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
35776             
35777             if(!this.allowZero && v === '0') {
35778                 this.hiddenEl().dom.value = '';
35779                 this.inputEl().dom.value = '';
35780             }
35781             
35782             this.validate();
35783         }
35784     },
35785
35786     decimalPrecisionFcn : function(v)
35787     {
35788         return Math.floor(v);
35789     },
35790
35791     beforeBlur : function()
35792     {
35793         var v = this.parseValue(this.getRawValue());
35794         
35795         if(v || v === 0 || v === ''){
35796             this.setValue(v);
35797         }
35798     },
35799     
35800     hiddenEl : function()
35801     {
35802         return this.el.select('input.hidden-number-input',true).first();
35803     }
35804     
35805 });
35806
35807  
35808
35809 /*
35810 * Licence: LGPL
35811 */
35812
35813 /**
35814  * @class Roo.bootstrap.DocumentSlider
35815  * @extends Roo.bootstrap.Component
35816  * Bootstrap DocumentSlider class
35817  * 
35818  * @constructor
35819  * Create a new DocumentViewer
35820  * @param {Object} config The config object
35821  */
35822
35823 Roo.bootstrap.DocumentSlider = function(config){
35824     Roo.bootstrap.DocumentSlider.superclass.constructor.call(this, config);
35825     
35826     this.files = [];
35827     
35828     this.addEvents({
35829         /**
35830          * @event initial
35831          * Fire after initEvent
35832          * @param {Roo.bootstrap.DocumentSlider} this
35833          */
35834         "initial" : true,
35835         /**
35836          * @event update
35837          * Fire after update
35838          * @param {Roo.bootstrap.DocumentSlider} this
35839          */
35840         "update" : true,
35841         /**
35842          * @event click
35843          * Fire after click
35844          * @param {Roo.bootstrap.DocumentSlider} this
35845          */
35846         "click" : true
35847     });
35848 };
35849
35850 Roo.extend(Roo.bootstrap.DocumentSlider, Roo.bootstrap.Component,  {
35851     
35852     files : false,
35853     
35854     indicator : 0,
35855     
35856     getAutoCreate : function()
35857     {
35858         var cfg = {
35859             tag : 'div',
35860             cls : 'roo-document-slider',
35861             cn : [
35862                 {
35863                     tag : 'div',
35864                     cls : 'roo-document-slider-header',
35865                     cn : [
35866                         {
35867                             tag : 'div',
35868                             cls : 'roo-document-slider-header-title'
35869                         }
35870                     ]
35871                 },
35872                 {
35873                     tag : 'div',
35874                     cls : 'roo-document-slider-body',
35875                     cn : [
35876                         {
35877                             tag : 'div',
35878                             cls : 'roo-document-slider-prev',
35879                             cn : [
35880                                 {
35881                                     tag : 'i',
35882                                     cls : 'fa fa-chevron-left'
35883                                 }
35884                             ]
35885                         },
35886                         {
35887                             tag : 'div',
35888                             cls : 'roo-document-slider-thumb',
35889                             cn : [
35890                                 {
35891                                     tag : 'img',
35892                                     cls : 'roo-document-slider-image'
35893                                 }
35894                             ]
35895                         },
35896                         {
35897                             tag : 'div',
35898                             cls : 'roo-document-slider-next',
35899                             cn : [
35900                                 {
35901                                     tag : 'i',
35902                                     cls : 'fa fa-chevron-right'
35903                                 }
35904                             ]
35905                         }
35906                     ]
35907                 }
35908             ]
35909         };
35910         
35911         return cfg;
35912     },
35913     
35914     initEvents : function()
35915     {
35916         this.headerEl = this.el.select('.roo-document-slider-header', true).first();
35917         this.headerEl.setVisibilityMode(Roo.Element.DISPLAY);
35918         
35919         this.titleEl = this.el.select('.roo-document-slider-header .roo-document-slider-header-title', true).first();
35920         this.titleEl.setVisibilityMode(Roo.Element.DISPLAY);
35921         
35922         this.bodyEl = this.el.select('.roo-document-slider-body', true).first();
35923         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
35924         
35925         this.thumbEl = this.el.select('.roo-document-slider-thumb', true).first();
35926         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
35927         
35928         this.imageEl = this.el.select('.roo-document-slider-image', true).first();
35929         this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
35930         
35931         this.prevIndicator = this.el.select('.roo-document-slider-prev i', true).first();
35932         this.prevIndicator.setVisibilityMode(Roo.Element.DISPLAY);
35933         
35934         this.nextIndicator = this.el.select('.roo-document-slider-next i', true).first();
35935         this.nextIndicator.setVisibilityMode(Roo.Element.DISPLAY);
35936         
35937         this.thumbEl.on('click', this.onClick, this);
35938         
35939         this.prevIndicator.on('click', this.prev, this);
35940         
35941         this.nextIndicator.on('click', this.next, this);
35942         
35943     },
35944     
35945     initial : function()
35946     {
35947         if(this.files.length){
35948             this.indicator = 1;
35949             this.update()
35950         }
35951         
35952         this.fireEvent('initial', this);
35953     },
35954     
35955     update : function()
35956     {
35957         this.imageEl.attr('src', this.files[this.indicator - 1]);
35958         
35959         this.titleEl.dom.innerHTML = String.format('{0} / {1}', this.indicator, this.files.length);
35960         
35961         this.prevIndicator.show();
35962         
35963         if(this.indicator == 1){
35964             this.prevIndicator.hide();
35965         }
35966         
35967         this.nextIndicator.show();
35968         
35969         if(this.indicator == this.files.length){
35970             this.nextIndicator.hide();
35971         }
35972         
35973         this.thumbEl.scrollTo('top');
35974         
35975         this.fireEvent('update', this);
35976     },
35977     
35978     onClick : function(e)
35979     {
35980         e.preventDefault();
35981         
35982         this.fireEvent('click', this);
35983     },
35984     
35985     prev : function(e)
35986     {
35987         e.preventDefault();
35988         
35989         this.indicator = Math.max(1, this.indicator - 1);
35990         
35991         this.update();
35992     },
35993     
35994     next : function(e)
35995     {
35996         e.preventDefault();
35997         
35998         this.indicator = Math.min(this.files.length, this.indicator + 1);
35999         
36000         this.update();
36001     }
36002 });
36003 /*
36004  * - LGPL
36005  *
36006  * RadioSet
36007  *
36008  *
36009  */
36010
36011 /**
36012  * @class Roo.bootstrap.RadioSet
36013  * @extends Roo.bootstrap.Input
36014  * Bootstrap RadioSet class
36015  * @cfg {String} indicatorpos (left|right) default left
36016  * @cfg {Boolean} inline (true|false) inline the element (default true)
36017  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the radio
36018  * @constructor
36019  * Create a new RadioSet
36020  * @param {Object} config The config object
36021  */
36022
36023 Roo.bootstrap.RadioSet = function(config){
36024     
36025     Roo.bootstrap.RadioSet.superclass.constructor.call(this, config);
36026     
36027     this.radioes = [];
36028     
36029     Roo.bootstrap.RadioSet.register(this);
36030     
36031     this.addEvents({
36032         /**
36033         * @event check
36034         * Fires when the element is checked or unchecked.
36035         * @param {Roo.bootstrap.RadioSet} this This radio
36036         * @param {Roo.bootstrap.Radio} item The checked item
36037         */
36038        check : true,
36039        /**
36040         * @event click
36041         * Fires when the element is click.
36042         * @param {Roo.bootstrap.RadioSet} this This radio set
36043         * @param {Roo.bootstrap.Radio} item The checked item
36044         * @param {Roo.EventObject} e The event object
36045         */
36046        click : true
36047     });
36048     
36049 };
36050
36051 Roo.extend(Roo.bootstrap.RadioSet, Roo.bootstrap.Input,  {
36052
36053     radioes : false,
36054     
36055     inline : true,
36056     
36057     weight : '',
36058     
36059     indicatorpos : 'left',
36060     
36061     getAutoCreate : function()
36062     {
36063         var label = {
36064             tag : 'label',
36065             cls : 'roo-radio-set-label',
36066             cn : [
36067                 {
36068                     tag : 'span',
36069                     html : this.fieldLabel
36070                 }
36071             ]
36072         };
36073         if (Roo.bootstrap.version == 3) {
36074             
36075             
36076             if(this.indicatorpos == 'left'){
36077                 label.cn.unshift({
36078                     tag : 'i',
36079                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
36080                     tooltip : 'This field is required'
36081                 });
36082             } else {
36083                 label.cn.push({
36084                     tag : 'i',
36085                     cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
36086                     tooltip : 'This field is required'
36087                 });
36088             }
36089         }
36090         var items = {
36091             tag : 'div',
36092             cls : 'roo-radio-set-items'
36093         };
36094         
36095         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
36096         
36097         if (align === 'left' && this.fieldLabel.length) {
36098             
36099             items = {
36100                 cls : "roo-radio-set-right", 
36101                 cn: [
36102                     items
36103                 ]
36104             };
36105             
36106             if(this.labelWidth > 12){
36107                 label.style = "width: " + this.labelWidth + 'px';
36108             }
36109             
36110             if(this.labelWidth < 13 && this.labelmd == 0){
36111                 this.labelmd = this.labelWidth;
36112             }
36113             
36114             if(this.labellg > 0){
36115                 label.cls += ' col-lg-' + this.labellg;
36116                 items.cls += ' col-lg-' + (12 - this.labellg);
36117             }
36118             
36119             if(this.labelmd > 0){
36120                 label.cls += ' col-md-' + this.labelmd;
36121                 items.cls += ' col-md-' + (12 - this.labelmd);
36122             }
36123             
36124             if(this.labelsm > 0){
36125                 label.cls += ' col-sm-' + this.labelsm;
36126                 items.cls += ' col-sm-' + (12 - this.labelsm);
36127             }
36128             
36129             if(this.labelxs > 0){
36130                 label.cls += ' col-xs-' + this.labelxs;
36131                 items.cls += ' col-xs-' + (12 - this.labelxs);
36132             }
36133         }
36134         
36135         var cfg = {
36136             tag : 'div',
36137             cls : 'roo-radio-set',
36138             cn : [
36139                 {
36140                     tag : 'input',
36141                     cls : 'roo-radio-set-input',
36142                     type : 'hidden',
36143                     name : this.name,
36144                     value : this.value ? this.value :  ''
36145                 },
36146                 label,
36147                 items
36148             ]
36149         };
36150         
36151         if(this.weight.length){
36152             cfg.cls += ' roo-radio-' + this.weight;
36153         }
36154         
36155         if(this.inline) {
36156             cfg.cls += ' roo-radio-set-inline';
36157         }
36158         
36159         var settings=this;
36160         ['xs','sm','md','lg'].map(function(size){
36161             if (settings[size]) {
36162                 cfg.cls += ' col-' + size + '-' + settings[size];
36163             }
36164         });
36165         
36166         return cfg;
36167         
36168     },
36169
36170     initEvents : function()
36171     {
36172         this.labelEl = this.el.select('.roo-radio-set-label', true).first();
36173         this.labelEl.setVisibilityMode(Roo.Element.DISPLAY);
36174         
36175         if(!this.fieldLabel.length){
36176             this.labelEl.hide();
36177         }
36178         
36179         this.itemsEl = this.el.select('.roo-radio-set-items', true).first();
36180         this.itemsEl.setVisibilityMode(Roo.Element.DISPLAY);
36181         
36182         this.indicator = this.indicatorEl();
36183         
36184         if(this.indicator){
36185             this.indicator.addClass('invisible');
36186         }
36187         
36188         this.originalValue = this.getValue();
36189         
36190     },
36191     
36192     inputEl: function ()
36193     {
36194         return this.el.select('.roo-radio-set-input', true).first();
36195     },
36196     
36197     getChildContainer : function()
36198     {
36199         return this.itemsEl;
36200     },
36201     
36202     register : function(item)
36203     {
36204         this.radioes.push(item);
36205         
36206     },
36207     
36208     validate : function()
36209     {   
36210         if(this.getVisibilityEl().hasClass('hidden')){
36211             return true;
36212         }
36213         
36214         var valid = false;
36215         
36216         Roo.each(this.radioes, function(i){
36217             if(!i.checked){
36218                 return;
36219             }
36220             
36221             valid = true;
36222             return false;
36223         });
36224         
36225         if(this.allowBlank) {
36226             return true;
36227         }
36228         
36229         if(this.disabled || valid){
36230             this.markValid();
36231             return true;
36232         }
36233         
36234         this.markInvalid();
36235         return false;
36236         
36237     },
36238     
36239     markValid : function()
36240     {
36241         if(this.labelEl.isVisible(true) && this.indicatorEl()){
36242             this.indicatorEl().removeClass('visible');
36243             this.indicatorEl().addClass('invisible');
36244         }
36245         
36246         
36247         if (Roo.bootstrap.version == 3) {
36248             this.el.removeClass([this.invalidClass, this.validClass]);
36249             this.el.addClass(this.validClass);
36250         } else {
36251             this.el.removeClass(['is-invalid','is-valid']);
36252             this.el.addClass(['is-valid']);
36253         }
36254         this.fireEvent('valid', this);
36255     },
36256     
36257     markInvalid : function(msg)
36258     {
36259         if(this.allowBlank || this.disabled){
36260             return;
36261         }
36262         
36263         if(this.labelEl.isVisible(true) && this.indicatorEl()){
36264             this.indicatorEl().removeClass('invisible');
36265             this.indicatorEl().addClass('visible');
36266         }
36267         if (Roo.bootstrap.version == 3) {
36268             this.el.removeClass([this.invalidClass, this.validClass]);
36269             this.el.addClass(this.invalidClass);
36270         } else {
36271             this.el.removeClass(['is-invalid','is-valid']);
36272             this.el.addClass(['is-invalid']);
36273         }
36274         
36275         this.fireEvent('invalid', this, msg);
36276         
36277     },
36278     
36279     setValue : function(v, suppressEvent)
36280     {   
36281         if(this.value === v){
36282             return;
36283         }
36284         
36285         this.value = v;
36286         
36287         if(this.rendered){
36288             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
36289         }
36290         
36291         Roo.each(this.radioes, function(i){
36292             i.checked = false;
36293             i.el.removeClass('checked');
36294         });
36295         
36296         Roo.each(this.radioes, function(i){
36297             
36298             if(i.value === v || i.value.toString() === v.toString()){
36299                 i.checked = true;
36300                 i.el.addClass('checked');
36301                 
36302                 if(suppressEvent !== true){
36303                     this.fireEvent('check', this, i);
36304                 }
36305                 
36306                 return false;
36307             }
36308             
36309         }, this);
36310         
36311         this.validate();
36312     },
36313     
36314     clearInvalid : function(){
36315         
36316         if(!this.el || this.preventMark){
36317             return;
36318         }
36319         
36320         this.el.removeClass([this.invalidClass]);
36321         
36322         this.fireEvent('valid', this);
36323     }
36324     
36325 });
36326
36327 Roo.apply(Roo.bootstrap.RadioSet, {
36328     
36329     groups: {},
36330     
36331     register : function(set)
36332     {
36333         this.groups[set.name] = set;
36334     },
36335     
36336     get: function(name) 
36337     {
36338         if (typeof(this.groups[name]) == 'undefined') {
36339             return false;
36340         }
36341         
36342         return this.groups[name] ;
36343     }
36344     
36345 });
36346 /*
36347  * Based on:
36348  * Ext JS Library 1.1.1
36349  * Copyright(c) 2006-2007, Ext JS, LLC.
36350  *
36351  * Originally Released Under LGPL - original licence link has changed is not relivant.
36352  *
36353  * Fork - LGPL
36354  * <script type="text/javascript">
36355  */
36356
36357
36358 /**
36359  * @class Roo.bootstrap.SplitBar
36360  * @extends Roo.util.Observable
36361  * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
36362  * <br><br>
36363  * Usage:
36364  * <pre><code>
36365 var split = new Roo.bootstrap.SplitBar("elementToDrag", "elementToSize",
36366                    Roo.bootstrap.SplitBar.HORIZONTAL, Roo.bootstrap.SplitBar.LEFT);
36367 split.setAdapter(new Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter("container"));
36368 split.minSize = 100;
36369 split.maxSize = 600;
36370 split.animate = true;
36371 split.on('moved', splitterMoved);
36372 </code></pre>
36373  * @constructor
36374  * Create a new SplitBar
36375  * @config {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar. 
36376  * @config {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged 
36377  * @config {Number} orientation (optional) Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
36378  * @config {Number} placement (optional) Either Roo.bootstrap.SplitBar.LEFT or Roo.bootstrap.SplitBar.RIGHT for horizontal or  
36379                         Roo.bootstrap.SplitBar.TOP or Roo.bootstrap.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
36380                         position of the SplitBar).
36381  */
36382 Roo.bootstrap.SplitBar = function(cfg){
36383     
36384     /** @private */
36385     
36386     //{
36387     //  dragElement : elm
36388     //  resizingElement: el,
36389         // optional..
36390     //    orientation : Either Roo.bootstrap.SplitBar.HORIZONTAL
36391     //    placement : Roo.bootstrap.SplitBar.LEFT  ,
36392         // existingProxy ???
36393     //}
36394     
36395     this.el = Roo.get(cfg.dragElement, true);
36396     this.el.dom.unselectable = "on";
36397     /** @private */
36398     this.resizingEl = Roo.get(cfg.resizingElement, true);
36399
36400     /**
36401      * @private
36402      * The orientation of the split. Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
36403      * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
36404      * @type Number
36405      */
36406     this.orientation = cfg.orientation || Roo.bootstrap.SplitBar.HORIZONTAL;
36407     
36408     /**
36409      * The minimum size of the resizing element. (Defaults to 0)
36410      * @type Number
36411      */
36412     this.minSize = 0;
36413     
36414     /**
36415      * The maximum size of the resizing element. (Defaults to 2000)
36416      * @type Number
36417      */
36418     this.maxSize = 2000;
36419     
36420     /**
36421      * Whether to animate the transition to the new size
36422      * @type Boolean
36423      */
36424     this.animate = false;
36425     
36426     /**
36427      * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
36428      * @type Boolean
36429      */
36430     this.useShim = false;
36431     
36432     /** @private */
36433     this.shim = null;
36434     
36435     if(!cfg.existingProxy){
36436         /** @private */
36437         this.proxy = Roo.bootstrap.SplitBar.createProxy(this.orientation);
36438     }else{
36439         this.proxy = Roo.get(cfg.existingProxy).dom;
36440     }
36441     /** @private */
36442     this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
36443     
36444     /** @private */
36445     this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
36446     
36447     /** @private */
36448     this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
36449     
36450     /** @private */
36451     this.dragSpecs = {};
36452     
36453     /**
36454      * @private The adapter to use to positon and resize elements
36455      */
36456     this.adapter = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
36457     this.adapter.init(this);
36458     
36459     if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
36460         /** @private */
36461         this.placement = cfg.placement || (this.el.getX() > this.resizingEl.getX() ? Roo.bootstrap.SplitBar.LEFT : Roo.bootstrap.SplitBar.RIGHT);
36462         this.el.addClass("roo-splitbar-h");
36463     }else{
36464         /** @private */
36465         this.placement = cfg.placement || (this.el.getY() > this.resizingEl.getY() ? Roo.bootstrap.SplitBar.TOP : Roo.bootstrap.SplitBar.BOTTOM);
36466         this.el.addClass("roo-splitbar-v");
36467     }
36468     
36469     this.addEvents({
36470         /**
36471          * @event resize
36472          * Fires when the splitter is moved (alias for {@link #event-moved})
36473          * @param {Roo.bootstrap.SplitBar} this
36474          * @param {Number} newSize the new width or height
36475          */
36476         "resize" : true,
36477         /**
36478          * @event moved
36479          * Fires when the splitter is moved
36480          * @param {Roo.bootstrap.SplitBar} this
36481          * @param {Number} newSize the new width or height
36482          */
36483         "moved" : true,
36484         /**
36485          * @event beforeresize
36486          * Fires before the splitter is dragged
36487          * @param {Roo.bootstrap.SplitBar} this
36488          */
36489         "beforeresize" : true,
36490
36491         "beforeapply" : true
36492     });
36493
36494     Roo.util.Observable.call(this);
36495 };
36496
36497 Roo.extend(Roo.bootstrap.SplitBar, Roo.util.Observable, {
36498     onStartProxyDrag : function(x, y){
36499         this.fireEvent("beforeresize", this);
36500         if(!this.overlay){
36501             var o = Roo.DomHelper.insertFirst(document.body,  {cls: "roo-drag-overlay", html: "&#160;"}, true);
36502             o.unselectable();
36503             o.enableDisplayMode("block");
36504             // all splitbars share the same overlay
36505             Roo.bootstrap.SplitBar.prototype.overlay = o;
36506         }
36507         this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
36508         this.overlay.show();
36509         Roo.get(this.proxy).setDisplayed("block");
36510         var size = this.adapter.getElementSize(this);
36511         this.activeMinSize = this.getMinimumSize();;
36512         this.activeMaxSize = this.getMaximumSize();;
36513         var c1 = size - this.activeMinSize;
36514         var c2 = Math.max(this.activeMaxSize - size, 0);
36515         if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
36516             this.dd.resetConstraints();
36517             this.dd.setXConstraint(
36518                 this.placement == Roo.bootstrap.SplitBar.LEFT ? c1 : c2, 
36519                 this.placement == Roo.bootstrap.SplitBar.LEFT ? c2 : c1
36520             );
36521             this.dd.setYConstraint(0, 0);
36522         }else{
36523             this.dd.resetConstraints();
36524             this.dd.setXConstraint(0, 0);
36525             this.dd.setYConstraint(
36526                 this.placement == Roo.bootstrap.SplitBar.TOP ? c1 : c2, 
36527                 this.placement == Roo.bootstrap.SplitBar.TOP ? c2 : c1
36528             );
36529          }
36530         this.dragSpecs.startSize = size;
36531         this.dragSpecs.startPoint = [x, y];
36532         Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
36533     },
36534     
36535     /** 
36536      * @private Called after the drag operation by the DDProxy
36537      */
36538     onEndProxyDrag : function(e){
36539         Roo.get(this.proxy).setDisplayed(false);
36540         var endPoint = Roo.lib.Event.getXY(e);
36541         if(this.overlay){
36542             this.overlay.hide();
36543         }
36544         var newSize;
36545         if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
36546             newSize = this.dragSpecs.startSize + 
36547                 (this.placement == Roo.bootstrap.SplitBar.LEFT ?
36548                     endPoint[0] - this.dragSpecs.startPoint[0] :
36549                     this.dragSpecs.startPoint[0] - endPoint[0]
36550                 );
36551         }else{
36552             newSize = this.dragSpecs.startSize + 
36553                 (this.placement == Roo.bootstrap.SplitBar.TOP ?
36554                     endPoint[1] - this.dragSpecs.startPoint[1] :
36555                     this.dragSpecs.startPoint[1] - endPoint[1]
36556                 );
36557         }
36558         newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
36559         if(newSize != this.dragSpecs.startSize){
36560             if(this.fireEvent('beforeapply', this, newSize) !== false){
36561                 this.adapter.setElementSize(this, newSize);
36562                 this.fireEvent("moved", this, newSize);
36563                 this.fireEvent("resize", this, newSize);
36564             }
36565         }
36566     },
36567     
36568     /**
36569      * Get the adapter this SplitBar uses
36570      * @return The adapter object
36571      */
36572     getAdapter : function(){
36573         return this.adapter;
36574     },
36575     
36576     /**
36577      * Set the adapter this SplitBar uses
36578      * @param {Object} adapter A SplitBar adapter object
36579      */
36580     setAdapter : function(adapter){
36581         this.adapter = adapter;
36582         this.adapter.init(this);
36583     },
36584     
36585     /**
36586      * Gets the minimum size for the resizing element
36587      * @return {Number} The minimum size
36588      */
36589     getMinimumSize : function(){
36590         return this.minSize;
36591     },
36592     
36593     /**
36594      * Sets the minimum size for the resizing element
36595      * @param {Number} minSize The minimum size
36596      */
36597     setMinimumSize : function(minSize){
36598         this.minSize = minSize;
36599     },
36600     
36601     /**
36602      * Gets the maximum size for the resizing element
36603      * @return {Number} The maximum size
36604      */
36605     getMaximumSize : function(){
36606         return this.maxSize;
36607     },
36608     
36609     /**
36610      * Sets the maximum size for the resizing element
36611      * @param {Number} maxSize The maximum size
36612      */
36613     setMaximumSize : function(maxSize){
36614         this.maxSize = maxSize;
36615     },
36616     
36617     /**
36618      * Sets the initialize size for the resizing element
36619      * @param {Number} size The initial size
36620      */
36621     setCurrentSize : function(size){
36622         var oldAnimate = this.animate;
36623         this.animate = false;
36624         this.adapter.setElementSize(this, size);
36625         this.animate = oldAnimate;
36626     },
36627     
36628     /**
36629      * Destroy this splitbar. 
36630      * @param {Boolean} removeEl True to remove the element
36631      */
36632     destroy : function(removeEl){
36633         if(this.shim){
36634             this.shim.remove();
36635         }
36636         this.dd.unreg();
36637         this.proxy.parentNode.removeChild(this.proxy);
36638         if(removeEl){
36639             this.el.remove();
36640         }
36641     }
36642 });
36643
36644 /**
36645  * @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.
36646  */
36647 Roo.bootstrap.SplitBar.createProxy = function(dir){
36648     var proxy = new Roo.Element(document.createElement("div"));
36649     proxy.unselectable();
36650     var cls = 'roo-splitbar-proxy';
36651     proxy.addClass(cls + ' ' + (dir == Roo.bootstrap.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
36652     document.body.appendChild(proxy.dom);
36653     return proxy.dom;
36654 };
36655
36656 /** 
36657  * @class Roo.bootstrap.SplitBar.BasicLayoutAdapter
36658  * Default Adapter. It assumes the splitter and resizing element are not positioned
36659  * elements and only gets/sets the width of the element. Generally used for table based layouts.
36660  */
36661 Roo.bootstrap.SplitBar.BasicLayoutAdapter = function(){
36662 };
36663
36664 Roo.bootstrap.SplitBar.BasicLayoutAdapter.prototype = {
36665     // do nothing for now
36666     init : function(s){
36667     
36668     },
36669     /**
36670      * Called before drag operations to get the current size of the resizing element. 
36671      * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
36672      */
36673      getElementSize : function(s){
36674         if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
36675             return s.resizingEl.getWidth();
36676         }else{
36677             return s.resizingEl.getHeight();
36678         }
36679     },
36680     
36681     /**
36682      * Called after drag operations to set the size of the resizing element.
36683      * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
36684      * @param {Number} newSize The new size to set
36685      * @param {Function} onComplete A function to be invoked when resizing is complete
36686      */
36687     setElementSize : function(s, newSize, onComplete){
36688         if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
36689             if(!s.animate){
36690                 s.resizingEl.setWidth(newSize);
36691                 if(onComplete){
36692                     onComplete(s, newSize);
36693                 }
36694             }else{
36695                 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
36696             }
36697         }else{
36698             
36699             if(!s.animate){
36700                 s.resizingEl.setHeight(newSize);
36701                 if(onComplete){
36702                     onComplete(s, newSize);
36703                 }
36704             }else{
36705                 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
36706             }
36707         }
36708     }
36709 };
36710
36711 /** 
36712  *@class Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter
36713  * @extends Roo.bootstrap.SplitBar.BasicLayoutAdapter
36714  * Adapter that  moves the splitter element to align with the resized sizing element. 
36715  * Used with an absolute positioned SplitBar.
36716  * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
36717  * document.body, make sure you assign an id to the body element.
36718  */
36719 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter = function(container){
36720     this.basic = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
36721     this.container = Roo.get(container);
36722 };
36723
36724 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter.prototype = {
36725     init : function(s){
36726         this.basic.init(s);
36727     },
36728     
36729     getElementSize : function(s){
36730         return this.basic.getElementSize(s);
36731     },
36732     
36733     setElementSize : function(s, newSize, onComplete){
36734         this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
36735     },
36736     
36737     moveSplitter : function(s){
36738         var yes = Roo.bootstrap.SplitBar;
36739         switch(s.placement){
36740             case yes.LEFT:
36741                 s.el.setX(s.resizingEl.getRight());
36742                 break;
36743             case yes.RIGHT:
36744                 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
36745                 break;
36746             case yes.TOP:
36747                 s.el.setY(s.resizingEl.getBottom());
36748                 break;
36749             case yes.BOTTOM:
36750                 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
36751                 break;
36752         }
36753     }
36754 };
36755
36756 /**
36757  * Orientation constant - Create a vertical SplitBar
36758  * @static
36759  * @type Number
36760  */
36761 Roo.bootstrap.SplitBar.VERTICAL = 1;
36762
36763 /**
36764  * Orientation constant - Create a horizontal SplitBar
36765  * @static
36766  * @type Number
36767  */
36768 Roo.bootstrap.SplitBar.HORIZONTAL = 2;
36769
36770 /**
36771  * Placement constant - The resizing element is to the left of the splitter element
36772  * @static
36773  * @type Number
36774  */
36775 Roo.bootstrap.SplitBar.LEFT = 1;
36776
36777 /**
36778  * Placement constant - The resizing element is to the right of the splitter element
36779  * @static
36780  * @type Number
36781  */
36782 Roo.bootstrap.SplitBar.RIGHT = 2;
36783
36784 /**
36785  * Placement constant - The resizing element is positioned above the splitter element
36786  * @static
36787  * @type Number
36788  */
36789 Roo.bootstrap.SplitBar.TOP = 3;
36790
36791 /**
36792  * Placement constant - The resizing element is positioned under splitter element
36793  * @static
36794  * @type Number
36795  */
36796 Roo.bootstrap.SplitBar.BOTTOM = 4;
36797 Roo.namespace("Roo.bootstrap.layout");/*
36798  * Based on:
36799  * Ext JS Library 1.1.1
36800  * Copyright(c) 2006-2007, Ext JS, LLC.
36801  *
36802  * Originally Released Under LGPL - original licence link has changed is not relivant.
36803  *
36804  * Fork - LGPL
36805  * <script type="text/javascript">
36806  */
36807
36808 /**
36809  * @class Roo.bootstrap.layout.Manager
36810  * @extends Roo.bootstrap.Component
36811  * Base class for layout managers.
36812  */
36813 Roo.bootstrap.layout.Manager = function(config)
36814 {
36815     Roo.bootstrap.layout.Manager.superclass.constructor.call(this,config);
36816
36817
36818
36819
36820
36821     /** false to disable window resize monitoring @type Boolean */
36822     this.monitorWindowResize = true;
36823     this.regions = {};
36824     this.addEvents({
36825         /**
36826          * @event layout
36827          * Fires when a layout is performed.
36828          * @param {Roo.LayoutManager} this
36829          */
36830         "layout" : true,
36831         /**
36832          * @event regionresized
36833          * Fires when the user resizes a region.
36834          * @param {Roo.LayoutRegion} region The resized region
36835          * @param {Number} newSize The new size (width for east/west, height for north/south)
36836          */
36837         "regionresized" : true,
36838         /**
36839          * @event regioncollapsed
36840          * Fires when a region is collapsed.
36841          * @param {Roo.LayoutRegion} region The collapsed region
36842          */
36843         "regioncollapsed" : true,
36844         /**
36845          * @event regionexpanded
36846          * Fires when a region is expanded.
36847          * @param {Roo.LayoutRegion} region The expanded region
36848          */
36849         "regionexpanded" : true
36850     });
36851     this.updating = false;
36852
36853     if (config.el) {
36854         this.el = Roo.get(config.el);
36855         this.initEvents();
36856     }
36857
36858 };
36859
36860 Roo.extend(Roo.bootstrap.layout.Manager, Roo.bootstrap.Component, {
36861
36862
36863     regions : null,
36864
36865     monitorWindowResize : true,
36866
36867
36868     updating : false,
36869
36870
36871     onRender : function(ct, position)
36872     {
36873         if(!this.el){
36874             this.el = Roo.get(ct);
36875             this.initEvents();
36876         }
36877         //this.fireEvent('render',this);
36878     },
36879
36880
36881     initEvents: function()
36882     {
36883
36884
36885         // ie scrollbar fix
36886         if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
36887             document.body.scroll = "no";
36888         }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
36889             this.el.position('relative');
36890         }
36891         this.id = this.el.id;
36892         this.el.addClass("roo-layout-container");
36893         Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
36894         if(this.el.dom != document.body ) {
36895             this.el.on('resize', this.layout,this);
36896             this.el.on('show', this.layout,this);
36897         }
36898
36899     },
36900
36901     /**
36902      * Returns true if this layout is currently being updated
36903      * @return {Boolean}
36904      */
36905     isUpdating : function(){
36906         return this.updating;
36907     },
36908
36909     /**
36910      * Suspend the LayoutManager from doing auto-layouts while
36911      * making multiple add or remove calls
36912      */
36913     beginUpdate : function(){
36914         this.updating = true;
36915     },
36916
36917     /**
36918      * Restore auto-layouts and optionally disable the manager from performing a layout
36919      * @param {Boolean} noLayout true to disable a layout update
36920      */
36921     endUpdate : function(noLayout){
36922         this.updating = false;
36923         if(!noLayout){
36924             this.layout();
36925         }
36926     },
36927
36928     layout: function(){
36929         // abstract...
36930     },
36931
36932     onRegionResized : function(region, newSize){
36933         this.fireEvent("regionresized", region, newSize);
36934         this.layout();
36935     },
36936
36937     onRegionCollapsed : function(region){
36938         this.fireEvent("regioncollapsed", region);
36939     },
36940
36941     onRegionExpanded : function(region){
36942         this.fireEvent("regionexpanded", region);
36943     },
36944
36945     /**
36946      * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
36947      * performs box-model adjustments.
36948      * @return {Object} The size as an object {width: (the width), height: (the height)}
36949      */
36950     getViewSize : function()
36951     {
36952         var size;
36953         if(this.el.dom != document.body){
36954             size = this.el.getSize();
36955         }else{
36956             size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
36957         }
36958         size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
36959         size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
36960         return size;
36961     },
36962
36963     /**
36964      * Returns the Element this layout is bound to.
36965      * @return {Roo.Element}
36966      */
36967     getEl : function(){
36968         return this.el;
36969     },
36970
36971     /**
36972      * Returns the specified region.
36973      * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
36974      * @return {Roo.LayoutRegion}
36975      */
36976     getRegion : function(target){
36977         return this.regions[target.toLowerCase()];
36978     },
36979
36980     onWindowResize : function(){
36981         if(this.monitorWindowResize){
36982             this.layout();
36983         }
36984     }
36985 });
36986 /*
36987  * Based on:
36988  * Ext JS Library 1.1.1
36989  * Copyright(c) 2006-2007, Ext JS, LLC.
36990  *
36991  * Originally Released Under LGPL - original licence link has changed is not relivant.
36992  *
36993  * Fork - LGPL
36994  * <script type="text/javascript">
36995  */
36996 /**
36997  * @class Roo.bootstrap.layout.Border
36998  * @extends Roo.bootstrap.layout.Manager
36999  * This class represents a common layout manager used in desktop applications. For screenshots and more details,
37000  * please see: examples/bootstrap/nested.html<br><br>
37001  
37002 <b>The container the layout is rendered into can be either the body element or any other element.
37003 If it is not the body element, the container needs to either be an absolute positioned element,
37004 or you will need to add "position:relative" to the css of the container.  You will also need to specify
37005 the container size if it is not the body element.</b>
37006
37007 * @constructor
37008 * Create a new Border
37009 * @param {Object} config Configuration options
37010  */
37011 Roo.bootstrap.layout.Border = function(config){
37012     config = config || {};
37013     Roo.bootstrap.layout.Border.superclass.constructor.call(this, config);
37014     
37015     
37016     
37017     Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
37018         if(config[region]){
37019             config[region].region = region;
37020             this.addRegion(config[region]);
37021         }
37022     },this);
37023     
37024 };
37025
37026 Roo.bootstrap.layout.Border.regions =  ["north","south","east","west","center"];
37027
37028 Roo.extend(Roo.bootstrap.layout.Border, Roo.bootstrap.layout.Manager, {
37029     
37030     parent : false, // this might point to a 'nest' or a ???
37031     
37032     /**
37033      * Creates and adds a new region if it doesn't already exist.
37034      * @param {String} target The target region key (north, south, east, west or center).
37035      * @param {Object} config The regions config object
37036      * @return {BorderLayoutRegion} The new region
37037      */
37038     addRegion : function(config)
37039     {
37040         if(!this.regions[config.region]){
37041             var r = this.factory(config);
37042             this.bindRegion(r);
37043         }
37044         return this.regions[config.region];
37045     },
37046
37047     // private (kinda)
37048     bindRegion : function(r){
37049         this.regions[r.config.region] = r;
37050         
37051         r.on("visibilitychange",    this.layout, this);
37052         r.on("paneladded",          this.layout, this);
37053         r.on("panelremoved",        this.layout, this);
37054         r.on("invalidated",         this.layout, this);
37055         r.on("resized",             this.onRegionResized, this);
37056         r.on("collapsed",           this.onRegionCollapsed, this);
37057         r.on("expanded",            this.onRegionExpanded, this);
37058     },
37059
37060     /**
37061      * Performs a layout update.
37062      */
37063     layout : function()
37064     {
37065         if(this.updating) {
37066             return;
37067         }
37068         
37069         // render all the rebions if they have not been done alreayd?
37070         Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
37071             if(this.regions[region] && !this.regions[region].bodyEl){
37072                 this.regions[region].onRender(this.el)
37073             }
37074         },this);
37075         
37076         var size = this.getViewSize();
37077         var w = size.width;
37078         var h = size.height;
37079         var centerW = w;
37080         var centerH = h;
37081         var centerY = 0;
37082         var centerX = 0;
37083         //var x = 0, y = 0;
37084
37085         var rs = this.regions;
37086         var north = rs["north"];
37087         var south = rs["south"]; 
37088         var west = rs["west"];
37089         var east = rs["east"];
37090         var center = rs["center"];
37091         //if(this.hideOnLayout){ // not supported anymore
37092             //c.el.setStyle("display", "none");
37093         //}
37094         if(north && north.isVisible()){
37095             var b = north.getBox();
37096             var m = north.getMargins();
37097             b.width = w - (m.left+m.right);
37098             b.x = m.left;
37099             b.y = m.top;
37100             centerY = b.height + b.y + m.bottom;
37101             centerH -= centerY;
37102             north.updateBox(this.safeBox(b));
37103         }
37104         if(south && south.isVisible()){
37105             var b = south.getBox();
37106             var m = south.getMargins();
37107             b.width = w - (m.left+m.right);
37108             b.x = m.left;
37109             var totalHeight = (b.height + m.top + m.bottom);
37110             b.y = h - totalHeight + m.top;
37111             centerH -= totalHeight;
37112             south.updateBox(this.safeBox(b));
37113         }
37114         if(west && west.isVisible()){
37115             var b = west.getBox();
37116             var m = west.getMargins();
37117             b.height = centerH - (m.top+m.bottom);
37118             b.x = m.left;
37119             b.y = centerY + m.top;
37120             var totalWidth = (b.width + m.left + m.right);
37121             centerX += totalWidth;
37122             centerW -= totalWidth;
37123             west.updateBox(this.safeBox(b));
37124         }
37125         if(east && east.isVisible()){
37126             var b = east.getBox();
37127             var m = east.getMargins();
37128             b.height = centerH - (m.top+m.bottom);
37129             var totalWidth = (b.width + m.left + m.right);
37130             b.x = w - totalWidth + m.left;
37131             b.y = centerY + m.top;
37132             centerW -= totalWidth;
37133             east.updateBox(this.safeBox(b));
37134         }
37135         if(center){
37136             var m = center.getMargins();
37137             var centerBox = {
37138                 x: centerX + m.left,
37139                 y: centerY + m.top,
37140                 width: centerW - (m.left+m.right),
37141                 height: centerH - (m.top+m.bottom)
37142             };
37143             //if(this.hideOnLayout){
37144                 //center.el.setStyle("display", "block");
37145             //}
37146             center.updateBox(this.safeBox(centerBox));
37147         }
37148         this.el.repaint();
37149         this.fireEvent("layout", this);
37150     },
37151
37152     // private
37153     safeBox : function(box){
37154         box.width = Math.max(0, box.width);
37155         box.height = Math.max(0, box.height);
37156         return box;
37157     },
37158
37159     /**
37160      * Adds a ContentPanel (or subclass) to this layout.
37161      * @param {String} target The target region key (north, south, east, west or center).
37162      * @param {Roo.ContentPanel} panel The panel to add
37163      * @return {Roo.ContentPanel} The added panel
37164      */
37165     add : function(target, panel){
37166          
37167         target = target.toLowerCase();
37168         return this.regions[target].add(panel);
37169     },
37170
37171     /**
37172      * Remove a ContentPanel (or subclass) to this layout.
37173      * @param {String} target The target region key (north, south, east, west or center).
37174      * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
37175      * @return {Roo.ContentPanel} The removed panel
37176      */
37177     remove : function(target, panel){
37178         target = target.toLowerCase();
37179         return this.regions[target].remove(panel);
37180     },
37181
37182     /**
37183      * Searches all regions for a panel with the specified id
37184      * @param {String} panelId
37185      * @return {Roo.ContentPanel} The panel or null if it wasn't found
37186      */
37187     findPanel : function(panelId){
37188         var rs = this.regions;
37189         for(var target in rs){
37190             if(typeof rs[target] != "function"){
37191                 var p = rs[target].getPanel(panelId);
37192                 if(p){
37193                     return p;
37194                 }
37195             }
37196         }
37197         return null;
37198     },
37199
37200     /**
37201      * Searches all regions for a panel with the specified id and activates (shows) it.
37202      * @param {String/ContentPanel} panelId The panels id or the panel itself
37203      * @return {Roo.ContentPanel} The shown panel or null
37204      */
37205     showPanel : function(panelId) {
37206       var rs = this.regions;
37207       for(var target in rs){
37208          var r = rs[target];
37209          if(typeof r != "function"){
37210             if(r.hasPanel(panelId)){
37211                return r.showPanel(panelId);
37212             }
37213          }
37214       }
37215       return null;
37216    },
37217
37218    /**
37219      * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
37220      * @param {Roo.state.Provider} provider (optional) An alternate state provider
37221      */
37222    /*
37223     restoreState : function(provider){
37224         if(!provider){
37225             provider = Roo.state.Manager;
37226         }
37227         var sm = new Roo.LayoutStateManager();
37228         sm.init(this, provider);
37229     },
37230 */
37231  
37232  
37233     /**
37234      * Adds a xtype elements to the layout.
37235      * <pre><code>
37236
37237 layout.addxtype({
37238        xtype : 'ContentPanel',
37239        region: 'west',
37240        items: [ .... ]
37241    }
37242 );
37243
37244 layout.addxtype({
37245         xtype : 'NestedLayoutPanel',
37246         region: 'west',
37247         layout: {
37248            center: { },
37249            west: { }   
37250         },
37251         items : [ ... list of content panels or nested layout panels.. ]
37252    }
37253 );
37254 </code></pre>
37255      * @param {Object} cfg Xtype definition of item to add.
37256      */
37257     addxtype : function(cfg)
37258     {
37259         // basically accepts a pannel...
37260         // can accept a layout region..!?!?
37261         //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
37262         
37263         
37264         // theory?  children can only be panels??
37265         
37266         //if (!cfg.xtype.match(/Panel$/)) {
37267         //    return false;
37268         //}
37269         var ret = false;
37270         
37271         if (typeof(cfg.region) == 'undefined') {
37272             Roo.log("Failed to add Panel, region was not set");
37273             Roo.log(cfg);
37274             return false;
37275         }
37276         var region = cfg.region;
37277         delete cfg.region;
37278         
37279           
37280         var xitems = [];
37281         if (cfg.items) {
37282             xitems = cfg.items;
37283             delete cfg.items;
37284         }
37285         var nb = false;
37286         
37287         if ( region == 'center') {
37288             Roo.log("Center: " + cfg.title);
37289         }
37290         
37291         
37292         switch(cfg.xtype) 
37293         {
37294             case 'Content':  // ContentPanel (el, cfg)
37295             case 'Scroll':  // ContentPanel (el, cfg)
37296             case 'View': 
37297                 cfg.autoCreate = cfg.autoCreate || true;
37298                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
37299                 //} else {
37300                 //    var el = this.el.createChild();
37301                 //    ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
37302                 //}
37303                 
37304                 this.add(region, ret);
37305                 break;
37306             
37307             /*
37308             case 'TreePanel': // our new panel!
37309                 cfg.el = this.el.createChild();
37310                 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
37311                 this.add(region, ret);
37312                 break;
37313             */
37314             
37315             case 'Nest': 
37316                 // create a new Layout (which is  a Border Layout...
37317                 
37318                 var clayout = cfg.layout;
37319                 clayout.el  = this.el.createChild();
37320                 clayout.items   = clayout.items  || [];
37321                 
37322                 delete cfg.layout;
37323                 
37324                 // replace this exitems with the clayout ones..
37325                 xitems = clayout.items;
37326                  
37327                 // force background off if it's in center...
37328                 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
37329                     cfg.background = false;
37330                 }
37331                 cfg.layout  = new Roo.bootstrap.layout.Border(clayout);
37332                 
37333                 
37334                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
37335                 //console.log('adding nested layout panel '  + cfg.toSource());
37336                 this.add(region, ret);
37337                 nb = {}; /// find first...
37338                 break;
37339             
37340             case 'Grid':
37341                 
37342                 // needs grid and region
37343                 
37344                 //var el = this.getRegion(region).el.createChild();
37345                 /*
37346                  *var el = this.el.createChild();
37347                 // create the grid first...
37348                 cfg.grid.container = el;
37349                 cfg.grid = new cfg.grid.xns[cfg.grid.xtype](cfg.grid);
37350                 */
37351                 
37352                 if (region == 'center' && this.active ) {
37353                     cfg.background = false;
37354                 }
37355                 
37356                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
37357                 
37358                 this.add(region, ret);
37359                 /*
37360                 if (cfg.background) {
37361                     // render grid on panel activation (if panel background)
37362                     ret.on('activate', function(gp) {
37363                         if (!gp.grid.rendered) {
37364                     //        gp.grid.render(el);
37365                         }
37366                     });
37367                 } else {
37368                   //  cfg.grid.render(el);
37369                 }
37370                 */
37371                 break;
37372            
37373            
37374             case 'Border': // it can get called on it'self... - might need to check if this is fixed?
37375                 // it was the old xcomponent building that caused this before.
37376                 // espeically if border is the top element in the tree.
37377                 ret = this;
37378                 break; 
37379                 
37380                     
37381                 
37382                 
37383                 
37384             default:
37385                 /*
37386                 if (typeof(Roo[cfg.xtype]) != 'undefined') {
37387                     
37388                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
37389                     this.add(region, ret);
37390                 } else {
37391                 */
37392                     Roo.log(cfg);
37393                     throw "Can not add '" + cfg.xtype + "' to Border";
37394                     return null;
37395              
37396                                 
37397              
37398         }
37399         this.beginUpdate();
37400         // add children..
37401         var region = '';
37402         var abn = {};
37403         Roo.each(xitems, function(i)  {
37404             region = nb && i.region ? i.region : false;
37405             
37406             var add = ret.addxtype(i);
37407            
37408             if (region) {
37409                 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
37410                 if (!i.background) {
37411                     abn[region] = nb[region] ;
37412                 }
37413             }
37414             
37415         });
37416         this.endUpdate();
37417
37418         // make the last non-background panel active..
37419         //if (nb) { Roo.log(abn); }
37420         if (nb) {
37421             
37422             for(var r in abn) {
37423                 region = this.getRegion(r);
37424                 if (region) {
37425                     // tried using nb[r], but it does not work..
37426                      
37427                     region.showPanel(abn[r]);
37428                    
37429                 }
37430             }
37431         }
37432         return ret;
37433         
37434     },
37435     
37436     
37437 // private
37438     factory : function(cfg)
37439     {
37440         
37441         var validRegions = Roo.bootstrap.layout.Border.regions;
37442
37443         var target = cfg.region;
37444         cfg.mgr = this;
37445         
37446         var r = Roo.bootstrap.layout;
37447         Roo.log(target);
37448         switch(target){
37449             case "north":
37450                 return new r.North(cfg);
37451             case "south":
37452                 return new r.South(cfg);
37453             case "east":
37454                 return new r.East(cfg);
37455             case "west":
37456                 return new r.West(cfg);
37457             case "center":
37458                 return new r.Center(cfg);
37459         }
37460         throw 'Layout region "'+target+'" not supported.';
37461     }
37462     
37463     
37464 });
37465  /*
37466  * Based on:
37467  * Ext JS Library 1.1.1
37468  * Copyright(c) 2006-2007, Ext JS, LLC.
37469  *
37470  * Originally Released Under LGPL - original licence link has changed is not relivant.
37471  *
37472  * Fork - LGPL
37473  * <script type="text/javascript">
37474  */
37475  
37476 /**
37477  * @class Roo.bootstrap.layout.Basic
37478  * @extends Roo.util.Observable
37479  * This class represents a lightweight region in a layout manager. This region does not move dom nodes
37480  * and does not have a titlebar, tabs or any other features. All it does is size and position 
37481  * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
37482  * @cfg {Roo.bootstrap.layout.Manager}   mgr The manager
37483  * @cfg {string}   region  the region that it inhabits..
37484  * @cfg {bool}   skipConfig skip config?
37485  * 
37486
37487  */
37488 Roo.bootstrap.layout.Basic = function(config){
37489     
37490     this.mgr = config.mgr;
37491     
37492     this.position = config.region;
37493     
37494     var skipConfig = config.skipConfig;
37495     
37496     this.events = {
37497         /**
37498          * @scope Roo.BasicLayoutRegion
37499          */
37500         
37501         /**
37502          * @event beforeremove
37503          * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
37504          * @param {Roo.LayoutRegion} this
37505          * @param {Roo.ContentPanel} panel The panel
37506          * @param {Object} e The cancel event object
37507          */
37508         "beforeremove" : true,
37509         /**
37510          * @event invalidated
37511          * Fires when the layout for this region is changed.
37512          * @param {Roo.LayoutRegion} this
37513          */
37514         "invalidated" : true,
37515         /**
37516          * @event visibilitychange
37517          * Fires when this region is shown or hidden 
37518          * @param {Roo.LayoutRegion} this
37519          * @param {Boolean} visibility true or false
37520          */
37521         "visibilitychange" : true,
37522         /**
37523          * @event paneladded
37524          * Fires when a panel is added. 
37525          * @param {Roo.LayoutRegion} this
37526          * @param {Roo.ContentPanel} panel The panel
37527          */
37528         "paneladded" : true,
37529         /**
37530          * @event panelremoved
37531          * Fires when a panel is removed. 
37532          * @param {Roo.LayoutRegion} this
37533          * @param {Roo.ContentPanel} panel The panel
37534          */
37535         "panelremoved" : true,
37536         /**
37537          * @event beforecollapse
37538          * Fires when this region before collapse.
37539          * @param {Roo.LayoutRegion} this
37540          */
37541         "beforecollapse" : true,
37542         /**
37543          * @event collapsed
37544          * Fires when this region is collapsed.
37545          * @param {Roo.LayoutRegion} this
37546          */
37547         "collapsed" : true,
37548         /**
37549          * @event expanded
37550          * Fires when this region is expanded.
37551          * @param {Roo.LayoutRegion} this
37552          */
37553         "expanded" : true,
37554         /**
37555          * @event slideshow
37556          * Fires when this region is slid into view.
37557          * @param {Roo.LayoutRegion} this
37558          */
37559         "slideshow" : true,
37560         /**
37561          * @event slidehide
37562          * Fires when this region slides out of view. 
37563          * @param {Roo.LayoutRegion} this
37564          */
37565         "slidehide" : true,
37566         /**
37567          * @event panelactivated
37568          * Fires when a panel is activated. 
37569          * @param {Roo.LayoutRegion} this
37570          * @param {Roo.ContentPanel} panel The activated panel
37571          */
37572         "panelactivated" : true,
37573         /**
37574          * @event resized
37575          * Fires when the user resizes this region. 
37576          * @param {Roo.LayoutRegion} this
37577          * @param {Number} newSize The new size (width for east/west, height for north/south)
37578          */
37579         "resized" : true
37580     };
37581     /** A collection of panels in this region. @type Roo.util.MixedCollection */
37582     this.panels = new Roo.util.MixedCollection();
37583     this.panels.getKey = this.getPanelId.createDelegate(this);
37584     this.box = null;
37585     this.activePanel = null;
37586     // ensure listeners are added...
37587     
37588     if (config.listeners || config.events) {
37589         Roo.bootstrap.layout.Basic.superclass.constructor.call(this, {
37590             listeners : config.listeners || {},
37591             events : config.events || {}
37592         });
37593     }
37594     
37595     if(skipConfig !== true){
37596         this.applyConfig(config);
37597     }
37598 };
37599
37600 Roo.extend(Roo.bootstrap.layout.Basic, Roo.util.Observable,
37601 {
37602     getPanelId : function(p){
37603         return p.getId();
37604     },
37605     
37606     applyConfig : function(config){
37607         this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
37608         this.config = config;
37609         
37610     },
37611     
37612     /**
37613      * Resizes the region to the specified size. For vertical regions (west, east) this adjusts 
37614      * the width, for horizontal (north, south) the height.
37615      * @param {Number} newSize The new width or height
37616      */
37617     resizeTo : function(newSize){
37618         var el = this.el ? this.el :
37619                  (this.activePanel ? this.activePanel.getEl() : null);
37620         if(el){
37621             switch(this.position){
37622                 case "east":
37623                 case "west":
37624                     el.setWidth(newSize);
37625                     this.fireEvent("resized", this, newSize);
37626                 break;
37627                 case "north":
37628                 case "south":
37629                     el.setHeight(newSize);
37630                     this.fireEvent("resized", this, newSize);
37631                 break;                
37632             }
37633         }
37634     },
37635     
37636     getBox : function(){
37637         return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
37638     },
37639     
37640     getMargins : function(){
37641         return this.margins;
37642     },
37643     
37644     updateBox : function(box){
37645         this.box = box;
37646         var el = this.activePanel.getEl();
37647         el.dom.style.left = box.x + "px";
37648         el.dom.style.top = box.y + "px";
37649         this.activePanel.setSize(box.width, box.height);
37650     },
37651     
37652     /**
37653      * Returns the container element for this region.
37654      * @return {Roo.Element}
37655      */
37656     getEl : function(){
37657         return this.activePanel;
37658     },
37659     
37660     /**
37661      * Returns true if this region is currently visible.
37662      * @return {Boolean}
37663      */
37664     isVisible : function(){
37665         return this.activePanel ? true : false;
37666     },
37667     
37668     setActivePanel : function(panel){
37669         panel = this.getPanel(panel);
37670         if(this.activePanel && this.activePanel != panel){
37671             this.activePanel.setActiveState(false);
37672             this.activePanel.getEl().setLeftTop(-10000,-10000);
37673         }
37674         this.activePanel = panel;
37675         panel.setActiveState(true);
37676         if(this.box){
37677             panel.setSize(this.box.width, this.box.height);
37678         }
37679         this.fireEvent("panelactivated", this, panel);
37680         this.fireEvent("invalidated");
37681     },
37682     
37683     /**
37684      * Show the specified panel.
37685      * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
37686      * @return {Roo.ContentPanel} The shown panel or null
37687      */
37688     showPanel : function(panel){
37689         panel = this.getPanel(panel);
37690         if(panel){
37691             this.setActivePanel(panel);
37692         }
37693         return panel;
37694     },
37695     
37696     /**
37697      * Get the active panel for this region.
37698      * @return {Roo.ContentPanel} The active panel or null
37699      */
37700     getActivePanel : function(){
37701         return this.activePanel;
37702     },
37703     
37704     /**
37705      * Add the passed ContentPanel(s)
37706      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
37707      * @return {Roo.ContentPanel} The panel added (if only one was added)
37708      */
37709     add : function(panel){
37710         if(arguments.length > 1){
37711             for(var i = 0, len = arguments.length; i < len; i++) {
37712                 this.add(arguments[i]);
37713             }
37714             return null;
37715         }
37716         if(this.hasPanel(panel)){
37717             this.showPanel(panel);
37718             return panel;
37719         }
37720         var el = panel.getEl();
37721         if(el.dom.parentNode != this.mgr.el.dom){
37722             this.mgr.el.dom.appendChild(el.dom);
37723         }
37724         if(panel.setRegion){
37725             panel.setRegion(this);
37726         }
37727         this.panels.add(panel);
37728         el.setStyle("position", "absolute");
37729         if(!panel.background){
37730             this.setActivePanel(panel);
37731             if(this.config.initialSize && this.panels.getCount()==1){
37732                 this.resizeTo(this.config.initialSize);
37733             }
37734         }
37735         this.fireEvent("paneladded", this, panel);
37736         return panel;
37737     },
37738     
37739     /**
37740      * Returns true if the panel is in this region.
37741      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
37742      * @return {Boolean}
37743      */
37744     hasPanel : function(panel){
37745         if(typeof panel == "object"){ // must be panel obj
37746             panel = panel.getId();
37747         }
37748         return this.getPanel(panel) ? true : false;
37749     },
37750     
37751     /**
37752      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
37753      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
37754      * @param {Boolean} preservePanel Overrides the config preservePanel option
37755      * @return {Roo.ContentPanel} The panel that was removed
37756      */
37757     remove : function(panel, preservePanel){
37758         panel = this.getPanel(panel);
37759         if(!panel){
37760             return null;
37761         }
37762         var e = {};
37763         this.fireEvent("beforeremove", this, panel, e);
37764         if(e.cancel === true){
37765             return null;
37766         }
37767         var panelId = panel.getId();
37768         this.panels.removeKey(panelId);
37769         return panel;
37770     },
37771     
37772     /**
37773      * Returns the panel specified or null if it's not in this region.
37774      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
37775      * @return {Roo.ContentPanel}
37776      */
37777     getPanel : function(id){
37778         if(typeof id == "object"){ // must be panel obj
37779             return id;
37780         }
37781         return this.panels.get(id);
37782     },
37783     
37784     /**
37785      * Returns this regions position (north/south/east/west/center).
37786      * @return {String} 
37787      */
37788     getPosition: function(){
37789         return this.position;    
37790     }
37791 });/*
37792  * Based on:
37793  * Ext JS Library 1.1.1
37794  * Copyright(c) 2006-2007, Ext JS, LLC.
37795  *
37796  * Originally Released Under LGPL - original licence link has changed is not relivant.
37797  *
37798  * Fork - LGPL
37799  * <script type="text/javascript">
37800  */
37801  
37802 /**
37803  * @class Roo.bootstrap.layout.Region
37804  * @extends Roo.bootstrap.layout.Basic
37805  * This class represents a region in a layout manager.
37806  
37807  * @cfg {Object}    margins         Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
37808  * @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})
37809  * @cfg {String}    tabPosition     (top|bottom) "top" or "bottom" (defaults to "bottom")
37810  * @cfg {Boolean}   alwaysShowTabs  True to always display tabs even when there is only 1 panel (defaults to false)
37811  * @cfg {Boolean}   autoScroll      True to enable overflow scrolling (defaults to false)
37812  * @cfg {Boolean}   titlebar        True to display a title bar (defaults to true)
37813  * @cfg {String}    title           The title for the region (overrides panel titles)
37814  * @cfg {Boolean}   animate         True to animate expand/collapse (defaults to false)
37815  * @cfg {Boolean}   autoHide        False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
37816  * @cfg {Boolean}   preservePanels  True to preserve removed panels so they can be readded later (defaults to false)
37817  * @cfg {Boolean}   closeOnTab      True to place the close icon on the tabs instead of the region titlebar (defaults to false)
37818  * @cfg {Boolean}   hideTabs        True to hide the tab strip (defaults to false)
37819  * @cfg {Boolean}   resizeTabs      True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
37820  *                      the space available, similar to FireFox 1.5 tabs (defaults to false)
37821  * @cfg {Number}    minTabWidth     The minimum tab width (defaults to 40)
37822  * @cfg {Number}    preferredTabWidth The preferred tab width (defaults to 150)
37823  * @cfg {String}    overflow       (hidden|visible) if you have menus in the region, then you need to set this to visible.
37824
37825  * @cfg {Boolean}   hidden          True to start the region hidden (defaults to false)
37826  * @cfg {Boolean}   hideWhenEmpty   True to hide the region when it has no panels
37827  * @cfg {Boolean}   disableTabTips  True to disable tab tooltips
37828  * @cfg {Number}    width           For East/West panels
37829  * @cfg {Number}    height          For North/South panels
37830  * @cfg {Boolean}   split           To show the splitter
37831  * @cfg {Boolean}   toolbar         xtype configuration for a toolbar - shows on right of tabbar
37832  * 
37833  * @cfg {string}   cls             Extra CSS classes to add to region
37834  * 
37835  * @cfg {Roo.bootstrap.layout.Manager}   mgr The manager
37836  * @cfg {string}   region  the region that it inhabits..
37837  *
37838
37839  * @xxxcfg {Boolean}   collapsible     DISABLED False to disable collapsing (defaults to true)
37840  * @xxxcfg {Boolean}   collapsed       DISABLED True to set the initial display to collapsed (defaults to false)
37841
37842  * @xxxcfg {String}    collapsedTitle  DISABLED Optional string message to display in the collapsed block of a north or south region
37843  * @xxxxcfg {Boolean}   floatable       DISABLED False to disable floating (defaults to true)
37844  * @xxxxcfg {Boolean}   showPin         True to show a pin button NOT SUPPORTED YET
37845  */
37846 Roo.bootstrap.layout.Region = function(config)
37847 {
37848     this.applyConfig(config);
37849
37850     var mgr = config.mgr;
37851     var pos = config.region;
37852     config.skipConfig = true;
37853     Roo.bootstrap.layout.Region.superclass.constructor.call(this, config);
37854     
37855     if (mgr.el) {
37856         this.onRender(mgr.el);   
37857     }
37858      
37859     this.visible = true;
37860     this.collapsed = false;
37861     this.unrendered_panels = [];
37862 };
37863
37864 Roo.extend(Roo.bootstrap.layout.Region, Roo.bootstrap.layout.Basic, {
37865
37866     position: '', // set by wrapper (eg. north/south etc..)
37867     unrendered_panels : null,  // unrendered panels.
37868     
37869     tabPosition : false,
37870     
37871     mgr: false, // points to 'Border'
37872     
37873     
37874     createBody : function(){
37875         /** This region's body element 
37876         * @type Roo.Element */
37877         this.bodyEl = this.el.createChild({
37878                 tag: "div",
37879                 cls: "roo-layout-panel-body tab-content" // bootstrap added...
37880         });
37881     },
37882
37883     onRender: function(ctr, pos)
37884     {
37885         var dh = Roo.DomHelper;
37886         /** This region's container element 
37887         * @type Roo.Element */
37888         this.el = dh.append(ctr.dom, {
37889                 tag: "div",
37890                 cls: (this.config.cls || '') + " roo-layout-region roo-layout-panel roo-layout-panel-" + this.position
37891             }, true);
37892         /** This region's title element 
37893         * @type Roo.Element */
37894     
37895         this.titleEl = dh.append(this.el.dom,  {
37896                 tag: "div",
37897                 unselectable: "on",
37898                 cls: "roo-unselectable roo-layout-panel-hd breadcrumb roo-layout-title-" + this.position,
37899                 children:[
37900                     {tag: "span", cls: "roo-unselectable roo-layout-panel-hd-text", unselectable: "on", html: "&#160;"},
37901                     {tag: "div", cls: "roo-unselectable roo-layout-panel-hd-tools", unselectable: "on"}
37902                 ]
37903             }, true);
37904         
37905         this.titleEl.enableDisplayMode();
37906         /** This region's title text element 
37907         * @type HTMLElement */
37908         this.titleTextEl = this.titleEl.dom.firstChild;
37909         this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
37910         /*
37911         this.closeBtn = this.createTool(this.tools.dom, "roo-layout-close");
37912         this.closeBtn.enableDisplayMode();
37913         this.closeBtn.on("click", this.closeClicked, this);
37914         this.closeBtn.hide();
37915     */
37916         this.createBody(this.config);
37917         if(this.config.hideWhenEmpty){
37918             this.hide();
37919             this.on("paneladded", this.validateVisibility, this);
37920             this.on("panelremoved", this.validateVisibility, this);
37921         }
37922         if(this.autoScroll){
37923             this.bodyEl.setStyle("overflow", "auto");
37924         }else{
37925             this.bodyEl.setStyle("overflow", this.config.overflow || 'hidden');
37926         }
37927         //if(c.titlebar !== false){
37928             if((!this.config.titlebar && !this.config.title) || this.config.titlebar === false){
37929                 this.titleEl.hide();
37930             }else{
37931                 this.titleEl.show();
37932                 if(this.config.title){
37933                     this.titleTextEl.innerHTML = this.config.title;
37934                 }
37935             }
37936         //}
37937         if(this.config.collapsed){
37938             this.collapse(true);
37939         }
37940         if(this.config.hidden){
37941             this.hide();
37942         }
37943         
37944         if (this.unrendered_panels && this.unrendered_panels.length) {
37945             for (var i =0;i< this.unrendered_panels.length; i++) {
37946                 this.add(this.unrendered_panels[i]);
37947             }
37948             this.unrendered_panels = null;
37949             
37950         }
37951         
37952     },
37953     
37954     applyConfig : function(c)
37955     {
37956         /*
37957          *if(c.collapsible && this.position != "center" && !this.collapsedEl){
37958             var dh = Roo.DomHelper;
37959             if(c.titlebar !== false){
37960                 this.collapseBtn = this.createTool(this.tools.dom, "roo-layout-collapse-"+this.position);
37961                 this.collapseBtn.on("click", this.collapse, this);
37962                 this.collapseBtn.enableDisplayMode();
37963                 /*
37964                 if(c.showPin === true || this.showPin){
37965                     this.stickBtn = this.createTool(this.tools.dom, "roo-layout-stick");
37966                     this.stickBtn.enableDisplayMode();
37967                     this.stickBtn.on("click", this.expand, this);
37968                     this.stickBtn.hide();
37969                 }
37970                 
37971             }
37972             */
37973             /** This region's collapsed element
37974             * @type Roo.Element */
37975             /*
37976              *
37977             this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
37978                 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
37979             ]}, true);
37980             
37981             if(c.floatable !== false){
37982                this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
37983                this.collapsedEl.on("click", this.collapseClick, this);
37984             }
37985
37986             if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
37987                 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
37988                    id: "message", unselectable: "on", style:{"float":"left"}});
37989                this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
37990              }
37991             this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
37992             this.expandBtn.on("click", this.expand, this);
37993             
37994         }
37995         
37996         if(this.collapseBtn){
37997             this.collapseBtn.setVisible(c.collapsible == true);
37998         }
37999         
38000         this.cmargins = c.cmargins || this.cmargins ||
38001                          (this.position == "west" || this.position == "east" ?
38002                              {top: 0, left: 2, right:2, bottom: 0} :
38003                              {top: 2, left: 0, right:0, bottom: 2});
38004         */
38005         this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
38006         
38007         
38008         this.tabPosition = [ 'top','bottom', 'west'].indexOf(c.tabPosition) > -1 ? c.tabPosition : "top";
38009         
38010         this.autoScroll = c.autoScroll || false;
38011         
38012         
38013        
38014         
38015         this.duration = c.duration || .30;
38016         this.slideDuration = c.slideDuration || .45;
38017         this.config = c;
38018        
38019     },
38020     /**
38021      * Returns true if this region is currently visible.
38022      * @return {Boolean}
38023      */
38024     isVisible : function(){
38025         return this.visible;
38026     },
38027
38028     /**
38029      * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
38030      * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&amp;#160;")
38031      */
38032     //setCollapsedTitle : function(title){
38033     //    title = title || "&#160;";
38034      //   if(this.collapsedTitleTextEl){
38035       //      this.collapsedTitleTextEl.innerHTML = title;
38036        // }
38037     //},
38038
38039     getBox : function(){
38040         var b;
38041       //  if(!this.collapsed){
38042             b = this.el.getBox(false, true);
38043        // }else{
38044           //  b = this.collapsedEl.getBox(false, true);
38045         //}
38046         return b;
38047     },
38048
38049     getMargins : function(){
38050         return this.margins;
38051         //return this.collapsed ? this.cmargins : this.margins;
38052     },
38053 /*
38054     highlight : function(){
38055         this.el.addClass("x-layout-panel-dragover");
38056     },
38057
38058     unhighlight : function(){
38059         this.el.removeClass("x-layout-panel-dragover");
38060     },
38061 */
38062     updateBox : function(box)
38063     {
38064         if (!this.bodyEl) {
38065             return; // not rendered yet..
38066         }
38067         
38068         this.box = box;
38069         if(!this.collapsed){
38070             this.el.dom.style.left = box.x + "px";
38071             this.el.dom.style.top = box.y + "px";
38072             this.updateBody(box.width, box.height);
38073         }else{
38074             this.collapsedEl.dom.style.left = box.x + "px";
38075             this.collapsedEl.dom.style.top = box.y + "px";
38076             this.collapsedEl.setSize(box.width, box.height);
38077         }
38078         if(this.tabs){
38079             this.tabs.autoSizeTabs();
38080         }
38081     },
38082
38083     updateBody : function(w, h)
38084     {
38085         if(w !== null){
38086             this.el.setWidth(w);
38087             w -= this.el.getBorderWidth("rl");
38088             if(this.config.adjustments){
38089                 w += this.config.adjustments[0];
38090             }
38091         }
38092         if(h !== null && h > 0){
38093             this.el.setHeight(h);
38094             h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
38095             h -= this.el.getBorderWidth("tb");
38096             if(this.config.adjustments){
38097                 h += this.config.adjustments[1];
38098             }
38099             this.bodyEl.setHeight(h);
38100             if(this.tabs){
38101                 h = this.tabs.syncHeight(h);
38102             }
38103         }
38104         if(this.panelSize){
38105             w = w !== null ? w : this.panelSize.width;
38106             h = h !== null ? h : this.panelSize.height;
38107         }
38108         if(this.activePanel){
38109             var el = this.activePanel.getEl();
38110             w = w !== null ? w : el.getWidth();
38111             h = h !== null ? h : el.getHeight();
38112             this.panelSize = {width: w, height: h};
38113             this.activePanel.setSize(w, h);
38114         }
38115         if(Roo.isIE && this.tabs){
38116             this.tabs.el.repaint();
38117         }
38118     },
38119
38120     /**
38121      * Returns the container element for this region.
38122      * @return {Roo.Element}
38123      */
38124     getEl : function(){
38125         return this.el;
38126     },
38127
38128     /**
38129      * Hides this region.
38130      */
38131     hide : function(){
38132         //if(!this.collapsed){
38133             this.el.dom.style.left = "-2000px";
38134             this.el.hide();
38135         //}else{
38136          //   this.collapsedEl.dom.style.left = "-2000px";
38137          //   this.collapsedEl.hide();
38138        // }
38139         this.visible = false;
38140         this.fireEvent("visibilitychange", this, false);
38141     },
38142
38143     /**
38144      * Shows this region if it was previously hidden.
38145      */
38146     show : function(){
38147         //if(!this.collapsed){
38148             this.el.show();
38149         //}else{
38150         //    this.collapsedEl.show();
38151        // }
38152         this.visible = true;
38153         this.fireEvent("visibilitychange", this, true);
38154     },
38155 /*
38156     closeClicked : function(){
38157         if(this.activePanel){
38158             this.remove(this.activePanel);
38159         }
38160     },
38161
38162     collapseClick : function(e){
38163         if(this.isSlid){
38164            e.stopPropagation();
38165            this.slideIn();
38166         }else{
38167            e.stopPropagation();
38168            this.slideOut();
38169         }
38170     },
38171 */
38172     /**
38173      * Collapses this region.
38174      * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
38175      */
38176     /*
38177     collapse : function(skipAnim, skipCheck = false){
38178         if(this.collapsed) {
38179             return;
38180         }
38181         
38182         if(skipCheck || this.fireEvent("beforecollapse", this) != false){
38183             
38184             this.collapsed = true;
38185             if(this.split){
38186                 this.split.el.hide();
38187             }
38188             if(this.config.animate && skipAnim !== true){
38189                 this.fireEvent("invalidated", this);
38190                 this.animateCollapse();
38191             }else{
38192                 this.el.setLocation(-20000,-20000);
38193                 this.el.hide();
38194                 this.collapsedEl.show();
38195                 this.fireEvent("collapsed", this);
38196                 this.fireEvent("invalidated", this);
38197             }
38198         }
38199         
38200     },
38201 */
38202     animateCollapse : function(){
38203         // overridden
38204     },
38205
38206     /**
38207      * Expands this region if it was previously collapsed.
38208      * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
38209      * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
38210      */
38211     /*
38212     expand : function(e, skipAnim){
38213         if(e) {
38214             e.stopPropagation();
38215         }
38216         if(!this.collapsed || this.el.hasActiveFx()) {
38217             return;
38218         }
38219         if(this.isSlid){
38220             this.afterSlideIn();
38221             skipAnim = true;
38222         }
38223         this.collapsed = false;
38224         if(this.config.animate && skipAnim !== true){
38225             this.animateExpand();
38226         }else{
38227             this.el.show();
38228             if(this.split){
38229                 this.split.el.show();
38230             }
38231             this.collapsedEl.setLocation(-2000,-2000);
38232             this.collapsedEl.hide();
38233             this.fireEvent("invalidated", this);
38234             this.fireEvent("expanded", this);
38235         }
38236     },
38237 */
38238     animateExpand : function(){
38239         // overridden
38240     },
38241
38242     initTabs : function()
38243     {
38244         //this.bodyEl.setStyle("overflow", "hidden"); -- this is set in render?
38245         
38246         var ts = new Roo.bootstrap.panel.Tabs({
38247             el: this.bodyEl.dom,
38248             region : this,
38249             tabPosition: this.tabPosition ? this.tabPosition  : 'top',
38250             disableTooltips: this.config.disableTabTips,
38251             toolbar : this.config.toolbar
38252         });
38253         
38254         if(this.config.hideTabs){
38255             ts.stripWrap.setDisplayed(false);
38256         }
38257         this.tabs = ts;
38258         ts.resizeTabs = this.config.resizeTabs === true;
38259         ts.minTabWidth = this.config.minTabWidth || 40;
38260         ts.maxTabWidth = this.config.maxTabWidth || 250;
38261         ts.preferredTabWidth = this.config.preferredTabWidth || 150;
38262         ts.monitorResize = false;
38263         //ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden"); // this is set in render?
38264         ts.bodyEl.addClass('roo-layout-tabs-body');
38265         this.panels.each(this.initPanelAsTab, this);
38266     },
38267
38268     initPanelAsTab : function(panel){
38269         var ti = this.tabs.addTab(
38270             panel.getEl().id,
38271             panel.getTitle(),
38272             null,
38273             this.config.closeOnTab && panel.isClosable(),
38274             panel.tpl
38275         );
38276         if(panel.tabTip !== undefined){
38277             ti.setTooltip(panel.tabTip);
38278         }
38279         ti.on("activate", function(){
38280               this.setActivePanel(panel);
38281         }, this);
38282         
38283         if(this.config.closeOnTab){
38284             ti.on("beforeclose", function(t, e){
38285                 e.cancel = true;
38286                 this.remove(panel);
38287             }, this);
38288         }
38289         
38290         panel.tabItem = ti;
38291         
38292         return ti;
38293     },
38294
38295     updatePanelTitle : function(panel, title)
38296     {
38297         if(this.activePanel == panel){
38298             this.updateTitle(title);
38299         }
38300         if(this.tabs){
38301             var ti = this.tabs.getTab(panel.getEl().id);
38302             ti.setText(title);
38303             if(panel.tabTip !== undefined){
38304                 ti.setTooltip(panel.tabTip);
38305             }
38306         }
38307     },
38308
38309     updateTitle : function(title){
38310         if(this.titleTextEl && !this.config.title){
38311             this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : "&#160;");
38312         }
38313     },
38314
38315     setActivePanel : function(panel)
38316     {
38317         panel = this.getPanel(panel);
38318         if(this.activePanel && this.activePanel != panel){
38319             if(this.activePanel.setActiveState(false) === false){
38320                 return;
38321             }
38322         }
38323         this.activePanel = panel;
38324         panel.setActiveState(true);
38325         if(this.panelSize){
38326             panel.setSize(this.panelSize.width, this.panelSize.height);
38327         }
38328         if(this.closeBtn){
38329             this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
38330         }
38331         this.updateTitle(panel.getTitle());
38332         if(this.tabs){
38333             this.fireEvent("invalidated", this);
38334         }
38335         this.fireEvent("panelactivated", this, panel);
38336     },
38337
38338     /**
38339      * Shows the specified panel.
38340      * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
38341      * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
38342      */
38343     showPanel : function(panel)
38344     {
38345         panel = this.getPanel(panel);
38346         if(panel){
38347             if(this.tabs){
38348                 var tab = this.tabs.getTab(panel.getEl().id);
38349                 if(tab.isHidden()){
38350                     this.tabs.unhideTab(tab.id);
38351                 }
38352                 tab.activate();
38353             }else{
38354                 this.setActivePanel(panel);
38355             }
38356         }
38357         return panel;
38358     },
38359
38360     /**
38361      * Get the active panel for this region.
38362      * @return {Roo.ContentPanel} The active panel or null
38363      */
38364     getActivePanel : function(){
38365         return this.activePanel;
38366     },
38367
38368     validateVisibility : function(){
38369         if(this.panels.getCount() < 1){
38370             this.updateTitle("&#160;");
38371             this.closeBtn.hide();
38372             this.hide();
38373         }else{
38374             if(!this.isVisible()){
38375                 this.show();
38376             }
38377         }
38378     },
38379
38380     /**
38381      * Adds the passed ContentPanel(s) to this region.
38382      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
38383      * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
38384      */
38385     add : function(panel)
38386     {
38387         if(arguments.length > 1){
38388             for(var i = 0, len = arguments.length; i < len; i++) {
38389                 this.add(arguments[i]);
38390             }
38391             return null;
38392         }
38393         
38394         // if we have not been rendered yet, then we can not really do much of this..
38395         if (!this.bodyEl) {
38396             this.unrendered_panels.push(panel);
38397             return panel;
38398         }
38399         
38400         
38401         
38402         
38403         if(this.hasPanel(panel)){
38404             this.showPanel(panel);
38405             return panel;
38406         }
38407         panel.setRegion(this);
38408         this.panels.add(panel);
38409        /* if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
38410             // sinle panel - no tab...?? would it not be better to render it with the tabs,
38411             // and hide them... ???
38412             this.bodyEl.dom.appendChild(panel.getEl().dom);
38413             if(panel.background !== true){
38414                 this.setActivePanel(panel);
38415             }
38416             this.fireEvent("paneladded", this, panel);
38417             return panel;
38418         }
38419         */
38420         if(!this.tabs){
38421             this.initTabs();
38422         }else{
38423             this.initPanelAsTab(panel);
38424         }
38425         
38426         
38427         if(panel.background !== true){
38428             this.tabs.activate(panel.getEl().id);
38429         }
38430         this.fireEvent("paneladded", this, panel);
38431         return panel;
38432     },
38433
38434     /**
38435      * Hides the tab for the specified panel.
38436      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
38437      */
38438     hidePanel : function(panel){
38439         if(this.tabs && (panel = this.getPanel(panel))){
38440             this.tabs.hideTab(panel.getEl().id);
38441         }
38442     },
38443
38444     /**
38445      * Unhides the tab for a previously hidden panel.
38446      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
38447      */
38448     unhidePanel : function(panel){
38449         if(this.tabs && (panel = this.getPanel(panel))){
38450             this.tabs.unhideTab(panel.getEl().id);
38451         }
38452     },
38453
38454     clearPanels : function(){
38455         while(this.panels.getCount() > 0){
38456              this.remove(this.panels.first());
38457         }
38458     },
38459
38460     /**
38461      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
38462      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
38463      * @param {Boolean} preservePanel Overrides the config preservePanel option
38464      * @return {Roo.ContentPanel} The panel that was removed
38465      */
38466     remove : function(panel, preservePanel)
38467     {
38468         panel = this.getPanel(panel);
38469         if(!panel){
38470             return null;
38471         }
38472         var e = {};
38473         this.fireEvent("beforeremove", this, panel, e);
38474         if(e.cancel === true){
38475             return null;
38476         }
38477         preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
38478         var panelId = panel.getId();
38479         this.panels.removeKey(panelId);
38480         if(preservePanel){
38481             document.body.appendChild(panel.getEl().dom);
38482         }
38483         if(this.tabs){
38484             this.tabs.removeTab(panel.getEl().id);
38485         }else if (!preservePanel){
38486             this.bodyEl.dom.removeChild(panel.getEl().dom);
38487         }
38488         if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
38489             var p = this.panels.first();
38490             var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
38491             tempEl.appendChild(p.getEl().dom);
38492             this.bodyEl.update("");
38493             this.bodyEl.dom.appendChild(p.getEl().dom);
38494             tempEl = null;
38495             this.updateTitle(p.getTitle());
38496             this.tabs = null;
38497             this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
38498             this.setActivePanel(p);
38499         }
38500         panel.setRegion(null);
38501         if(this.activePanel == panel){
38502             this.activePanel = null;
38503         }
38504         if(this.config.autoDestroy !== false && preservePanel !== true){
38505             try{panel.destroy();}catch(e){}
38506         }
38507         this.fireEvent("panelremoved", this, panel);
38508         return panel;
38509     },
38510
38511     /**
38512      * Returns the TabPanel component used by this region
38513      * @return {Roo.TabPanel}
38514      */
38515     getTabs : function(){
38516         return this.tabs;
38517     },
38518
38519     createTool : function(parentEl, className){
38520         var btn = Roo.DomHelper.append(parentEl, {
38521             tag: "div",
38522             cls: "x-layout-tools-button",
38523             children: [ {
38524                 tag: "div",
38525                 cls: "roo-layout-tools-button-inner " + className,
38526                 html: "&#160;"
38527             }]
38528         }, true);
38529         btn.addClassOnOver("roo-layout-tools-button-over");
38530         return btn;
38531     }
38532 });/*
38533  * Based on:
38534  * Ext JS Library 1.1.1
38535  * Copyright(c) 2006-2007, Ext JS, LLC.
38536  *
38537  * Originally Released Under LGPL - original licence link has changed is not relivant.
38538  *
38539  * Fork - LGPL
38540  * <script type="text/javascript">
38541  */
38542  
38543
38544
38545 /**
38546  * @class Roo.SplitLayoutRegion
38547  * @extends Roo.LayoutRegion
38548  * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
38549  */
38550 Roo.bootstrap.layout.Split = function(config){
38551     this.cursor = config.cursor;
38552     Roo.bootstrap.layout.Split.superclass.constructor.call(this, config);
38553 };
38554
38555 Roo.extend(Roo.bootstrap.layout.Split, Roo.bootstrap.layout.Region,
38556 {
38557     splitTip : "Drag to resize.",
38558     collapsibleSplitTip : "Drag to resize. Double click to hide.",
38559     useSplitTips : false,
38560
38561     applyConfig : function(config){
38562         Roo.bootstrap.layout.Split.superclass.applyConfig.call(this, config);
38563     },
38564     
38565     onRender : function(ctr,pos) {
38566         
38567         Roo.bootstrap.layout.Split.superclass.onRender.call(this, ctr,pos);
38568         if(!this.config.split){
38569             return;
38570         }
38571         if(!this.split){
38572             
38573             var splitEl = Roo.DomHelper.append(ctr.dom,  {
38574                             tag: "div",
38575                             id: this.el.id + "-split",
38576                             cls: "roo-layout-split roo-layout-split-"+this.position,
38577                             html: "&#160;"
38578             });
38579             /** The SplitBar for this region 
38580             * @type Roo.SplitBar */
38581             // does not exist yet...
38582             Roo.log([this.position, this.orientation]);
38583             
38584             this.split = new Roo.bootstrap.SplitBar({
38585                 dragElement : splitEl,
38586                 resizingElement: this.el,
38587                 orientation : this.orientation
38588             });
38589             
38590             this.split.on("moved", this.onSplitMove, this);
38591             this.split.useShim = this.config.useShim === true;
38592             this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
38593             if(this.useSplitTips){
38594                 this.split.el.dom.title = this.config.collapsible ? this.collapsibleSplitTip : this.splitTip;
38595             }
38596             //if(config.collapsible){
38597             //    this.split.el.on("dblclick", this.collapse,  this);
38598             //}
38599         }
38600         if(typeof this.config.minSize != "undefined"){
38601             this.split.minSize = this.config.minSize;
38602         }
38603         if(typeof this.config.maxSize != "undefined"){
38604             this.split.maxSize = this.config.maxSize;
38605         }
38606         if(this.config.hideWhenEmpty || this.config.hidden || this.config.collapsed){
38607             this.hideSplitter();
38608         }
38609         
38610     },
38611
38612     getHMaxSize : function(){
38613          var cmax = this.config.maxSize || 10000;
38614          var center = this.mgr.getRegion("center");
38615          return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
38616     },
38617
38618     getVMaxSize : function(){
38619          var cmax = this.config.maxSize || 10000;
38620          var center = this.mgr.getRegion("center");
38621          return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
38622     },
38623
38624     onSplitMove : function(split, newSize){
38625         this.fireEvent("resized", this, newSize);
38626     },
38627     
38628     /** 
38629      * Returns the {@link Roo.SplitBar} for this region.
38630      * @return {Roo.SplitBar}
38631      */
38632     getSplitBar : function(){
38633         return this.split;
38634     },
38635     
38636     hide : function(){
38637         this.hideSplitter();
38638         Roo.bootstrap.layout.Split.superclass.hide.call(this);
38639     },
38640
38641     hideSplitter : function(){
38642         if(this.split){
38643             this.split.el.setLocation(-2000,-2000);
38644             this.split.el.hide();
38645         }
38646     },
38647
38648     show : function(){
38649         if(this.split){
38650             this.split.el.show();
38651         }
38652         Roo.bootstrap.layout.Split.superclass.show.call(this);
38653     },
38654     
38655     beforeSlide: function(){
38656         if(Roo.isGecko){// firefox overflow auto bug workaround
38657             this.bodyEl.clip();
38658             if(this.tabs) {
38659                 this.tabs.bodyEl.clip();
38660             }
38661             if(this.activePanel){
38662                 this.activePanel.getEl().clip();
38663                 
38664                 if(this.activePanel.beforeSlide){
38665                     this.activePanel.beforeSlide();
38666                 }
38667             }
38668         }
38669     },
38670     
38671     afterSlide : function(){
38672         if(Roo.isGecko){// firefox overflow auto bug workaround
38673             this.bodyEl.unclip();
38674             if(this.tabs) {
38675                 this.tabs.bodyEl.unclip();
38676             }
38677             if(this.activePanel){
38678                 this.activePanel.getEl().unclip();
38679                 if(this.activePanel.afterSlide){
38680                     this.activePanel.afterSlide();
38681                 }
38682             }
38683         }
38684     },
38685
38686     initAutoHide : function(){
38687         if(this.autoHide !== false){
38688             if(!this.autoHideHd){
38689                 var st = new Roo.util.DelayedTask(this.slideIn, this);
38690                 this.autoHideHd = {
38691                     "mouseout": function(e){
38692                         if(!e.within(this.el, true)){
38693                             st.delay(500);
38694                         }
38695                     },
38696                     "mouseover" : function(e){
38697                         st.cancel();
38698                     },
38699                     scope : this
38700                 };
38701             }
38702             this.el.on(this.autoHideHd);
38703         }
38704     },
38705
38706     clearAutoHide : function(){
38707         if(this.autoHide !== false){
38708             this.el.un("mouseout", this.autoHideHd.mouseout);
38709             this.el.un("mouseover", this.autoHideHd.mouseover);
38710         }
38711     },
38712
38713     clearMonitor : function(){
38714         Roo.get(document).un("click", this.slideInIf, this);
38715     },
38716
38717     // these names are backwards but not changed for compat
38718     slideOut : function(){
38719         if(this.isSlid || this.el.hasActiveFx()){
38720             return;
38721         }
38722         this.isSlid = true;
38723         if(this.collapseBtn){
38724             this.collapseBtn.hide();
38725         }
38726         this.closeBtnState = this.closeBtn.getStyle('display');
38727         this.closeBtn.hide();
38728         if(this.stickBtn){
38729             this.stickBtn.show();
38730         }
38731         this.el.show();
38732         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
38733         this.beforeSlide();
38734         this.el.setStyle("z-index", 10001);
38735         this.el.slideIn(this.getSlideAnchor(), {
38736             callback: function(){
38737                 this.afterSlide();
38738                 this.initAutoHide();
38739                 Roo.get(document).on("click", this.slideInIf, this);
38740                 this.fireEvent("slideshow", this);
38741             },
38742             scope: this,
38743             block: true
38744         });
38745     },
38746
38747     afterSlideIn : function(){
38748         this.clearAutoHide();
38749         this.isSlid = false;
38750         this.clearMonitor();
38751         this.el.setStyle("z-index", "");
38752         if(this.collapseBtn){
38753             this.collapseBtn.show();
38754         }
38755         this.closeBtn.setStyle('display', this.closeBtnState);
38756         if(this.stickBtn){
38757             this.stickBtn.hide();
38758         }
38759         this.fireEvent("slidehide", this);
38760     },
38761
38762     slideIn : function(cb){
38763         if(!this.isSlid || this.el.hasActiveFx()){
38764             Roo.callback(cb);
38765             return;
38766         }
38767         this.isSlid = false;
38768         this.beforeSlide();
38769         this.el.slideOut(this.getSlideAnchor(), {
38770             callback: function(){
38771                 this.el.setLeftTop(-10000, -10000);
38772                 this.afterSlide();
38773                 this.afterSlideIn();
38774                 Roo.callback(cb);
38775             },
38776             scope: this,
38777             block: true
38778         });
38779     },
38780     
38781     slideInIf : function(e){
38782         if(!e.within(this.el)){
38783             this.slideIn();
38784         }
38785     },
38786
38787     animateCollapse : function(){
38788         this.beforeSlide();
38789         this.el.setStyle("z-index", 20000);
38790         var anchor = this.getSlideAnchor();
38791         this.el.slideOut(anchor, {
38792             callback : function(){
38793                 this.el.setStyle("z-index", "");
38794                 this.collapsedEl.slideIn(anchor, {duration:.3});
38795                 this.afterSlide();
38796                 this.el.setLocation(-10000,-10000);
38797                 this.el.hide();
38798                 this.fireEvent("collapsed", this);
38799             },
38800             scope: this,
38801             block: true
38802         });
38803     },
38804
38805     animateExpand : function(){
38806         this.beforeSlide();
38807         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
38808         this.el.setStyle("z-index", 20000);
38809         this.collapsedEl.hide({
38810             duration:.1
38811         });
38812         this.el.slideIn(this.getSlideAnchor(), {
38813             callback : function(){
38814                 this.el.setStyle("z-index", "");
38815                 this.afterSlide();
38816                 if(this.split){
38817                     this.split.el.show();
38818                 }
38819                 this.fireEvent("invalidated", this);
38820                 this.fireEvent("expanded", this);
38821             },
38822             scope: this,
38823             block: true
38824         });
38825     },
38826
38827     anchors : {
38828         "west" : "left",
38829         "east" : "right",
38830         "north" : "top",
38831         "south" : "bottom"
38832     },
38833
38834     sanchors : {
38835         "west" : "l",
38836         "east" : "r",
38837         "north" : "t",
38838         "south" : "b"
38839     },
38840
38841     canchors : {
38842         "west" : "tl-tr",
38843         "east" : "tr-tl",
38844         "north" : "tl-bl",
38845         "south" : "bl-tl"
38846     },
38847
38848     getAnchor : function(){
38849         return this.anchors[this.position];
38850     },
38851
38852     getCollapseAnchor : function(){
38853         return this.canchors[this.position];
38854     },
38855
38856     getSlideAnchor : function(){
38857         return this.sanchors[this.position];
38858     },
38859
38860     getAlignAdj : function(){
38861         var cm = this.cmargins;
38862         switch(this.position){
38863             case "west":
38864                 return [0, 0];
38865             break;
38866             case "east":
38867                 return [0, 0];
38868             break;
38869             case "north":
38870                 return [0, 0];
38871             break;
38872             case "south":
38873                 return [0, 0];
38874             break;
38875         }
38876     },
38877
38878     getExpandAdj : function(){
38879         var c = this.collapsedEl, cm = this.cmargins;
38880         switch(this.position){
38881             case "west":
38882                 return [-(cm.right+c.getWidth()+cm.left), 0];
38883             break;
38884             case "east":
38885                 return [cm.right+c.getWidth()+cm.left, 0];
38886             break;
38887             case "north":
38888                 return [0, -(cm.top+cm.bottom+c.getHeight())];
38889             break;
38890             case "south":
38891                 return [0, cm.top+cm.bottom+c.getHeight()];
38892             break;
38893         }
38894     }
38895 });/*
38896  * Based on:
38897  * Ext JS Library 1.1.1
38898  * Copyright(c) 2006-2007, Ext JS, LLC.
38899  *
38900  * Originally Released Under LGPL - original licence link has changed is not relivant.
38901  *
38902  * Fork - LGPL
38903  * <script type="text/javascript">
38904  */
38905 /*
38906  * These classes are private internal classes
38907  */
38908 Roo.bootstrap.layout.Center = function(config){
38909     config.region = "center";
38910     Roo.bootstrap.layout.Region.call(this, config);
38911     this.visible = true;
38912     this.minWidth = config.minWidth || 20;
38913     this.minHeight = config.minHeight || 20;
38914 };
38915
38916 Roo.extend(Roo.bootstrap.layout.Center, Roo.bootstrap.layout.Region, {
38917     hide : function(){
38918         // center panel can't be hidden
38919     },
38920     
38921     show : function(){
38922         // center panel can't be hidden
38923     },
38924     
38925     getMinWidth: function(){
38926         return this.minWidth;
38927     },
38928     
38929     getMinHeight: function(){
38930         return this.minHeight;
38931     }
38932 });
38933
38934
38935
38936
38937  
38938
38939
38940
38941
38942
38943
38944 Roo.bootstrap.layout.North = function(config)
38945 {
38946     config.region = 'north';
38947     config.cursor = 'n-resize';
38948     
38949     Roo.bootstrap.layout.Split.call(this, config);
38950     
38951     
38952     if(this.split){
38953         this.split.placement = Roo.bootstrap.SplitBar.TOP;
38954         this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
38955         this.split.el.addClass("roo-layout-split-v");
38956     }
38957     var size = config.initialSize || config.height;
38958     if(typeof size != "undefined"){
38959         this.el.setHeight(size);
38960     }
38961 };
38962 Roo.extend(Roo.bootstrap.layout.North, Roo.bootstrap.layout.Split,
38963 {
38964     orientation: Roo.bootstrap.SplitBar.VERTICAL,
38965     
38966     
38967     
38968     getBox : function(){
38969         if(this.collapsed){
38970             return this.collapsedEl.getBox();
38971         }
38972         var box = this.el.getBox();
38973         if(this.split){
38974             box.height += this.split.el.getHeight();
38975         }
38976         return box;
38977     },
38978     
38979     updateBox : function(box){
38980         if(this.split && !this.collapsed){
38981             box.height -= this.split.el.getHeight();
38982             this.split.el.setLeft(box.x);
38983             this.split.el.setTop(box.y+box.height);
38984             this.split.el.setWidth(box.width);
38985         }
38986         if(this.collapsed){
38987             this.updateBody(box.width, null);
38988         }
38989         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
38990     }
38991 });
38992
38993
38994
38995
38996
38997 Roo.bootstrap.layout.South = function(config){
38998     config.region = 'south';
38999     config.cursor = 's-resize';
39000     Roo.bootstrap.layout.Split.call(this, config);
39001     if(this.split){
39002         this.split.placement = Roo.bootstrap.SplitBar.BOTTOM;
39003         this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
39004         this.split.el.addClass("roo-layout-split-v");
39005     }
39006     var size = config.initialSize || config.height;
39007     if(typeof size != "undefined"){
39008         this.el.setHeight(size);
39009     }
39010 };
39011
39012 Roo.extend(Roo.bootstrap.layout.South, Roo.bootstrap.layout.Split, {
39013     orientation: Roo.bootstrap.SplitBar.VERTICAL,
39014     getBox : function(){
39015         if(this.collapsed){
39016             return this.collapsedEl.getBox();
39017         }
39018         var box = this.el.getBox();
39019         if(this.split){
39020             var sh = this.split.el.getHeight();
39021             box.height += sh;
39022             box.y -= sh;
39023         }
39024         return box;
39025     },
39026     
39027     updateBox : function(box){
39028         if(this.split && !this.collapsed){
39029             var sh = this.split.el.getHeight();
39030             box.height -= sh;
39031             box.y += sh;
39032             this.split.el.setLeft(box.x);
39033             this.split.el.setTop(box.y-sh);
39034             this.split.el.setWidth(box.width);
39035         }
39036         if(this.collapsed){
39037             this.updateBody(box.width, null);
39038         }
39039         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
39040     }
39041 });
39042
39043 Roo.bootstrap.layout.East = function(config){
39044     config.region = "east";
39045     config.cursor = "e-resize";
39046     Roo.bootstrap.layout.Split.call(this, config);
39047     if(this.split){
39048         this.split.placement = Roo.bootstrap.SplitBar.RIGHT;
39049         this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
39050         this.split.el.addClass("roo-layout-split-h");
39051     }
39052     var size = config.initialSize || config.width;
39053     if(typeof size != "undefined"){
39054         this.el.setWidth(size);
39055     }
39056 };
39057 Roo.extend(Roo.bootstrap.layout.East, Roo.bootstrap.layout.Split, {
39058     orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
39059     getBox : function(){
39060         if(this.collapsed){
39061             return this.collapsedEl.getBox();
39062         }
39063         var box = this.el.getBox();
39064         if(this.split){
39065             var sw = this.split.el.getWidth();
39066             box.width += sw;
39067             box.x -= sw;
39068         }
39069         return box;
39070     },
39071
39072     updateBox : function(box){
39073         if(this.split && !this.collapsed){
39074             var sw = this.split.el.getWidth();
39075             box.width -= sw;
39076             this.split.el.setLeft(box.x);
39077             this.split.el.setTop(box.y);
39078             this.split.el.setHeight(box.height);
39079             box.x += sw;
39080         }
39081         if(this.collapsed){
39082             this.updateBody(null, box.height);
39083         }
39084         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
39085     }
39086 });
39087
39088 Roo.bootstrap.layout.West = function(config){
39089     config.region = "west";
39090     config.cursor = "w-resize";
39091     
39092     Roo.bootstrap.layout.Split.call(this, config);
39093     if(this.split){
39094         this.split.placement = Roo.bootstrap.SplitBar.LEFT;
39095         this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
39096         this.split.el.addClass("roo-layout-split-h");
39097     }
39098     
39099 };
39100 Roo.extend(Roo.bootstrap.layout.West, Roo.bootstrap.layout.Split, {
39101     orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
39102     
39103     onRender: function(ctr, pos)
39104     {
39105         Roo.bootstrap.layout.West.superclass.onRender.call(this, ctr,pos);
39106         var size = this.config.initialSize || this.config.width;
39107         if(typeof size != "undefined"){
39108             this.el.setWidth(size);
39109         }
39110     },
39111     
39112     getBox : function(){
39113         if(this.collapsed){
39114             return this.collapsedEl.getBox();
39115         }
39116         var box = this.el.getBox();
39117         if(this.split){
39118             box.width += this.split.el.getWidth();
39119         }
39120         return box;
39121     },
39122     
39123     updateBox : function(box){
39124         if(this.split && !this.collapsed){
39125             var sw = this.split.el.getWidth();
39126             box.width -= sw;
39127             this.split.el.setLeft(box.x+box.width);
39128             this.split.el.setTop(box.y);
39129             this.split.el.setHeight(box.height);
39130         }
39131         if(this.collapsed){
39132             this.updateBody(null, box.height);
39133         }
39134         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
39135     }
39136 });Roo.namespace("Roo.bootstrap.panel");/*
39137  * Based on:
39138  * Ext JS Library 1.1.1
39139  * Copyright(c) 2006-2007, Ext JS, LLC.
39140  *
39141  * Originally Released Under LGPL - original licence link has changed is not relivant.
39142  *
39143  * Fork - LGPL
39144  * <script type="text/javascript">
39145  */
39146 /**
39147  * @class Roo.ContentPanel
39148  * @extends Roo.util.Observable
39149  * A basic ContentPanel element.
39150  * @cfg {Boolean}   fitToFrame    True for this panel to adjust its size to fit when the region resizes  (defaults to false)
39151  * @cfg {Boolean}   fitContainer   When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container  (defaults to false)
39152  * @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
39153  * @cfg {Boolean}   closable      True if the panel can be closed/removed
39154  * @cfg {Boolean}   background    True if the panel should not be activated when it is added (defaults to false)
39155  * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
39156  * @cfg {Toolbar}   toolbar       A toolbar for this panel
39157  * @cfg {Boolean} autoScroll    True to scroll overflow in this panel (use with {@link #fitToFrame})
39158  * @cfg {String} title          The title for this panel
39159  * @cfg {Array} adjustments     Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
39160  * @cfg {String} url            Calls {@link #setUrl} with this value
39161  * @cfg {String} region         (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
39162  * @cfg {String/Object} params  When used with {@link #url}, calls {@link #setUrl} with this value
39163  * @cfg {Boolean} loadOnce      When used with {@link #url}, calls {@link #setUrl} with this value
39164  * @cfg {String}    content        Raw content to fill content panel with (uses setContent on construction.)
39165  * @cfg {Boolean} badges render the badges
39166  * @cfg {String} cls  extra classes to use  
39167  * @cfg {String} background (primary|secondary|success|info|warning|danger|light|dark)
39168
39169  * @constructor
39170  * Create a new ContentPanel.
39171  * @param {String/HTMLElement/Roo.Element} el The container element for this panel
39172  * @param {String/Object} config A string to set only the title or a config object
39173  * @param {String} content (optional) Set the HTML content for this panel
39174  * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
39175  */
39176 Roo.bootstrap.panel.Content = function( config){
39177     
39178     this.tpl = config.tpl || false;
39179     
39180     var el = config.el;
39181     var content = config.content;
39182
39183     if(config.autoCreate){ // xtype is available if this is called from factory
39184         el = Roo.id();
39185     }
39186     this.el = Roo.get(el);
39187     if(!this.el && config && config.autoCreate){
39188         if(typeof config.autoCreate == "object"){
39189             if(!config.autoCreate.id){
39190                 config.autoCreate.id = config.id||el;
39191             }
39192             this.el = Roo.DomHelper.append(document.body,
39193                         config.autoCreate, true);
39194         }else{
39195             var elcfg =  {
39196                 tag: "div",
39197                 cls: (config.cls || '') +
39198                     (config.background ? ' bg-' + config.background : '') +
39199                     " roo-layout-inactive-content",
39200                 id: config.id||el
39201             };
39202             if (config.html) {
39203                 elcfg.html = config.html;
39204                 
39205             }
39206                         
39207             this.el = Roo.DomHelper.append(document.body, elcfg , true);
39208         }
39209     } 
39210     this.closable = false;
39211     this.loaded = false;
39212     this.active = false;
39213    
39214       
39215     if (config.toolbar && !config.toolbar.el && config.toolbar.xtype) {
39216         
39217         this.toolbar = new config.toolbar.xns[config.toolbar.xtype](config.toolbar);
39218         
39219         this.wrapEl = this.el; //this.el.wrap();
39220         var ti = [];
39221         if (config.toolbar.items) {
39222             ti = config.toolbar.items ;
39223             delete config.toolbar.items ;
39224         }
39225         
39226         var nitems = [];
39227         this.toolbar.render(this.wrapEl, 'before');
39228         for(var i =0;i < ti.length;i++) {
39229           //  Roo.log(['add child', items[i]]);
39230             nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
39231         }
39232         this.toolbar.items = nitems;
39233         this.toolbar.el.insertBefore(this.wrapEl.dom.firstChild);
39234         delete config.toolbar;
39235         
39236     }
39237     /*
39238     // xtype created footer. - not sure if will work as we normally have to render first..
39239     if (this.footer && !this.footer.el && this.footer.xtype) {
39240         if (!this.wrapEl) {
39241             this.wrapEl = this.el.wrap();
39242         }
39243     
39244         this.footer.container = this.wrapEl.createChild();
39245          
39246         this.footer = Roo.factory(this.footer, Roo);
39247         
39248     }
39249     */
39250     
39251      if(typeof config == "string"){
39252         this.title = config;
39253     }else{
39254         Roo.apply(this, config);
39255     }
39256     
39257     if(this.resizeEl){
39258         this.resizeEl = Roo.get(this.resizeEl, true);
39259     }else{
39260         this.resizeEl = this.el;
39261     }
39262     // handle view.xtype
39263     
39264  
39265     
39266     
39267     this.addEvents({
39268         /**
39269          * @event activate
39270          * Fires when this panel is activated. 
39271          * @param {Roo.ContentPanel} this
39272          */
39273         "activate" : true,
39274         /**
39275          * @event deactivate
39276          * Fires when this panel is activated. 
39277          * @param {Roo.ContentPanel} this
39278          */
39279         "deactivate" : true,
39280
39281         /**
39282          * @event resize
39283          * Fires when this panel is resized if fitToFrame is true.
39284          * @param {Roo.ContentPanel} this
39285          * @param {Number} width The width after any component adjustments
39286          * @param {Number} height The height after any component adjustments
39287          */
39288         "resize" : true,
39289         
39290          /**
39291          * @event render
39292          * Fires when this tab is created
39293          * @param {Roo.ContentPanel} this
39294          */
39295         "render" : true
39296         
39297         
39298         
39299     });
39300     
39301
39302     
39303     
39304     if(this.autoScroll){
39305         this.resizeEl.setStyle("overflow", "auto");
39306     } else {
39307         // fix randome scrolling
39308         //this.el.on('scroll', function() {
39309         //    Roo.log('fix random scolling');
39310         //    this.scrollTo('top',0); 
39311         //});
39312     }
39313     content = content || this.content;
39314     if(content){
39315         this.setContent(content);
39316     }
39317     if(config && config.url){
39318         this.setUrl(this.url, this.params, this.loadOnce);
39319     }
39320     
39321     
39322     
39323     Roo.bootstrap.panel.Content.superclass.constructor.call(this);
39324     
39325     if (this.view && typeof(this.view.xtype) != 'undefined') {
39326         this.view.el = this.el.appendChild(document.createElement("div"));
39327         this.view = Roo.factory(this.view); 
39328         this.view.render  &&  this.view.render(false, '');  
39329     }
39330     
39331     
39332     this.fireEvent('render', this);
39333 };
39334
39335 Roo.extend(Roo.bootstrap.panel.Content, Roo.bootstrap.Component, {
39336     
39337     cls : '',
39338     background : '',
39339     
39340     tabTip : '',
39341     
39342     setRegion : function(region){
39343         this.region = region;
39344         this.setActiveClass(region && !this.background);
39345     },
39346     
39347     
39348     setActiveClass: function(state)
39349     {
39350         if(state){
39351            this.el.replaceClass("roo-layout-inactive-content", "roo-layout-active-content");
39352            this.el.setStyle('position','relative');
39353         }else{
39354            this.el.replaceClass("roo-layout-active-content", "roo-layout-inactive-content");
39355            this.el.setStyle('position', 'absolute');
39356         } 
39357     },
39358     
39359     /**
39360      * Returns the toolbar for this Panel if one was configured. 
39361      * @return {Roo.Toolbar} 
39362      */
39363     getToolbar : function(){
39364         return this.toolbar;
39365     },
39366     
39367     setActiveState : function(active)
39368     {
39369         this.active = active;
39370         this.setActiveClass(active);
39371         if(!active){
39372             if(this.fireEvent("deactivate", this) === false){
39373                 return false;
39374             }
39375             return true;
39376         }
39377         this.fireEvent("activate", this);
39378         return true;
39379     },
39380     /**
39381      * Updates this panel's element
39382      * @param {String} content The new content
39383      * @param {Boolean} loadScripts (optional) true to look for and process scripts
39384     */
39385     setContent : function(content, loadScripts){
39386         this.el.update(content, loadScripts);
39387     },
39388
39389     ignoreResize : function(w, h){
39390         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
39391             return true;
39392         }else{
39393             this.lastSize = {width: w, height: h};
39394             return false;
39395         }
39396     },
39397     /**
39398      * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
39399      * @return {Roo.UpdateManager} The UpdateManager
39400      */
39401     getUpdateManager : function(){
39402         return this.el.getUpdateManager();
39403     },
39404      /**
39405      * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
39406      * @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:
39407 <pre><code>
39408 panel.load({
39409     url: "your-url.php",
39410     params: {param1: "foo", param2: "bar"}, // or a URL encoded string
39411     callback: yourFunction,
39412     scope: yourObject, //(optional scope)
39413     discardUrl: false,
39414     nocache: false,
39415     text: "Loading...",
39416     timeout: 30,
39417     scripts: false
39418 });
39419 </code></pre>
39420      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
39421      * 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.
39422      * @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}
39423      * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
39424      * @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.
39425      * @return {Roo.ContentPanel} this
39426      */
39427     load : function(){
39428         var um = this.el.getUpdateManager();
39429         um.update.apply(um, arguments);
39430         return this;
39431     },
39432
39433
39434     /**
39435      * 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.
39436      * @param {String/Function} url The URL to load the content from or a function to call to get the URL
39437      * @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)
39438      * @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)
39439      * @return {Roo.UpdateManager} The UpdateManager
39440      */
39441     setUrl : function(url, params, loadOnce){
39442         if(this.refreshDelegate){
39443             this.removeListener("activate", this.refreshDelegate);
39444         }
39445         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
39446         this.on("activate", this.refreshDelegate);
39447         return this.el.getUpdateManager();
39448     },
39449     
39450     _handleRefresh : function(url, params, loadOnce){
39451         if(!loadOnce || !this.loaded){
39452             var updater = this.el.getUpdateManager();
39453             updater.update(url, params, this._setLoaded.createDelegate(this));
39454         }
39455     },
39456     
39457     _setLoaded : function(){
39458         this.loaded = true;
39459     }, 
39460     
39461     /**
39462      * Returns this panel's id
39463      * @return {String} 
39464      */
39465     getId : function(){
39466         return this.el.id;
39467     },
39468     
39469     /** 
39470      * Returns this panel's element - used by regiosn to add.
39471      * @return {Roo.Element} 
39472      */
39473     getEl : function(){
39474         return this.wrapEl || this.el;
39475     },
39476     
39477    
39478     
39479     adjustForComponents : function(width, height)
39480     {
39481         //Roo.log('adjustForComponents ');
39482         if(this.resizeEl != this.el){
39483             width -= this.el.getFrameWidth('lr');
39484             height -= this.el.getFrameWidth('tb');
39485         }
39486         if(this.toolbar){
39487             var te = this.toolbar.getEl();
39488             te.setWidth(width);
39489             height -= te.getHeight();
39490         }
39491         if(this.footer){
39492             var te = this.footer.getEl();
39493             te.setWidth(width);
39494             height -= te.getHeight();
39495         }
39496         
39497         
39498         if(this.adjustments){
39499             width += this.adjustments[0];
39500             height += this.adjustments[1];
39501         }
39502         return {"width": width, "height": height};
39503     },
39504     
39505     setSize : function(width, height){
39506         if(this.fitToFrame && !this.ignoreResize(width, height)){
39507             if(this.fitContainer && this.resizeEl != this.el){
39508                 this.el.setSize(width, height);
39509             }
39510             var size = this.adjustForComponents(width, height);
39511             this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
39512             this.fireEvent('resize', this, size.width, size.height);
39513         }
39514     },
39515     
39516     /**
39517      * Returns this panel's title
39518      * @return {String} 
39519      */
39520     getTitle : function(){
39521         
39522         if (typeof(this.title) != 'object') {
39523             return this.title;
39524         }
39525         
39526         var t = '';
39527         for (var k in this.title) {
39528             if (!this.title.hasOwnProperty(k)) {
39529                 continue;
39530             }
39531             
39532             if (k.indexOf('-') >= 0) {
39533                 var s = k.split('-');
39534                 for (var i = 0; i<s.length; i++) {
39535                     t += "<span class='visible-"+s[i]+"'>"+this.title[k]+"</span>";
39536                 }
39537             } else {
39538                 t += "<span class='visible-"+k+"'>"+this.title[k]+"</span>";
39539             }
39540         }
39541         return t;
39542     },
39543     
39544     /**
39545      * Set this panel's title
39546      * @param {String} title
39547      */
39548     setTitle : function(title){
39549         this.title = title;
39550         if(this.region){
39551             this.region.updatePanelTitle(this, title);
39552         }
39553     },
39554     
39555     /**
39556      * Returns true is this panel was configured to be closable
39557      * @return {Boolean} 
39558      */
39559     isClosable : function(){
39560         return this.closable;
39561     },
39562     
39563     beforeSlide : function(){
39564         this.el.clip();
39565         this.resizeEl.clip();
39566     },
39567     
39568     afterSlide : function(){
39569         this.el.unclip();
39570         this.resizeEl.unclip();
39571     },
39572     
39573     /**
39574      *   Force a content refresh from the URL specified in the {@link #setUrl} method.
39575      *   Will fail silently if the {@link #setUrl} method has not been called.
39576      *   This does not activate the panel, just updates its content.
39577      */
39578     refresh : function(){
39579         if(this.refreshDelegate){
39580            this.loaded = false;
39581            this.refreshDelegate();
39582         }
39583     },
39584     
39585     /**
39586      * Destroys this panel
39587      */
39588     destroy : function(){
39589         this.el.removeAllListeners();
39590         var tempEl = document.createElement("span");
39591         tempEl.appendChild(this.el.dom);
39592         tempEl.innerHTML = "";
39593         this.el.remove();
39594         this.el = null;
39595     },
39596     
39597     /**
39598      * form - if the content panel contains a form - this is a reference to it.
39599      * @type {Roo.form.Form}
39600      */
39601     form : false,
39602     /**
39603      * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
39604      *    This contains a reference to it.
39605      * @type {Roo.View}
39606      */
39607     view : false,
39608     
39609       /**
39610      * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
39611      * <pre><code>
39612
39613 layout.addxtype({
39614        xtype : 'Form',
39615        items: [ .... ]
39616    }
39617 );
39618
39619 </code></pre>
39620      * @param {Object} cfg Xtype definition of item to add.
39621      */
39622     
39623     
39624     getChildContainer: function () {
39625         return this.getEl();
39626     }
39627     
39628     
39629     /*
39630         var  ret = new Roo.factory(cfg);
39631         return ret;
39632         
39633         
39634         // add form..
39635         if (cfg.xtype.match(/^Form$/)) {
39636             
39637             var el;
39638             //if (this.footer) {
39639             //    el = this.footer.container.insertSibling(false, 'before');
39640             //} else {
39641                 el = this.el.createChild();
39642             //}
39643
39644             this.form = new  Roo.form.Form(cfg);
39645             
39646             
39647             if ( this.form.allItems.length) {
39648                 this.form.render(el.dom);
39649             }
39650             return this.form;
39651         }
39652         // should only have one of theses..
39653         if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
39654             // views.. should not be just added - used named prop 'view''
39655             
39656             cfg.el = this.el.appendChild(document.createElement("div"));
39657             // factory?
39658             
39659             var ret = new Roo.factory(cfg);
39660              
39661              ret.render && ret.render(false, ''); // render blank..
39662             this.view = ret;
39663             return ret;
39664         }
39665         return false;
39666     }
39667     \*/
39668 });
39669  
39670 /**
39671  * @class Roo.bootstrap.panel.Grid
39672  * @extends Roo.bootstrap.panel.Content
39673  * @constructor
39674  * Create a new GridPanel.
39675  * @cfg {Roo.bootstrap.Table} grid The grid for this panel
39676  * @param {Object} config A the config object
39677   
39678  */
39679
39680
39681
39682 Roo.bootstrap.panel.Grid = function(config)
39683 {
39684     
39685       
39686     this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
39687         {tag: "div", cls: "roo-layout-grid-wrapper roo-layout-inactive-content"}, true);
39688
39689     config.el = this.wrapper;
39690     //this.el = this.wrapper;
39691     
39692       if (config.container) {
39693         // ctor'ed from a Border/panel.grid
39694         
39695         
39696         this.wrapper.setStyle("overflow", "hidden");
39697         this.wrapper.addClass('roo-grid-container');
39698
39699     }
39700     
39701     
39702     if(config.toolbar){
39703         var tool_el = this.wrapper.createChild();    
39704         this.toolbar = Roo.factory(config.toolbar);
39705         var ti = [];
39706         if (config.toolbar.items) {
39707             ti = config.toolbar.items ;
39708             delete config.toolbar.items ;
39709         }
39710         
39711         var nitems = [];
39712         this.toolbar.render(tool_el);
39713         for(var i =0;i < ti.length;i++) {
39714           //  Roo.log(['add child', items[i]]);
39715             nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
39716         }
39717         this.toolbar.items = nitems;
39718         
39719         delete config.toolbar;
39720     }
39721     
39722     Roo.bootstrap.panel.Grid.superclass.constructor.call(this, config);
39723     config.grid.scrollBody = true;;
39724     config.grid.monitorWindowResize = false; // turn off autosizing
39725     config.grid.autoHeight = false;
39726     config.grid.autoWidth = false;
39727     
39728     this.grid = new config.grid.xns[config.grid.xtype](config.grid);
39729     
39730     if (config.background) {
39731         // render grid on panel activation (if panel background)
39732         this.on('activate', function(gp) {
39733             if (!gp.grid.rendered) {
39734                 gp.grid.render(this.wrapper);
39735                 gp.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");   
39736             }
39737         });
39738             
39739     } else {
39740         this.grid.render(this.wrapper);
39741         this.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");               
39742
39743     }
39744     //this.wrapper.dom.appendChild(config.grid.getGridEl().dom);
39745     // ??? needed ??? config.el = this.wrapper;
39746     
39747     
39748     
39749   
39750     // xtype created footer. - not sure if will work as we normally have to render first..
39751     if (this.footer && !this.footer.el && this.footer.xtype) {
39752         
39753         var ctr = this.grid.getView().getFooterPanel(true);
39754         this.footer.dataSource = this.grid.dataSource;
39755         this.footer = Roo.factory(this.footer, Roo);
39756         this.footer.render(ctr);
39757         
39758     }
39759     
39760     
39761     
39762     
39763      
39764 };
39765
39766 Roo.extend(Roo.bootstrap.panel.Grid, Roo.bootstrap.panel.Content, {
39767     getId : function(){
39768         return this.grid.id;
39769     },
39770     
39771     /**
39772      * Returns the grid for this panel
39773      * @return {Roo.bootstrap.Table} 
39774      */
39775     getGrid : function(){
39776         return this.grid;    
39777     },
39778     
39779     setSize : function(width, height){
39780         if(!this.ignoreResize(width, height)){
39781             var grid = this.grid;
39782             var size = this.adjustForComponents(width, height);
39783             // tfoot is not a footer?
39784           
39785             
39786             var gridel = grid.getGridEl();
39787             gridel.setSize(size.width, size.height);
39788             
39789             var tbd = grid.getGridEl().select('tbody', true).first();
39790             var thd = grid.getGridEl().select('thead',true).first();
39791             var tbf= grid.getGridEl().select('tfoot', true).first();
39792
39793             if (tbf) {
39794                 size.height -= thd.getHeight();
39795             }
39796             if (thd) {
39797                 size.height -= thd.getHeight();
39798             }
39799             
39800             tbd.setSize(size.width, size.height );
39801             // this is for the account management tab -seems to work there.
39802             var thd = grid.getGridEl().select('thead',true).first();
39803             //if (tbd) {
39804             //    tbd.setSize(size.width, size.height - thd.getHeight());
39805             //}
39806              
39807             grid.autoSize();
39808         }
39809     },
39810      
39811     
39812     
39813     beforeSlide : function(){
39814         this.grid.getView().scroller.clip();
39815     },
39816     
39817     afterSlide : function(){
39818         this.grid.getView().scroller.unclip();
39819     },
39820     
39821     destroy : function(){
39822         this.grid.destroy();
39823         delete this.grid;
39824         Roo.bootstrap.panel.Grid.superclass.destroy.call(this); 
39825     }
39826 });
39827
39828 /**
39829  * @class Roo.bootstrap.panel.Nest
39830  * @extends Roo.bootstrap.panel.Content
39831  * @constructor
39832  * Create a new Panel, that can contain a layout.Border.
39833  * 
39834  * 
39835  * @param {Roo.BorderLayout} layout The layout for this panel
39836  * @param {String/Object} config A string to set only the title or a config object
39837  */
39838 Roo.bootstrap.panel.Nest = function(config)
39839 {
39840     // construct with only one argument..
39841     /* FIXME - implement nicer consturctors
39842     if (layout.layout) {
39843         config = layout;
39844         layout = config.layout;
39845         delete config.layout;
39846     }
39847     if (layout.xtype && !layout.getEl) {
39848         // then layout needs constructing..
39849         layout = Roo.factory(layout, Roo);
39850     }
39851     */
39852     
39853     config.el =  config.layout.getEl();
39854     
39855     Roo.bootstrap.panel.Nest.superclass.constructor.call(this, config);
39856     
39857     config.layout.monitorWindowResize = false; // turn off autosizing
39858     this.layout = config.layout;
39859     this.layout.getEl().addClass("roo-layout-nested-layout");
39860     this.layout.parent = this;
39861     
39862     
39863     
39864     
39865 };
39866
39867 Roo.extend(Roo.bootstrap.panel.Nest, Roo.bootstrap.panel.Content, {
39868
39869     setSize : function(width, height){
39870         if(!this.ignoreResize(width, height)){
39871             var size = this.adjustForComponents(width, height);
39872             var el = this.layout.getEl();
39873             if (size.height < 1) {
39874                 el.setWidth(size.width);   
39875             } else {
39876                 el.setSize(size.width, size.height);
39877             }
39878             var touch = el.dom.offsetWidth;
39879             this.layout.layout();
39880             // ie requires a double layout on the first pass
39881             if(Roo.isIE && !this.initialized){
39882                 this.initialized = true;
39883                 this.layout.layout();
39884             }
39885         }
39886     },
39887     
39888     // activate all subpanels if not currently active..
39889     
39890     setActiveState : function(active){
39891         this.active = active;
39892         this.setActiveClass(active);
39893         
39894         if(!active){
39895             this.fireEvent("deactivate", this);
39896             return;
39897         }
39898         
39899         this.fireEvent("activate", this);
39900         // not sure if this should happen before or after..
39901         if (!this.layout) {
39902             return; // should not happen..
39903         }
39904         var reg = false;
39905         for (var r in this.layout.regions) {
39906             reg = this.layout.getRegion(r);
39907             if (reg.getActivePanel()) {
39908                 //reg.showPanel(reg.getActivePanel()); // force it to activate.. 
39909                 reg.setActivePanel(reg.getActivePanel());
39910                 continue;
39911             }
39912             if (!reg.panels.length) {
39913                 continue;
39914             }
39915             reg.showPanel(reg.getPanel(0));
39916         }
39917         
39918         
39919         
39920         
39921     },
39922     
39923     /**
39924      * Returns the nested BorderLayout for this panel
39925      * @return {Roo.BorderLayout} 
39926      */
39927     getLayout : function(){
39928         return this.layout;
39929     },
39930     
39931      /**
39932      * Adds a xtype elements to the layout of the nested panel
39933      * <pre><code>
39934
39935 panel.addxtype({
39936        xtype : 'ContentPanel',
39937        region: 'west',
39938        items: [ .... ]
39939    }
39940 );
39941
39942 panel.addxtype({
39943         xtype : 'NestedLayoutPanel',
39944         region: 'west',
39945         layout: {
39946            center: { },
39947            west: { }   
39948         },
39949         items : [ ... list of content panels or nested layout panels.. ]
39950    }
39951 );
39952 </code></pre>
39953      * @param {Object} cfg Xtype definition of item to add.
39954      */
39955     addxtype : function(cfg) {
39956         return this.layout.addxtype(cfg);
39957     
39958     }
39959 });/*
39960  * Based on:
39961  * Ext JS Library 1.1.1
39962  * Copyright(c) 2006-2007, Ext JS, LLC.
39963  *
39964  * Originally Released Under LGPL - original licence link has changed is not relivant.
39965  *
39966  * Fork - LGPL
39967  * <script type="text/javascript">
39968  */
39969 /**
39970  * @class Roo.TabPanel
39971  * @extends Roo.util.Observable
39972  * A lightweight tab container.
39973  * <br><br>
39974  * Usage:
39975  * <pre><code>
39976 // basic tabs 1, built from existing content
39977 var tabs = new Roo.TabPanel("tabs1");
39978 tabs.addTab("script", "View Script");
39979 tabs.addTab("markup", "View Markup");
39980 tabs.activate("script");
39981
39982 // more advanced tabs, built from javascript
39983 var jtabs = new Roo.TabPanel("jtabs");
39984 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
39985
39986 // set up the UpdateManager
39987 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
39988 var updater = tab2.getUpdateManager();
39989 updater.setDefaultUrl("ajax1.htm");
39990 tab2.on('activate', updater.refresh, updater, true);
39991
39992 // Use setUrl for Ajax loading
39993 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
39994 tab3.setUrl("ajax2.htm", null, true);
39995
39996 // Disabled tab
39997 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
39998 tab4.disable();
39999
40000 jtabs.activate("jtabs-1");
40001  * </code></pre>
40002  * @constructor
40003  * Create a new TabPanel.
40004  * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
40005  * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
40006  */
40007 Roo.bootstrap.panel.Tabs = function(config){
40008     /**
40009     * The container element for this TabPanel.
40010     * @type Roo.Element
40011     */
40012     this.el = Roo.get(config.el);
40013     delete config.el;
40014     if(config){
40015         if(typeof config == "boolean"){
40016             this.tabPosition = config ? "bottom" : "top";
40017         }else{
40018             Roo.apply(this, config);
40019         }
40020     }
40021     
40022     if(this.tabPosition == "bottom"){
40023         // if tabs are at the bottom = create the body first.
40024         this.bodyEl = Roo.get(this.createBody(this.el.dom));
40025         this.el.addClass("roo-tabs-bottom");
40026     }
40027     // next create the tabs holders
40028     
40029     if (this.tabPosition == "west"){
40030         
40031         var reg = this.region; // fake it..
40032         while (reg) {
40033             if (!reg.mgr.parent) {
40034                 break;
40035             }
40036             reg = reg.mgr.parent.region;
40037         }
40038         Roo.log("got nest?");
40039         Roo.log(reg);
40040         if (reg.mgr.getRegion('west')) {
40041             var ctrdom = reg.mgr.getRegion('west').bodyEl.dom;
40042             this.stripWrap = Roo.get(this.createStrip(ctrdom ), true);
40043             this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
40044             this.stripEl.setVisibilityMode(Roo.Element.DISPLAY);
40045             this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
40046         
40047             
40048         }
40049         
40050         
40051     } else {
40052      
40053         this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
40054         this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
40055         this.stripEl.setVisibilityMode(Roo.Element.DISPLAY);
40056         this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
40057     }
40058     
40059     
40060     if(Roo.isIE){
40061         Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
40062     }
40063     
40064     // finally - if tabs are at the top, then create the body last..
40065     if(this.tabPosition != "bottom"){
40066         /** The body element that contains {@link Roo.TabPanelItem} bodies. +
40067          * @type Roo.Element
40068          */
40069         this.bodyEl = Roo.get(this.createBody(this.el.dom));
40070         this.el.addClass("roo-tabs-top");
40071     }
40072     this.items = [];
40073
40074     this.bodyEl.setStyle("position", "relative");
40075
40076     this.active = null;
40077     this.activateDelegate = this.activate.createDelegate(this);
40078
40079     this.addEvents({
40080         /**
40081          * @event tabchange
40082          * Fires when the active tab changes
40083          * @param {Roo.TabPanel} this
40084          * @param {Roo.TabPanelItem} activePanel The new active tab
40085          */
40086         "tabchange": true,
40087         /**
40088          * @event beforetabchange
40089          * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
40090          * @param {Roo.TabPanel} this
40091          * @param {Object} e Set cancel to true on this object to cancel the tab change
40092          * @param {Roo.TabPanelItem} tab The tab being changed to
40093          */
40094         "beforetabchange" : true
40095     });
40096
40097     Roo.EventManager.onWindowResize(this.onResize, this);
40098     this.cpad = this.el.getPadding("lr");
40099     this.hiddenCount = 0;
40100
40101
40102     // toolbar on the tabbar support...
40103     if (this.toolbar) {
40104         alert("no toolbar support yet");
40105         this.toolbar  = false;
40106         /*
40107         var tcfg = this.toolbar;
40108         tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');  
40109         this.toolbar = new Roo.Toolbar(tcfg);
40110         if (Roo.isSafari) {
40111             var tbl = tcfg.container.child('table', true);
40112             tbl.setAttribute('width', '100%');
40113         }
40114         */
40115         
40116     }
40117    
40118
40119
40120     Roo.bootstrap.panel.Tabs.superclass.constructor.call(this);
40121 };
40122
40123 Roo.extend(Roo.bootstrap.panel.Tabs, Roo.util.Observable, {
40124     /*
40125      *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
40126      */
40127     tabPosition : "top",
40128     /*
40129      *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
40130      */
40131     currentTabWidth : 0,
40132     /*
40133      *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
40134      */
40135     minTabWidth : 40,
40136     /*
40137      *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
40138      */
40139     maxTabWidth : 250,
40140     /*
40141      *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
40142      */
40143     preferredTabWidth : 175,
40144     /*
40145      *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
40146      */
40147     resizeTabs : false,
40148     /*
40149      *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
40150      */
40151     monitorResize : true,
40152     /*
40153      *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar. 
40154      */
40155     toolbar : false,  // set by caller..
40156     
40157     region : false, /// set by caller
40158     
40159     disableTooltips : true, // not used yet...
40160
40161     /**
40162      * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
40163      * @param {String} id The id of the div to use <b>or create</b>
40164      * @param {String} text The text for the tab
40165      * @param {String} content (optional) Content to put in the TabPanelItem body
40166      * @param {Boolean} closable (optional) True to create a close icon on the tab
40167      * @return {Roo.TabPanelItem} The created TabPanelItem
40168      */
40169     addTab : function(id, text, content, closable, tpl)
40170     {
40171         var item = new Roo.bootstrap.panel.TabItem({
40172             panel: this,
40173             id : id,
40174             text : text,
40175             closable : closable,
40176             tpl : tpl
40177         });
40178         this.addTabItem(item);
40179         if(content){
40180             item.setContent(content);
40181         }
40182         return item;
40183     },
40184
40185     /**
40186      * Returns the {@link Roo.TabPanelItem} with the specified id/index
40187      * @param {String/Number} id The id or index of the TabPanelItem to fetch.
40188      * @return {Roo.TabPanelItem}
40189      */
40190     getTab : function(id){
40191         return this.items[id];
40192     },
40193
40194     /**
40195      * Hides the {@link Roo.TabPanelItem} with the specified id/index
40196      * @param {String/Number} id The id or index of the TabPanelItem to hide.
40197      */
40198     hideTab : function(id){
40199         var t = this.items[id];
40200         if(!t.isHidden()){
40201            t.setHidden(true);
40202            this.hiddenCount++;
40203            this.autoSizeTabs();
40204         }
40205     },
40206
40207     /**
40208      * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
40209      * @param {String/Number} id The id or index of the TabPanelItem to unhide.
40210      */
40211     unhideTab : function(id){
40212         var t = this.items[id];
40213         if(t.isHidden()){
40214            t.setHidden(false);
40215            this.hiddenCount--;
40216            this.autoSizeTabs();
40217         }
40218     },
40219
40220     /**
40221      * Adds an existing {@link Roo.TabPanelItem}.
40222      * @param {Roo.TabPanelItem} item The TabPanelItem to add
40223      */
40224     addTabItem : function(item)
40225     {
40226         this.items[item.id] = item;
40227         this.items.push(item);
40228         this.autoSizeTabs();
40229       //  if(this.resizeTabs){
40230     //       item.setWidth(this.currentTabWidth || this.preferredTabWidth);
40231   //         this.autoSizeTabs();
40232 //        }else{
40233 //            item.autoSize();
40234        // }
40235     },
40236
40237     /**
40238      * Removes a {@link Roo.TabPanelItem}.
40239      * @param {String/Number} id The id or index of the TabPanelItem to remove.
40240      */
40241     removeTab : function(id){
40242         var items = this.items;
40243         var tab = items[id];
40244         if(!tab) { return; }
40245         var index = items.indexOf(tab);
40246         if(this.active == tab && items.length > 1){
40247             var newTab = this.getNextAvailable(index);
40248             if(newTab) {
40249                 newTab.activate();
40250             }
40251         }
40252         this.stripEl.dom.removeChild(tab.pnode.dom);
40253         if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
40254             this.bodyEl.dom.removeChild(tab.bodyEl.dom);
40255         }
40256         items.splice(index, 1);
40257         delete this.items[tab.id];
40258         tab.fireEvent("close", tab);
40259         tab.purgeListeners();
40260         this.autoSizeTabs();
40261     },
40262
40263     getNextAvailable : function(start){
40264         var items = this.items;
40265         var index = start;
40266         // look for a next tab that will slide over to
40267         // replace the one being removed
40268         while(index < items.length){
40269             var item = items[++index];
40270             if(item && !item.isHidden()){
40271                 return item;
40272             }
40273         }
40274         // if one isn't found select the previous tab (on the left)
40275         index = start;
40276         while(index >= 0){
40277             var item = items[--index];
40278             if(item && !item.isHidden()){
40279                 return item;
40280             }
40281         }
40282         return null;
40283     },
40284
40285     /**
40286      * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
40287      * @param {String/Number} id The id or index of the TabPanelItem to disable.
40288      */
40289     disableTab : function(id){
40290         var tab = this.items[id];
40291         if(tab && this.active != tab){
40292             tab.disable();
40293         }
40294     },
40295
40296     /**
40297      * Enables a {@link Roo.TabPanelItem} that is disabled.
40298      * @param {String/Number} id The id or index of the TabPanelItem to enable.
40299      */
40300     enableTab : function(id){
40301         var tab = this.items[id];
40302         tab.enable();
40303     },
40304
40305     /**
40306      * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
40307      * @param {String/Number} id The id or index of the TabPanelItem to activate.
40308      * @return {Roo.TabPanelItem} The TabPanelItem.
40309      */
40310     activate : function(id)
40311     {
40312         //Roo.log('activite:'  + id);
40313         
40314         var tab = this.items[id];
40315         if(!tab){
40316             return null;
40317         }
40318         if(tab == this.active || tab.disabled){
40319             return tab;
40320         }
40321         var e = {};
40322         this.fireEvent("beforetabchange", this, e, tab);
40323         if(e.cancel !== true && !tab.disabled){
40324             if(this.active){
40325                 this.active.hide();
40326             }
40327             this.active = this.items[id];
40328             this.active.show();
40329             this.fireEvent("tabchange", this, this.active);
40330         }
40331         return tab;
40332     },
40333
40334     /**
40335      * Gets the active {@link Roo.TabPanelItem}.
40336      * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
40337      */
40338     getActiveTab : function(){
40339         return this.active;
40340     },
40341
40342     /**
40343      * Updates the tab body element to fit the height of the container element
40344      * for overflow scrolling
40345      * @param {Number} targetHeight (optional) Override the starting height from the elements height
40346      */
40347     syncHeight : function(targetHeight){
40348         var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
40349         var bm = this.bodyEl.getMargins();
40350         var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
40351         this.bodyEl.setHeight(newHeight);
40352         return newHeight;
40353     },
40354
40355     onResize : function(){
40356         if(this.monitorResize){
40357             this.autoSizeTabs();
40358         }
40359     },
40360
40361     /**
40362      * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
40363      */
40364     beginUpdate : function(){
40365         this.updating = true;
40366     },
40367
40368     /**
40369      * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
40370      */
40371     endUpdate : function(){
40372         this.updating = false;
40373         this.autoSizeTabs();
40374     },
40375
40376     /**
40377      * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
40378      */
40379     autoSizeTabs : function()
40380     {
40381         var count = this.items.length;
40382         var vcount = count - this.hiddenCount;
40383         
40384         if (vcount < 2) {
40385             this.stripEl.hide();
40386         } else {
40387             this.stripEl.show();
40388         }
40389         
40390         if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) {
40391             return;
40392         }
40393         
40394         
40395         var w = Math.max(this.el.getWidth() - this.cpad, 10);
40396         var availWidth = Math.floor(w / vcount);
40397         var b = this.stripBody;
40398         if(b.getWidth() > w){
40399             var tabs = this.items;
40400             this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
40401             if(availWidth < this.minTabWidth){
40402                 /*if(!this.sleft){    // incomplete scrolling code
40403                     this.createScrollButtons();
40404                 }
40405                 this.showScroll();
40406                 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
40407             }
40408         }else{
40409             if(this.currentTabWidth < this.preferredTabWidth){
40410                 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
40411             }
40412         }
40413     },
40414
40415     /**
40416      * Returns the number of tabs in this TabPanel.
40417      * @return {Number}
40418      */
40419      getCount : function(){
40420          return this.items.length;
40421      },
40422
40423     /**
40424      * Resizes all the tabs to the passed width
40425      * @param {Number} The new width
40426      */
40427     setTabWidth : function(width){
40428         this.currentTabWidth = width;
40429         for(var i = 0, len = this.items.length; i < len; i++) {
40430                 if(!this.items[i].isHidden()) {
40431                 this.items[i].setWidth(width);
40432             }
40433         }
40434     },
40435
40436     /**
40437      * Destroys this TabPanel
40438      * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
40439      */
40440     destroy : function(removeEl){
40441         Roo.EventManager.removeResizeListener(this.onResize, this);
40442         for(var i = 0, len = this.items.length; i < len; i++){
40443             this.items[i].purgeListeners();
40444         }
40445         if(removeEl === true){
40446             this.el.update("");
40447             this.el.remove();
40448         }
40449     },
40450     
40451     createStrip : function(container)
40452     {
40453         var strip = document.createElement("nav");
40454         strip.className = Roo.bootstrap.version == 4 ?
40455             "navbar-light bg-light" : 
40456             "navbar navbar-default"; //"x-tabs-wrap";
40457         container.appendChild(strip);
40458         return strip;
40459     },
40460     
40461     createStripList : function(strip)
40462     {
40463         // div wrapper for retard IE
40464         // returns the "tr" element.
40465         strip.innerHTML = '<ul class="nav nav-tabs" role="tablist"></ul>';
40466         //'<div class="x-tabs-strip-wrap">'+
40467           //  '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
40468           //  '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
40469         return strip.firstChild; //.firstChild.firstChild.firstChild;
40470     },
40471     createBody : function(container)
40472     {
40473         var body = document.createElement("div");
40474         Roo.id(body, "tab-body");
40475         //Roo.fly(body).addClass("x-tabs-body");
40476         Roo.fly(body).addClass("tab-content");
40477         container.appendChild(body);
40478         return body;
40479     },
40480     createItemBody :function(bodyEl, id){
40481         var body = Roo.getDom(id);
40482         if(!body){
40483             body = document.createElement("div");
40484             body.id = id;
40485         }
40486         //Roo.fly(body).addClass("x-tabs-item-body");
40487         Roo.fly(body).addClass("tab-pane");
40488          bodyEl.insertBefore(body, bodyEl.firstChild);
40489         return body;
40490     },
40491     /** @private */
40492     createStripElements :  function(stripEl, text, closable, tpl)
40493     {
40494         var td = document.createElement("li"); // was td..
40495         td.className = 'nav-item';
40496         
40497         //stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
40498         
40499         
40500         stripEl.appendChild(td);
40501         /*if(closable){
40502             td.className = "x-tabs-closable";
40503             if(!this.closeTpl){
40504                 this.closeTpl = new Roo.Template(
40505                    '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
40506                    '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
40507                    '<div unselectable="on" class="close-icon">&#160;</div></em></span></a>'
40508                 );
40509             }
40510             var el = this.closeTpl.overwrite(td, {"text": text});
40511             var close = el.getElementsByTagName("div")[0];
40512             var inner = el.getElementsByTagName("em")[0];
40513             return {"el": el, "close": close, "inner": inner};
40514         } else {
40515         */
40516         // not sure what this is..
40517 //            if(!this.tabTpl){
40518                 //this.tabTpl = new Roo.Template(
40519                 //   '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
40520                 //   '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
40521                 //);
40522 //                this.tabTpl = new Roo.Template(
40523 //                   '<a href="#">' +
40524 //                   '<span unselectable="on"' +
40525 //                            (this.disableTooltips ? '' : ' title="{text}"') +
40526 //                            ' >{text}</span></a>'
40527 //                );
40528 //                
40529 //            }
40530
40531
40532             var template = tpl || this.tabTpl || false;
40533             
40534             if(!template){
40535                 template =  new Roo.Template(
40536                         Roo.bootstrap.version == 4 ? 
40537                             (
40538                                 '<a class="nav-link" href="#" unselectable="on"' +
40539                                      (this.disableTooltips ? '' : ' title="{text}"') +
40540                                      ' >{text}</a>'
40541                             ) : (
40542                                 '<a class="nav-link" href="#">' +
40543                                 '<span unselectable="on"' +
40544                                          (this.disableTooltips ? '' : ' title="{text}"') +
40545                                     ' >{text}</span></a>'
40546                             )
40547                 );
40548             }
40549             
40550             switch (typeof(template)) {
40551                 case 'object' :
40552                     break;
40553                 case 'string' :
40554                     template = new Roo.Template(template);
40555                     break;
40556                 default :
40557                     break;
40558             }
40559             
40560             var el = template.overwrite(td, {"text": text});
40561             
40562             var inner = el.getElementsByTagName("span")[0];
40563             
40564             return {"el": el, "inner": inner};
40565             
40566     }
40567         
40568     
40569 });
40570
40571 /**
40572  * @class Roo.TabPanelItem
40573  * @extends Roo.util.Observable
40574  * Represents an individual item (tab plus body) in a TabPanel.
40575  * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
40576  * @param {String} id The id of this TabPanelItem
40577  * @param {String} text The text for the tab of this TabPanelItem
40578  * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
40579  */
40580 Roo.bootstrap.panel.TabItem = function(config){
40581     /**
40582      * The {@link Roo.TabPanel} this TabPanelItem belongs to
40583      * @type Roo.TabPanel
40584      */
40585     this.tabPanel = config.panel;
40586     /**
40587      * The id for this TabPanelItem
40588      * @type String
40589      */
40590     this.id = config.id;
40591     /** @private */
40592     this.disabled = false;
40593     /** @private */
40594     this.text = config.text;
40595     /** @private */
40596     this.loaded = false;
40597     this.closable = config.closable;
40598
40599     /**
40600      * The body element for this TabPanelItem.
40601      * @type Roo.Element
40602      */
40603     this.bodyEl = Roo.get(this.tabPanel.createItemBody(this.tabPanel.bodyEl.dom, config.id));
40604     this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
40605     this.bodyEl.setStyle("display", "block");
40606     this.bodyEl.setStyle("zoom", "1");
40607     //this.hideAction();
40608
40609     var els = this.tabPanel.createStripElements(this.tabPanel.stripEl.dom, config.text, config.closable, config.tpl);
40610     /** @private */
40611     this.el = Roo.get(els.el);
40612     this.inner = Roo.get(els.inner, true);
40613      this.textEl = Roo.bootstrap.version == 4 ?
40614         this.el : Roo.get(this.el.dom.firstChild, true);
40615
40616     this.pnode = this.linode = Roo.get(els.el.parentNode, true);
40617     this.status_node = Roo.bootstrap.version == 4 ? this.el : this.linode;
40618
40619     
40620 //    this.el.on("mousedown", this.onTabMouseDown, this);
40621     this.el.on("click", this.onTabClick, this);
40622     /** @private */
40623     if(config.closable){
40624         var c = Roo.get(els.close, true);
40625         c.dom.title = this.closeText;
40626         c.addClassOnOver("close-over");
40627         c.on("click", this.closeClick, this);
40628      }
40629
40630     this.addEvents({
40631          /**
40632          * @event activate
40633          * Fires when this tab becomes the active tab.
40634          * @param {Roo.TabPanel} tabPanel The parent TabPanel
40635          * @param {Roo.TabPanelItem} this
40636          */
40637         "activate": true,
40638         /**
40639          * @event beforeclose
40640          * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
40641          * @param {Roo.TabPanelItem} this
40642          * @param {Object} e Set cancel to true on this object to cancel the close.
40643          */
40644         "beforeclose": true,
40645         /**
40646          * @event close
40647          * Fires when this tab is closed.
40648          * @param {Roo.TabPanelItem} this
40649          */
40650          "close": true,
40651         /**
40652          * @event deactivate
40653          * Fires when this tab is no longer the active tab.
40654          * @param {Roo.TabPanel} tabPanel The parent TabPanel
40655          * @param {Roo.TabPanelItem} this
40656          */
40657          "deactivate" : true
40658     });
40659     this.hidden = false;
40660
40661     Roo.bootstrap.panel.TabItem.superclass.constructor.call(this);
40662 };
40663
40664 Roo.extend(Roo.bootstrap.panel.TabItem, Roo.util.Observable,
40665            {
40666     purgeListeners : function(){
40667        Roo.util.Observable.prototype.purgeListeners.call(this);
40668        this.el.removeAllListeners();
40669     },
40670     /**
40671      * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
40672      */
40673     show : function(){
40674         this.status_node.addClass("active");
40675         this.showAction();
40676         if(Roo.isOpera){
40677             this.tabPanel.stripWrap.repaint();
40678         }
40679         this.fireEvent("activate", this.tabPanel, this);
40680     },
40681
40682     /**
40683      * Returns true if this tab is the active tab.
40684      * @return {Boolean}
40685      */
40686     isActive : function(){
40687         return this.tabPanel.getActiveTab() == this;
40688     },
40689
40690     /**
40691      * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
40692      */
40693     hide : function(){
40694         this.status_node.removeClass("active");
40695         this.hideAction();
40696         this.fireEvent("deactivate", this.tabPanel, this);
40697     },
40698
40699     hideAction : function(){
40700         this.bodyEl.hide();
40701         this.bodyEl.setStyle("position", "absolute");
40702         this.bodyEl.setLeft("-20000px");
40703         this.bodyEl.setTop("-20000px");
40704     },
40705
40706     showAction : function(){
40707         this.bodyEl.setStyle("position", "relative");
40708         this.bodyEl.setTop("");
40709         this.bodyEl.setLeft("");
40710         this.bodyEl.show();
40711     },
40712
40713     /**
40714      * Set the tooltip for the tab.
40715      * @param {String} tooltip The tab's tooltip
40716      */
40717     setTooltip : function(text){
40718         if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
40719             this.textEl.dom.qtip = text;
40720             this.textEl.dom.removeAttribute('title');
40721         }else{
40722             this.textEl.dom.title = text;
40723         }
40724     },
40725
40726     onTabClick : function(e){
40727         e.preventDefault();
40728         this.tabPanel.activate(this.id);
40729     },
40730
40731     onTabMouseDown : function(e){
40732         e.preventDefault();
40733         this.tabPanel.activate(this.id);
40734     },
40735 /*
40736     getWidth : function(){
40737         return this.inner.getWidth();
40738     },
40739
40740     setWidth : function(width){
40741         var iwidth = width - this.linode.getPadding("lr");
40742         this.inner.setWidth(iwidth);
40743         this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
40744         this.linode.setWidth(width);
40745     },
40746 */
40747     /**
40748      * Show or hide the tab
40749      * @param {Boolean} hidden True to hide or false to show.
40750      */
40751     setHidden : function(hidden){
40752         this.hidden = hidden;
40753         this.linode.setStyle("display", hidden ? "none" : "");
40754     },
40755
40756     /**
40757      * Returns true if this tab is "hidden"
40758      * @return {Boolean}
40759      */
40760     isHidden : function(){
40761         return this.hidden;
40762     },
40763
40764     /**
40765      * Returns the text for this tab
40766      * @return {String}
40767      */
40768     getText : function(){
40769         return this.text;
40770     },
40771     /*
40772     autoSize : function(){
40773         //this.el.beginMeasure();
40774         this.textEl.setWidth(1);
40775         /*
40776          *  #2804 [new] Tabs in Roojs
40777          *  increase the width by 2-4 pixels to prevent the ellipssis showing in chrome
40778          */
40779         //this.setWidth(this.textEl.dom.scrollWidth+this.linode.getPadding("lr")+this.inner.getPadding("lr") + 2);
40780         //this.el.endMeasure();
40781     //},
40782
40783     /**
40784      * Sets the text for the tab (Note: this also sets the tooltip text)
40785      * @param {String} text The tab's text and tooltip
40786      */
40787     setText : function(text){
40788         this.text = text;
40789         this.textEl.update(text);
40790         this.setTooltip(text);
40791         //if(!this.tabPanel.resizeTabs){
40792         //    this.autoSize();
40793         //}
40794     },
40795     /**
40796      * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
40797      */
40798     activate : function(){
40799         this.tabPanel.activate(this.id);
40800     },
40801
40802     /**
40803      * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
40804      */
40805     disable : function(){
40806         if(this.tabPanel.active != this){
40807             this.disabled = true;
40808             this.status_node.addClass("disabled");
40809         }
40810     },
40811
40812     /**
40813      * Enables this TabPanelItem if it was previously disabled.
40814      */
40815     enable : function(){
40816         this.disabled = false;
40817         this.status_node.removeClass("disabled");
40818     },
40819
40820     /**
40821      * Sets the content for this TabPanelItem.
40822      * @param {String} content The content
40823      * @param {Boolean} loadScripts true to look for and load scripts
40824      */
40825     setContent : function(content, loadScripts){
40826         this.bodyEl.update(content, loadScripts);
40827     },
40828
40829     /**
40830      * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
40831      * @return {Roo.UpdateManager} The UpdateManager
40832      */
40833     getUpdateManager : function(){
40834         return this.bodyEl.getUpdateManager();
40835     },
40836
40837     /**
40838      * Set a URL to be used to load the content for this TabPanelItem.
40839      * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
40840      * @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)
40841      * @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)
40842      * @return {Roo.UpdateManager} The UpdateManager
40843      */
40844     setUrl : function(url, params, loadOnce){
40845         if(this.refreshDelegate){
40846             this.un('activate', this.refreshDelegate);
40847         }
40848         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
40849         this.on("activate", this.refreshDelegate);
40850         return this.bodyEl.getUpdateManager();
40851     },
40852
40853     /** @private */
40854     _handleRefresh : function(url, params, loadOnce){
40855         if(!loadOnce || !this.loaded){
40856             var updater = this.bodyEl.getUpdateManager();
40857             updater.update(url, params, this._setLoaded.createDelegate(this));
40858         }
40859     },
40860
40861     /**
40862      *   Forces a content refresh from the URL specified in the {@link #setUrl} method.
40863      *   Will fail silently if the setUrl method has not been called.
40864      *   This does not activate the panel, just updates its content.
40865      */
40866     refresh : function(){
40867         if(this.refreshDelegate){
40868            this.loaded = false;
40869            this.refreshDelegate();
40870         }
40871     },
40872
40873     /** @private */
40874     _setLoaded : function(){
40875         this.loaded = true;
40876     },
40877
40878     /** @private */
40879     closeClick : function(e){
40880         var o = {};
40881         e.stopEvent();
40882         this.fireEvent("beforeclose", this, o);
40883         if(o.cancel !== true){
40884             this.tabPanel.removeTab(this.id);
40885         }
40886     },
40887     /**
40888      * The text displayed in the tooltip for the close icon.
40889      * @type String
40890      */
40891     closeText : "Close this tab"
40892 });
40893 /**
40894 *    This script refer to:
40895 *    Title: International Telephone Input
40896 *    Author: Jack O'Connor
40897 *    Code version:  v12.1.12
40898 *    Availability: https://github.com/jackocnr/intl-tel-input.git
40899 **/
40900
40901 Roo.bootstrap.PhoneInputData = function() {
40902     var d = [
40903       [
40904         "Afghanistan (‫افغانستان‬‎)",
40905         "af",
40906         "93"
40907       ],
40908       [
40909         "Albania (Shqipëri)",
40910         "al",
40911         "355"
40912       ],
40913       [
40914         "Algeria (‫الجزائر‬‎)",
40915         "dz",
40916         "213"
40917       ],
40918       [
40919         "American Samoa",
40920         "as",
40921         "1684"
40922       ],
40923       [
40924         "Andorra",
40925         "ad",
40926         "376"
40927       ],
40928       [
40929         "Angola",
40930         "ao",
40931         "244"
40932       ],
40933       [
40934         "Anguilla",
40935         "ai",
40936         "1264"
40937       ],
40938       [
40939         "Antigua and Barbuda",
40940         "ag",
40941         "1268"
40942       ],
40943       [
40944         "Argentina",
40945         "ar",
40946         "54"
40947       ],
40948       [
40949         "Armenia (Հայաստան)",
40950         "am",
40951         "374"
40952       ],
40953       [
40954         "Aruba",
40955         "aw",
40956         "297"
40957       ],
40958       [
40959         "Australia",
40960         "au",
40961         "61",
40962         0
40963       ],
40964       [
40965         "Austria (Österreich)",
40966         "at",
40967         "43"
40968       ],
40969       [
40970         "Azerbaijan (Azərbaycan)",
40971         "az",
40972         "994"
40973       ],
40974       [
40975         "Bahamas",
40976         "bs",
40977         "1242"
40978       ],
40979       [
40980         "Bahrain (‫البحرين‬‎)",
40981         "bh",
40982         "973"
40983       ],
40984       [
40985         "Bangladesh (বাংলাদেশ)",
40986         "bd",
40987         "880"
40988       ],
40989       [
40990         "Barbados",
40991         "bb",
40992         "1246"
40993       ],
40994       [
40995         "Belarus (Беларусь)",
40996         "by",
40997         "375"
40998       ],
40999       [
41000         "Belgium (België)",
41001         "be",
41002         "32"
41003       ],
41004       [
41005         "Belize",
41006         "bz",
41007         "501"
41008       ],
41009       [
41010         "Benin (Bénin)",
41011         "bj",
41012         "229"
41013       ],
41014       [
41015         "Bermuda",
41016         "bm",
41017         "1441"
41018       ],
41019       [
41020         "Bhutan (འབྲུག)",
41021         "bt",
41022         "975"
41023       ],
41024       [
41025         "Bolivia",
41026         "bo",
41027         "591"
41028       ],
41029       [
41030         "Bosnia and Herzegovina (Босна и Херцеговина)",
41031         "ba",
41032         "387"
41033       ],
41034       [
41035         "Botswana",
41036         "bw",
41037         "267"
41038       ],
41039       [
41040         "Brazil (Brasil)",
41041         "br",
41042         "55"
41043       ],
41044       [
41045         "British Indian Ocean Territory",
41046         "io",
41047         "246"
41048       ],
41049       [
41050         "British Virgin Islands",
41051         "vg",
41052         "1284"
41053       ],
41054       [
41055         "Brunei",
41056         "bn",
41057         "673"
41058       ],
41059       [
41060         "Bulgaria (България)",
41061         "bg",
41062         "359"
41063       ],
41064       [
41065         "Burkina Faso",
41066         "bf",
41067         "226"
41068       ],
41069       [
41070         "Burundi (Uburundi)",
41071         "bi",
41072         "257"
41073       ],
41074       [
41075         "Cambodia (កម្ពុជា)",
41076         "kh",
41077         "855"
41078       ],
41079       [
41080         "Cameroon (Cameroun)",
41081         "cm",
41082         "237"
41083       ],
41084       [
41085         "Canada",
41086         "ca",
41087         "1",
41088         1,
41089         ["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"]
41090       ],
41091       [
41092         "Cape Verde (Kabu Verdi)",
41093         "cv",
41094         "238"
41095       ],
41096       [
41097         "Caribbean Netherlands",
41098         "bq",
41099         "599",
41100         1
41101       ],
41102       [
41103         "Cayman Islands",
41104         "ky",
41105         "1345"
41106       ],
41107       [
41108         "Central African Republic (République centrafricaine)",
41109         "cf",
41110         "236"
41111       ],
41112       [
41113         "Chad (Tchad)",
41114         "td",
41115         "235"
41116       ],
41117       [
41118         "Chile",
41119         "cl",
41120         "56"
41121       ],
41122       [
41123         "China (中国)",
41124         "cn",
41125         "86"
41126       ],
41127       [
41128         "Christmas Island",
41129         "cx",
41130         "61",
41131         2
41132       ],
41133       [
41134         "Cocos (Keeling) Islands",
41135         "cc",
41136         "61",
41137         1
41138       ],
41139       [
41140         "Colombia",
41141         "co",
41142         "57"
41143       ],
41144       [
41145         "Comoros (‫جزر القمر‬‎)",
41146         "km",
41147         "269"
41148       ],
41149       [
41150         "Congo (DRC) (Jamhuri ya Kidemokrasia ya Kongo)",
41151         "cd",
41152         "243"
41153       ],
41154       [
41155         "Congo (Republic) (Congo-Brazzaville)",
41156         "cg",
41157         "242"
41158       ],
41159       [
41160         "Cook Islands",
41161         "ck",
41162         "682"
41163       ],
41164       [
41165         "Costa Rica",
41166         "cr",
41167         "506"
41168       ],
41169       [
41170         "Côte d’Ivoire",
41171         "ci",
41172         "225"
41173       ],
41174       [
41175         "Croatia (Hrvatska)",
41176         "hr",
41177         "385"
41178       ],
41179       [
41180         "Cuba",
41181         "cu",
41182         "53"
41183       ],
41184       [
41185         "Curaçao",
41186         "cw",
41187         "599",
41188         0
41189       ],
41190       [
41191         "Cyprus (Κύπρος)",
41192         "cy",
41193         "357"
41194       ],
41195       [
41196         "Czech Republic (Česká republika)",
41197         "cz",
41198         "420"
41199       ],
41200       [
41201         "Denmark (Danmark)",
41202         "dk",
41203         "45"
41204       ],
41205       [
41206         "Djibouti",
41207         "dj",
41208         "253"
41209       ],
41210       [
41211         "Dominica",
41212         "dm",
41213         "1767"
41214       ],
41215       [
41216         "Dominican Republic (República Dominicana)",
41217         "do",
41218         "1",
41219         2,
41220         ["809", "829", "849"]
41221       ],
41222       [
41223         "Ecuador",
41224         "ec",
41225         "593"
41226       ],
41227       [
41228         "Egypt (‫مصر‬‎)",
41229         "eg",
41230         "20"
41231       ],
41232       [
41233         "El Salvador",
41234         "sv",
41235         "503"
41236       ],
41237       [
41238         "Equatorial Guinea (Guinea Ecuatorial)",
41239         "gq",
41240         "240"
41241       ],
41242       [
41243         "Eritrea",
41244         "er",
41245         "291"
41246       ],
41247       [
41248         "Estonia (Eesti)",
41249         "ee",
41250         "372"
41251       ],
41252       [
41253         "Ethiopia",
41254         "et",
41255         "251"
41256       ],
41257       [
41258         "Falkland Islands (Islas Malvinas)",
41259         "fk",
41260         "500"
41261       ],
41262       [
41263         "Faroe Islands (Føroyar)",
41264         "fo",
41265         "298"
41266       ],
41267       [
41268         "Fiji",
41269         "fj",
41270         "679"
41271       ],
41272       [
41273         "Finland (Suomi)",
41274         "fi",
41275         "358",
41276         0
41277       ],
41278       [
41279         "France",
41280         "fr",
41281         "33"
41282       ],
41283       [
41284         "French Guiana (Guyane française)",
41285         "gf",
41286         "594"
41287       ],
41288       [
41289         "French Polynesia (Polynésie française)",
41290         "pf",
41291         "689"
41292       ],
41293       [
41294         "Gabon",
41295         "ga",
41296         "241"
41297       ],
41298       [
41299         "Gambia",
41300         "gm",
41301         "220"
41302       ],
41303       [
41304         "Georgia (საქართველო)",
41305         "ge",
41306         "995"
41307       ],
41308       [
41309         "Germany (Deutschland)",
41310         "de",
41311         "49"
41312       ],
41313       [
41314         "Ghana (Gaana)",
41315         "gh",
41316         "233"
41317       ],
41318       [
41319         "Gibraltar",
41320         "gi",
41321         "350"
41322       ],
41323       [
41324         "Greece (Ελλάδα)",
41325         "gr",
41326         "30"
41327       ],
41328       [
41329         "Greenland (Kalaallit Nunaat)",
41330         "gl",
41331         "299"
41332       ],
41333       [
41334         "Grenada",
41335         "gd",
41336         "1473"
41337       ],
41338       [
41339         "Guadeloupe",
41340         "gp",
41341         "590",
41342         0
41343       ],
41344       [
41345         "Guam",
41346         "gu",
41347         "1671"
41348       ],
41349       [
41350         "Guatemala",
41351         "gt",
41352         "502"
41353       ],
41354       [
41355         "Guernsey",
41356         "gg",
41357         "44",
41358         1
41359       ],
41360       [
41361         "Guinea (Guinée)",
41362         "gn",
41363         "224"
41364       ],
41365       [
41366         "Guinea-Bissau (Guiné Bissau)",
41367         "gw",
41368         "245"
41369       ],
41370       [
41371         "Guyana",
41372         "gy",
41373         "592"
41374       ],
41375       [
41376         "Haiti",
41377         "ht",
41378         "509"
41379       ],
41380       [
41381         "Honduras",
41382         "hn",
41383         "504"
41384       ],
41385       [
41386         "Hong Kong (香港)",
41387         "hk",
41388         "852"
41389       ],
41390       [
41391         "Hungary (Magyarország)",
41392         "hu",
41393         "36"
41394       ],
41395       [
41396         "Iceland (Ísland)",
41397         "is",
41398         "354"
41399       ],
41400       [
41401         "India (भारत)",
41402         "in",
41403         "91"
41404       ],
41405       [
41406         "Indonesia",
41407         "id",
41408         "62"
41409       ],
41410       [
41411         "Iran (‫ایران‬‎)",
41412         "ir",
41413         "98"
41414       ],
41415       [
41416         "Iraq (‫العراق‬‎)",
41417         "iq",
41418         "964"
41419       ],
41420       [
41421         "Ireland",
41422         "ie",
41423         "353"
41424       ],
41425       [
41426         "Isle of Man",
41427         "im",
41428         "44",
41429         2
41430       ],
41431       [
41432         "Israel (‫ישראל‬‎)",
41433         "il",
41434         "972"
41435       ],
41436       [
41437         "Italy (Italia)",
41438         "it",
41439         "39",
41440         0
41441       ],
41442       [
41443         "Jamaica",
41444         "jm",
41445         "1876"
41446       ],
41447       [
41448         "Japan (日本)",
41449         "jp",
41450         "81"
41451       ],
41452       [
41453         "Jersey",
41454         "je",
41455         "44",
41456         3
41457       ],
41458       [
41459         "Jordan (‫الأردن‬‎)",
41460         "jo",
41461         "962"
41462       ],
41463       [
41464         "Kazakhstan (Казахстан)",
41465         "kz",
41466         "7",
41467         1
41468       ],
41469       [
41470         "Kenya",
41471         "ke",
41472         "254"
41473       ],
41474       [
41475         "Kiribati",
41476         "ki",
41477         "686"
41478       ],
41479       [
41480         "Kosovo",
41481         "xk",
41482         "383"
41483       ],
41484       [
41485         "Kuwait (‫الكويت‬‎)",
41486         "kw",
41487         "965"
41488       ],
41489       [
41490         "Kyrgyzstan (Кыргызстан)",
41491         "kg",
41492         "996"
41493       ],
41494       [
41495         "Laos (ລາວ)",
41496         "la",
41497         "856"
41498       ],
41499       [
41500         "Latvia (Latvija)",
41501         "lv",
41502         "371"
41503       ],
41504       [
41505         "Lebanon (‫لبنان‬‎)",
41506         "lb",
41507         "961"
41508       ],
41509       [
41510         "Lesotho",
41511         "ls",
41512         "266"
41513       ],
41514       [
41515         "Liberia",
41516         "lr",
41517         "231"
41518       ],
41519       [
41520         "Libya (‫ليبيا‬‎)",
41521         "ly",
41522         "218"
41523       ],
41524       [
41525         "Liechtenstein",
41526         "li",
41527         "423"
41528       ],
41529       [
41530         "Lithuania (Lietuva)",
41531         "lt",
41532         "370"
41533       ],
41534       [
41535         "Luxembourg",
41536         "lu",
41537         "352"
41538       ],
41539       [
41540         "Macau (澳門)",
41541         "mo",
41542         "853"
41543       ],
41544       [
41545         "Macedonia (FYROM) (Македонија)",
41546         "mk",
41547         "389"
41548       ],
41549       [
41550         "Madagascar (Madagasikara)",
41551         "mg",
41552         "261"
41553       ],
41554       [
41555         "Malawi",
41556         "mw",
41557         "265"
41558       ],
41559       [
41560         "Malaysia",
41561         "my",
41562         "60"
41563       ],
41564       [
41565         "Maldives",
41566         "mv",
41567         "960"
41568       ],
41569       [
41570         "Mali",
41571         "ml",
41572         "223"
41573       ],
41574       [
41575         "Malta",
41576         "mt",
41577         "356"
41578       ],
41579       [
41580         "Marshall Islands",
41581         "mh",
41582         "692"
41583       ],
41584       [
41585         "Martinique",
41586         "mq",
41587         "596"
41588       ],
41589       [
41590         "Mauritania (‫موريتانيا‬‎)",
41591         "mr",
41592         "222"
41593       ],
41594       [
41595         "Mauritius (Moris)",
41596         "mu",
41597         "230"
41598       ],
41599       [
41600         "Mayotte",
41601         "yt",
41602         "262",
41603         1
41604       ],
41605       [
41606         "Mexico (México)",
41607         "mx",
41608         "52"
41609       ],
41610       [
41611         "Micronesia",
41612         "fm",
41613         "691"
41614       ],
41615       [
41616         "Moldova (Republica Moldova)",
41617         "md",
41618         "373"
41619       ],
41620       [
41621         "Monaco",
41622         "mc",
41623         "377"
41624       ],
41625       [
41626         "Mongolia (Монгол)",
41627         "mn",
41628         "976"
41629       ],
41630       [
41631         "Montenegro (Crna Gora)",
41632         "me",
41633         "382"
41634       ],
41635       [
41636         "Montserrat",
41637         "ms",
41638         "1664"
41639       ],
41640       [
41641         "Morocco (‫المغرب‬‎)",
41642         "ma",
41643         "212",
41644         0
41645       ],
41646       [
41647         "Mozambique (Moçambique)",
41648         "mz",
41649         "258"
41650       ],
41651       [
41652         "Myanmar (Burma) (မြန်မာ)",
41653         "mm",
41654         "95"
41655       ],
41656       [
41657         "Namibia (Namibië)",
41658         "na",
41659         "264"
41660       ],
41661       [
41662         "Nauru",
41663         "nr",
41664         "674"
41665       ],
41666       [
41667         "Nepal (नेपाल)",
41668         "np",
41669         "977"
41670       ],
41671       [
41672         "Netherlands (Nederland)",
41673         "nl",
41674         "31"
41675       ],
41676       [
41677         "New Caledonia (Nouvelle-Calédonie)",
41678         "nc",
41679         "687"
41680       ],
41681       [
41682         "New Zealand",
41683         "nz",
41684         "64"
41685       ],
41686       [
41687         "Nicaragua",
41688         "ni",
41689         "505"
41690       ],
41691       [
41692         "Niger (Nijar)",
41693         "ne",
41694         "227"
41695       ],
41696       [
41697         "Nigeria",
41698         "ng",
41699         "234"
41700       ],
41701       [
41702         "Niue",
41703         "nu",
41704         "683"
41705       ],
41706       [
41707         "Norfolk Island",
41708         "nf",
41709         "672"
41710       ],
41711       [
41712         "North Korea (조선 민주주의 인민 공화국)",
41713         "kp",
41714         "850"
41715       ],
41716       [
41717         "Northern Mariana Islands",
41718         "mp",
41719         "1670"
41720       ],
41721       [
41722         "Norway (Norge)",
41723         "no",
41724         "47",
41725         0
41726       ],
41727       [
41728         "Oman (‫عُمان‬‎)",
41729         "om",
41730         "968"
41731       ],
41732       [
41733         "Pakistan (‫پاکستان‬‎)",
41734         "pk",
41735         "92"
41736       ],
41737       [
41738         "Palau",
41739         "pw",
41740         "680"
41741       ],
41742       [
41743         "Palestine (‫فلسطين‬‎)",
41744         "ps",
41745         "970"
41746       ],
41747       [
41748         "Panama (Panamá)",
41749         "pa",
41750         "507"
41751       ],
41752       [
41753         "Papua New Guinea",
41754         "pg",
41755         "675"
41756       ],
41757       [
41758         "Paraguay",
41759         "py",
41760         "595"
41761       ],
41762       [
41763         "Peru (Perú)",
41764         "pe",
41765         "51"
41766       ],
41767       [
41768         "Philippines",
41769         "ph",
41770         "63"
41771       ],
41772       [
41773         "Poland (Polska)",
41774         "pl",
41775         "48"
41776       ],
41777       [
41778         "Portugal",
41779         "pt",
41780         "351"
41781       ],
41782       [
41783         "Puerto Rico",
41784         "pr",
41785         "1",
41786         3,
41787         ["787", "939"]
41788       ],
41789       [
41790         "Qatar (‫قطر‬‎)",
41791         "qa",
41792         "974"
41793       ],
41794       [
41795         "Réunion (La Réunion)",
41796         "re",
41797         "262",
41798         0
41799       ],
41800       [
41801         "Romania (România)",
41802         "ro",
41803         "40"
41804       ],
41805       [
41806         "Russia (Россия)",
41807         "ru",
41808         "7",
41809         0
41810       ],
41811       [
41812         "Rwanda",
41813         "rw",
41814         "250"
41815       ],
41816       [
41817         "Saint Barthélemy",
41818         "bl",
41819         "590",
41820         1
41821       ],
41822       [
41823         "Saint Helena",
41824         "sh",
41825         "290"
41826       ],
41827       [
41828         "Saint Kitts and Nevis",
41829         "kn",
41830         "1869"
41831       ],
41832       [
41833         "Saint Lucia",
41834         "lc",
41835         "1758"
41836       ],
41837       [
41838         "Saint Martin (Saint-Martin (partie française))",
41839         "mf",
41840         "590",
41841         2
41842       ],
41843       [
41844         "Saint Pierre and Miquelon (Saint-Pierre-et-Miquelon)",
41845         "pm",
41846         "508"
41847       ],
41848       [
41849         "Saint Vincent and the Grenadines",
41850         "vc",
41851         "1784"
41852       ],
41853       [
41854         "Samoa",
41855         "ws",
41856         "685"
41857       ],
41858       [
41859         "San Marino",
41860         "sm",
41861         "378"
41862       ],
41863       [
41864         "São Tomé and Príncipe (São Tomé e Príncipe)",
41865         "st",
41866         "239"
41867       ],
41868       [
41869         "Saudi Arabia (‫المملكة العربية السعودية‬‎)",
41870         "sa",
41871         "966"
41872       ],
41873       [
41874         "Senegal (Sénégal)",
41875         "sn",
41876         "221"
41877       ],
41878       [
41879         "Serbia (Србија)",
41880         "rs",
41881         "381"
41882       ],
41883       [
41884         "Seychelles",
41885         "sc",
41886         "248"
41887       ],
41888       [
41889         "Sierra Leone",
41890         "sl",
41891         "232"
41892       ],
41893       [
41894         "Singapore",
41895         "sg",
41896         "65"
41897       ],
41898       [
41899         "Sint Maarten",
41900         "sx",
41901         "1721"
41902       ],
41903       [
41904         "Slovakia (Slovensko)",
41905         "sk",
41906         "421"
41907       ],
41908       [
41909         "Slovenia (Slovenija)",
41910         "si",
41911         "386"
41912       ],
41913       [
41914         "Solomon Islands",
41915         "sb",
41916         "677"
41917       ],
41918       [
41919         "Somalia (Soomaaliya)",
41920         "so",
41921         "252"
41922       ],
41923       [
41924         "South Africa",
41925         "za",
41926         "27"
41927       ],
41928       [
41929         "South Korea (대한민국)",
41930         "kr",
41931         "82"
41932       ],
41933       [
41934         "South Sudan (‫جنوب السودان‬‎)",
41935         "ss",
41936         "211"
41937       ],
41938       [
41939         "Spain (España)",
41940         "es",
41941         "34"
41942       ],
41943       [
41944         "Sri Lanka (ශ්‍රී ලංකාව)",
41945         "lk",
41946         "94"
41947       ],
41948       [
41949         "Sudan (‫السودان‬‎)",
41950         "sd",
41951         "249"
41952       ],
41953       [
41954         "Suriname",
41955         "sr",
41956         "597"
41957       ],
41958       [
41959         "Svalbard and Jan Mayen",
41960         "sj",
41961         "47",
41962         1
41963       ],
41964       [
41965         "Swaziland",
41966         "sz",
41967         "268"
41968       ],
41969       [
41970         "Sweden (Sverige)",
41971         "se",
41972         "46"
41973       ],
41974       [
41975         "Switzerland (Schweiz)",
41976         "ch",
41977         "41"
41978       ],
41979       [
41980         "Syria (‫سوريا‬‎)",
41981         "sy",
41982         "963"
41983       ],
41984       [
41985         "Taiwan (台灣)",
41986         "tw",
41987         "886"
41988       ],
41989       [
41990         "Tajikistan",
41991         "tj",
41992         "992"
41993       ],
41994       [
41995         "Tanzania",
41996         "tz",
41997         "255"
41998       ],
41999       [
42000         "Thailand (ไทย)",
42001         "th",
42002         "66"
42003       ],
42004       [
42005         "Timor-Leste",
42006         "tl",
42007         "670"
42008       ],
42009       [
42010         "Togo",
42011         "tg",
42012         "228"
42013       ],
42014       [
42015         "Tokelau",
42016         "tk",
42017         "690"
42018       ],
42019       [
42020         "Tonga",
42021         "to",
42022         "676"
42023       ],
42024       [
42025         "Trinidad and Tobago",
42026         "tt",
42027         "1868"
42028       ],
42029       [
42030         "Tunisia (‫تونس‬‎)",
42031         "tn",
42032         "216"
42033       ],
42034       [
42035         "Turkey (Türkiye)",
42036         "tr",
42037         "90"
42038       ],
42039       [
42040         "Turkmenistan",
42041         "tm",
42042         "993"
42043       ],
42044       [
42045         "Turks and Caicos Islands",
42046         "tc",
42047         "1649"
42048       ],
42049       [
42050         "Tuvalu",
42051         "tv",
42052         "688"
42053       ],
42054       [
42055         "U.S. Virgin Islands",
42056         "vi",
42057         "1340"
42058       ],
42059       [
42060         "Uganda",
42061         "ug",
42062         "256"
42063       ],
42064       [
42065         "Ukraine (Україна)",
42066         "ua",
42067         "380"
42068       ],
42069       [
42070         "United Arab Emirates (‫الإمارات العربية المتحدة‬‎)",
42071         "ae",
42072         "971"
42073       ],
42074       [
42075         "United Kingdom",
42076         "gb",
42077         "44",
42078         0
42079       ],
42080       [
42081         "United States",
42082         "us",
42083         "1",
42084         0
42085       ],
42086       [
42087         "Uruguay",
42088         "uy",
42089         "598"
42090       ],
42091       [
42092         "Uzbekistan (Oʻzbekiston)",
42093         "uz",
42094         "998"
42095       ],
42096       [
42097         "Vanuatu",
42098         "vu",
42099         "678"
42100       ],
42101       [
42102         "Vatican City (Città del Vaticano)",
42103         "va",
42104         "39",
42105         1
42106       ],
42107       [
42108         "Venezuela",
42109         "ve",
42110         "58"
42111       ],
42112       [
42113         "Vietnam (Việt Nam)",
42114         "vn",
42115         "84"
42116       ],
42117       [
42118         "Wallis and Futuna (Wallis-et-Futuna)",
42119         "wf",
42120         "681"
42121       ],
42122       [
42123         "Western Sahara (‫الصحراء الغربية‬‎)",
42124         "eh",
42125         "212",
42126         1
42127       ],
42128       [
42129         "Yemen (‫اليمن‬‎)",
42130         "ye",
42131         "967"
42132       ],
42133       [
42134         "Zambia",
42135         "zm",
42136         "260"
42137       ],
42138       [
42139         "Zimbabwe",
42140         "zw",
42141         "263"
42142       ],
42143       [
42144         "Åland Islands",
42145         "ax",
42146         "358",
42147         1
42148       ]
42149   ];
42150   
42151   return d;
42152 }/**
42153 *    This script refer to:
42154 *    Title: International Telephone Input
42155 *    Author: Jack O'Connor
42156 *    Code version:  v12.1.12
42157 *    Availability: https://github.com/jackocnr/intl-tel-input.git
42158 **/
42159
42160 /**
42161  * @class Roo.bootstrap.PhoneInput
42162  * @extends Roo.bootstrap.TriggerField
42163  * An input with International dial-code selection
42164  
42165  * @cfg {String} defaultDialCode default '+852'
42166  * @cfg {Array} preferedCountries default []
42167   
42168  * @constructor
42169  * Create a new PhoneInput.
42170  * @param {Object} config Configuration options
42171  */
42172
42173 Roo.bootstrap.PhoneInput = function(config) {
42174     Roo.bootstrap.PhoneInput.superclass.constructor.call(this, config);
42175 };
42176
42177 Roo.extend(Roo.bootstrap.PhoneInput, Roo.bootstrap.TriggerField, {
42178         
42179         listWidth: undefined,
42180         
42181         selectedClass: 'active',
42182         
42183         invalidClass : "has-warning",
42184         
42185         validClass: 'has-success',
42186         
42187         allowed: '0123456789',
42188         
42189         max_length: 15,
42190         
42191         /**
42192          * @cfg {String} defaultDialCode The default dial code when initializing the input
42193          */
42194         defaultDialCode: '+852',
42195         
42196         /**
42197          * @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
42198          */
42199         preferedCountries: false,
42200         
42201         getAutoCreate : function()
42202         {
42203             var data = Roo.bootstrap.PhoneInputData();
42204             var align = this.labelAlign || this.parentLabelAlign();
42205             var id = Roo.id();
42206             
42207             this.allCountries = [];
42208             this.dialCodeMapping = [];
42209             
42210             for (var i = 0; i < data.length; i++) {
42211               var c = data[i];
42212               this.allCountries[i] = {
42213                 name: c[0],
42214                 iso2: c[1],
42215                 dialCode: c[2],
42216                 priority: c[3] || 0,
42217                 areaCodes: c[4] || null
42218               };
42219               this.dialCodeMapping[c[2]] = {
42220                   name: c[0],
42221                   iso2: c[1],
42222                   priority: c[3] || 0,
42223                   areaCodes: c[4] || null
42224               };
42225             }
42226             
42227             var cfg = {
42228                 cls: 'form-group',
42229                 cn: []
42230             };
42231             
42232             var input =  {
42233                 tag: 'input',
42234                 id : id,
42235                 // type: 'number', -- do not use number - we get the flaky up/down arrows.
42236                 maxlength: this.max_length,
42237                 cls : 'form-control tel-input',
42238                 autocomplete: 'new-password'
42239             };
42240             
42241             var hiddenInput = {
42242                 tag: 'input',
42243                 type: 'hidden',
42244                 cls: 'hidden-tel-input'
42245             };
42246             
42247             if (this.name) {
42248                 hiddenInput.name = this.name;
42249             }
42250             
42251             if (this.disabled) {
42252                 input.disabled = true;
42253             }
42254             
42255             var flag_container = {
42256                 tag: 'div',
42257                 cls: 'flag-box',
42258                 cn: [
42259                     {
42260                         tag: 'div',
42261                         cls: 'flag'
42262                     },
42263                     {
42264                         tag: 'div',
42265                         cls: 'caret'
42266                     }
42267                 ]
42268             };
42269             
42270             var box = {
42271                 tag: 'div',
42272                 cls: this.hasFeedback ? 'has-feedback' : '',
42273                 cn: [
42274                     hiddenInput,
42275                     input,
42276                     {
42277                         tag: 'input',
42278                         cls: 'dial-code-holder',
42279                         disabled: true
42280                     }
42281                 ]
42282             };
42283             
42284             var container = {
42285                 cls: 'roo-select2-container input-group',
42286                 cn: [
42287                     flag_container,
42288                     box
42289                 ]
42290             };
42291             
42292             if (this.fieldLabel.length) {
42293                 var indicator = {
42294                     tag: 'i',
42295                     tooltip: 'This field is required'
42296                 };
42297                 
42298                 var label = {
42299                     tag: 'label',
42300                     'for':  id,
42301                     cls: 'control-label',
42302                     cn: []
42303                 };
42304                 
42305                 var label_text = {
42306                     tag: 'span',
42307                     html: this.fieldLabel
42308                 };
42309                 
42310                 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
42311                 label.cn = [
42312                     indicator,
42313                     label_text
42314                 ];
42315                 
42316                 if(this.indicatorpos == 'right') {
42317                     indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
42318                     label.cn = [
42319                         label_text,
42320                         indicator
42321                     ];
42322                 }
42323                 
42324                 if(align == 'left') {
42325                     container = {
42326                         tag: 'div',
42327                         cn: [
42328                             container
42329                         ]
42330                     };
42331                     
42332                     if(this.labelWidth > 12){
42333                         label.style = "width: " + this.labelWidth + 'px';
42334                     }
42335                     if(this.labelWidth < 13 && this.labelmd == 0){
42336                         this.labelmd = this.labelWidth;
42337                     }
42338                     if(this.labellg > 0){
42339                         label.cls += ' col-lg-' + this.labellg;
42340                         input.cls += ' col-lg-' + (12 - this.labellg);
42341                     }
42342                     if(this.labelmd > 0){
42343                         label.cls += ' col-md-' + this.labelmd;
42344                         container.cls += ' col-md-' + (12 - this.labelmd);
42345                     }
42346                     if(this.labelsm > 0){
42347                         label.cls += ' col-sm-' + this.labelsm;
42348                         container.cls += ' col-sm-' + (12 - this.labelsm);
42349                     }
42350                     if(this.labelxs > 0){
42351                         label.cls += ' col-xs-' + this.labelxs;
42352                         container.cls += ' col-xs-' + (12 - this.labelxs);
42353                     }
42354                 }
42355             }
42356             
42357             cfg.cn = [
42358                 label,
42359                 container
42360             ];
42361             
42362             var settings = this;
42363             
42364             ['xs','sm','md','lg'].map(function(size){
42365                 if (settings[size]) {
42366                     cfg.cls += ' col-' + size + '-' + settings[size];
42367                 }
42368             });
42369             
42370             this.store = new Roo.data.Store({
42371                 proxy : new Roo.data.MemoryProxy({}),
42372                 reader : new Roo.data.JsonReader({
42373                     fields : [
42374                         {
42375                             'name' : 'name',
42376                             'type' : 'string'
42377                         },
42378                         {
42379                             'name' : 'iso2',
42380                             'type' : 'string'
42381                         },
42382                         {
42383                             'name' : 'dialCode',
42384                             'type' : 'string'
42385                         },
42386                         {
42387                             'name' : 'priority',
42388                             'type' : 'string'
42389                         },
42390                         {
42391                             'name' : 'areaCodes',
42392                             'type' : 'string'
42393                         }
42394                     ]
42395                 })
42396             });
42397             
42398             if(!this.preferedCountries) {
42399                 this.preferedCountries = [
42400                     'hk',
42401                     'gb',
42402                     'us'
42403                 ];
42404             }
42405             
42406             var p = this.preferedCountries.reverse();
42407             
42408             if(p) {
42409                 for (var i = 0; i < p.length; i++) {
42410                     for (var j = 0; j < this.allCountries.length; j++) {
42411                         if(this.allCountries[j].iso2 == p[i]) {
42412                             var t = this.allCountries[j];
42413                             this.allCountries.splice(j,1);
42414                             this.allCountries.unshift(t);
42415                         }
42416                     } 
42417                 }
42418             }
42419             
42420             this.store.proxy.data = {
42421                 success: true,
42422                 data: this.allCountries
42423             };
42424             
42425             return cfg;
42426         },
42427         
42428         initEvents : function()
42429         {
42430             this.createList();
42431             Roo.bootstrap.PhoneInput.superclass.initEvents.call(this);
42432             
42433             this.indicator = this.indicatorEl();
42434             this.flag = this.flagEl();
42435             this.dialCodeHolder = this.dialCodeHolderEl();
42436             
42437             this.trigger = this.el.select('div.flag-box',true).first();
42438             this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
42439             
42440             var _this = this;
42441             
42442             (function(){
42443                 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
42444                 _this.list.setWidth(lw);
42445             }).defer(100);
42446             
42447             this.list.on('mouseover', this.onViewOver, this);
42448             this.list.on('mousemove', this.onViewMove, this);
42449             this.inputEl().on("keyup", this.onKeyUp, this);
42450             this.inputEl().on("keypress", this.onKeyPress, this);
42451             
42452             this.tpl = '<li><a href="#"><div class="flag {iso2}"></div>{name} <span class="dial-code">+{dialCode}</span></a></li>';
42453
42454             this.view = new Roo.View(this.list, this.tpl, {
42455                 singleSelect:true, store: this.store, selectedClass: this.selectedClass
42456             });
42457             
42458             this.view.on('click', this.onViewClick, this);
42459             this.setValue(this.defaultDialCode);
42460         },
42461         
42462         onTriggerClick : function(e)
42463         {
42464             Roo.log('trigger click');
42465             if(this.disabled){
42466                 return;
42467             }
42468             
42469             if(this.isExpanded()){
42470                 this.collapse();
42471                 this.hasFocus = false;
42472             }else {
42473                 this.store.load({});
42474                 this.hasFocus = true;
42475                 this.expand();
42476             }
42477         },
42478         
42479         isExpanded : function()
42480         {
42481             return this.list.isVisible();
42482         },
42483         
42484         collapse : function()
42485         {
42486             if(!this.isExpanded()){
42487                 return;
42488             }
42489             this.list.hide();
42490             Roo.get(document).un('mousedown', this.collapseIf, this);
42491             Roo.get(document).un('mousewheel', this.collapseIf, this);
42492             this.fireEvent('collapse', this);
42493             this.validate();
42494         },
42495         
42496         expand : function()
42497         {
42498             Roo.log('expand');
42499
42500             if(this.isExpanded() || !this.hasFocus){
42501                 return;
42502             }
42503             
42504             var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
42505             this.list.setWidth(lw);
42506             
42507             this.list.show();
42508             this.restrictHeight();
42509             
42510             Roo.get(document).on('mousedown', this.collapseIf, this);
42511             Roo.get(document).on('mousewheel', this.collapseIf, this);
42512             
42513             this.fireEvent('expand', this);
42514         },
42515         
42516         restrictHeight : function()
42517         {
42518             this.list.alignTo(this.inputEl(), this.listAlign);
42519             this.list.alignTo(this.inputEl(), this.listAlign);
42520         },
42521         
42522         onViewOver : function(e, t)
42523         {
42524             if(this.inKeyMode){
42525                 return;
42526             }
42527             var item = this.view.findItemFromChild(t);
42528             
42529             if(item){
42530                 var index = this.view.indexOf(item);
42531                 this.select(index, false);
42532             }
42533         },
42534
42535         // private
42536         onViewClick : function(view, doFocus, el, e)
42537         {
42538             var index = this.view.getSelectedIndexes()[0];
42539             
42540             var r = this.store.getAt(index);
42541             
42542             if(r){
42543                 this.onSelect(r, index);
42544             }
42545             if(doFocus !== false && !this.blockFocus){
42546                 this.inputEl().focus();
42547             }
42548         },
42549         
42550         onViewMove : function(e, t)
42551         {
42552             this.inKeyMode = false;
42553         },
42554         
42555         select : function(index, scrollIntoView)
42556         {
42557             this.selectedIndex = index;
42558             this.view.select(index);
42559             if(scrollIntoView !== false){
42560                 var el = this.view.getNode(index);
42561                 if(el){
42562                     this.list.scrollChildIntoView(el, false);
42563                 }
42564             }
42565         },
42566         
42567         createList : function()
42568         {
42569             this.list = Roo.get(document.body).createChild({
42570                 tag: 'ul',
42571                 cls: 'typeahead typeahead-long dropdown-menu tel-list',
42572                 style: 'display:none'
42573             });
42574             
42575             this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
42576         },
42577         
42578         collapseIf : function(e)
42579         {
42580             var in_combo  = e.within(this.el);
42581             var in_list =  e.within(this.list);
42582             var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
42583             
42584             if (in_combo || in_list || is_list) {
42585                 return;
42586             }
42587             this.collapse();
42588         },
42589         
42590         onSelect : function(record, index)
42591         {
42592             if(this.fireEvent('beforeselect', this, record, index) !== false){
42593                 
42594                 this.setFlagClass(record.data.iso2);
42595                 this.setDialCode(record.data.dialCode);
42596                 this.hasFocus = false;
42597                 this.collapse();
42598                 this.fireEvent('select', this, record, index);
42599             }
42600         },
42601         
42602         flagEl : function()
42603         {
42604             var flag = this.el.select('div.flag',true).first();
42605             if(!flag){
42606                 return false;
42607             }
42608             return flag;
42609         },
42610         
42611         dialCodeHolderEl : function()
42612         {
42613             var d = this.el.select('input.dial-code-holder',true).first();
42614             if(!d){
42615                 return false;
42616             }
42617             return d;
42618         },
42619         
42620         setDialCode : function(v)
42621         {
42622             this.dialCodeHolder.dom.value = '+'+v;
42623         },
42624         
42625         setFlagClass : function(n)
42626         {
42627             this.flag.dom.className = 'flag '+n;
42628         },
42629         
42630         getValue : function()
42631         {
42632             var v = this.inputEl().getValue();
42633             if(this.dialCodeHolder) {
42634                 v = this.dialCodeHolder.dom.value+this.inputEl().getValue();
42635             }
42636             return v;
42637         },
42638         
42639         setValue : function(v)
42640         {
42641             var d = this.getDialCode(v);
42642             
42643             //invalid dial code
42644             if(v.length == 0 || !d || d.length == 0) {
42645                 if(this.rendered){
42646                     this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
42647                     this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
42648                 }
42649                 return;
42650             }
42651             
42652             //valid dial code
42653             this.setFlagClass(this.dialCodeMapping[d].iso2);
42654             this.setDialCode(d);
42655             this.inputEl().dom.value = v.replace('+'+d,'');
42656             this.hiddenEl().dom.value = this.getValue();
42657             
42658             this.validate();
42659         },
42660         
42661         getDialCode : function(v)
42662         {
42663             v = v ||  '';
42664             
42665             if (v.length == 0) {
42666                 return this.dialCodeHolder.dom.value;
42667             }
42668             
42669             var dialCode = "";
42670             if (v.charAt(0) != "+") {
42671                 return false;
42672             }
42673             var numericChars = "";
42674             for (var i = 1; i < v.length; i++) {
42675               var c = v.charAt(i);
42676               if (!isNaN(c)) {
42677                 numericChars += c;
42678                 if (this.dialCodeMapping[numericChars]) {
42679                   dialCode = v.substr(1, i);
42680                 }
42681                 if (numericChars.length == 4) {
42682                   break;
42683                 }
42684               }
42685             }
42686             return dialCode;
42687         },
42688         
42689         reset : function()
42690         {
42691             this.setValue(this.defaultDialCode);
42692             this.validate();
42693         },
42694         
42695         hiddenEl : function()
42696         {
42697             return this.el.select('input.hidden-tel-input',true).first();
42698         },
42699         
42700         // after setting val
42701         onKeyUp : function(e){
42702             this.setValue(this.getValue());
42703         },
42704         
42705         onKeyPress : function(e){
42706             if(this.allowed.indexOf(String.fromCharCode(e.getCharCode())) === -1){
42707                 e.stopEvent();
42708             }
42709         }
42710         
42711 });
42712 /**
42713  * @class Roo.bootstrap.MoneyField
42714  * @extends Roo.bootstrap.ComboBox
42715  * Bootstrap MoneyField class
42716  * 
42717  * @constructor
42718  * Create a new MoneyField.
42719  * @param {Object} config Configuration options
42720  */
42721
42722 Roo.bootstrap.MoneyField = function(config) {
42723     
42724     Roo.bootstrap.MoneyField.superclass.constructor.call(this, config);
42725     
42726 };
42727
42728 Roo.extend(Roo.bootstrap.MoneyField, Roo.bootstrap.ComboBox, {
42729     
42730     /**
42731      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
42732      */
42733     allowDecimals : true,
42734     /**
42735      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
42736      */
42737     decimalSeparator : ".",
42738     /**
42739      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
42740      */
42741     decimalPrecision : 0,
42742     /**
42743      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
42744      */
42745     allowNegative : true,
42746     /**
42747      * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
42748      */
42749     allowZero: true,
42750     /**
42751      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
42752      */
42753     minValue : Number.NEGATIVE_INFINITY,
42754     /**
42755      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
42756      */
42757     maxValue : Number.MAX_VALUE,
42758     /**
42759      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
42760      */
42761     minText : "The minimum value for this field is {0}",
42762     /**
42763      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
42764      */
42765     maxText : "The maximum value for this field is {0}",
42766     /**
42767      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
42768      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
42769      */
42770     nanText : "{0} is not a valid number",
42771     /**
42772      * @cfg {Boolean} castInt (true|false) cast int if true (defalut true)
42773      */
42774     castInt : true,
42775     /**
42776      * @cfg {String} defaults currency of the MoneyField
42777      * value should be in lkey
42778      */
42779     defaultCurrency : false,
42780     /**
42781      * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
42782      */
42783     thousandsDelimiter : false,
42784     /**
42785      * @cfg {Number} max_length Maximum input field length allowed (defaults to Number.MAX_VALUE)
42786      */
42787     max_length: false,
42788     
42789     inputlg : 9,
42790     inputmd : 9,
42791     inputsm : 9,
42792     inputxs : 6,
42793     
42794     store : false,
42795     
42796     getAutoCreate : function()
42797     {
42798         var align = this.labelAlign || this.parentLabelAlign();
42799         
42800         var id = Roo.id();
42801
42802         var cfg = {
42803             cls: 'form-group',
42804             cn: []
42805         };
42806
42807         var input =  {
42808             tag: 'input',
42809             id : id,
42810             cls : 'form-control roo-money-amount-input',
42811             autocomplete: 'new-password'
42812         };
42813         
42814         var hiddenInput = {
42815             tag: 'input',
42816             type: 'hidden',
42817             id: Roo.id(),
42818             cls: 'hidden-number-input'
42819         };
42820         
42821         if(this.max_length) {
42822             input.maxlength = this.max_length; 
42823         }
42824         
42825         if (this.name) {
42826             hiddenInput.name = this.name;
42827         }
42828
42829         if (this.disabled) {
42830             input.disabled = true;
42831         }
42832
42833         var clg = 12 - this.inputlg;
42834         var cmd = 12 - this.inputmd;
42835         var csm = 12 - this.inputsm;
42836         var cxs = 12 - this.inputxs;
42837         
42838         var container = {
42839             tag : 'div',
42840             cls : 'row roo-money-field',
42841             cn : [
42842                 {
42843                     tag : 'div',
42844                     cls : 'roo-money-currency column col-lg-' + clg + ' col-md-' + cmd + ' col-sm-' + csm + ' col-xs-' + cxs,
42845                     cn : [
42846                         {
42847                             tag : 'div',
42848                             cls: 'roo-select2-container input-group',
42849                             cn: [
42850                                 {
42851                                     tag : 'input',
42852                                     cls : 'form-control roo-money-currency-input',
42853                                     autocomplete: 'new-password',
42854                                     readOnly : 1,
42855                                     name : this.currencyName
42856                                 },
42857                                 {
42858                                     tag :'span',
42859                                     cls : 'input-group-addon',
42860                                     cn : [
42861                                         {
42862                                             tag: 'span',
42863                                             cls: 'caret'
42864                                         }
42865                                     ]
42866                                 }
42867                             ]
42868                         }
42869                     ]
42870                 },
42871                 {
42872                     tag : 'div',
42873                     cls : 'roo-money-amount column col-lg-' + this.inputlg + ' col-md-' + this.inputmd + ' col-sm-' + this.inputsm + ' col-xs-' + this.inputxs,
42874                     cn : [
42875                         {
42876                             tag: 'div',
42877                             cls: this.hasFeedback ? 'has-feedback' : '',
42878                             cn: [
42879                                 input
42880                             ]
42881                         }
42882                     ]
42883                 }
42884             ]
42885             
42886         };
42887         
42888         if (this.fieldLabel.length) {
42889             var indicator = {
42890                 tag: 'i',
42891                 tooltip: 'This field is required'
42892             };
42893
42894             var label = {
42895                 tag: 'label',
42896                 'for':  id,
42897                 cls: 'control-label',
42898                 cn: []
42899             };
42900
42901             var label_text = {
42902                 tag: 'span',
42903                 html: this.fieldLabel
42904             };
42905
42906             indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
42907             label.cn = [
42908                 indicator,
42909                 label_text
42910             ];
42911
42912             if(this.indicatorpos == 'right') {
42913                 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
42914                 label.cn = [
42915                     label_text,
42916                     indicator
42917                 ];
42918             }
42919
42920             if(align == 'left') {
42921                 container = {
42922                     tag: 'div',
42923                     cn: [
42924                         container
42925                     ]
42926                 };
42927
42928                 if(this.labelWidth > 12){
42929                     label.style = "width: " + this.labelWidth + 'px';
42930                 }
42931                 if(this.labelWidth < 13 && this.labelmd == 0){
42932                     this.labelmd = this.labelWidth;
42933                 }
42934                 if(this.labellg > 0){
42935                     label.cls += ' col-lg-' + this.labellg;
42936                     input.cls += ' col-lg-' + (12 - this.labellg);
42937                 }
42938                 if(this.labelmd > 0){
42939                     label.cls += ' col-md-' + this.labelmd;
42940                     container.cls += ' col-md-' + (12 - this.labelmd);
42941                 }
42942                 if(this.labelsm > 0){
42943                     label.cls += ' col-sm-' + this.labelsm;
42944                     container.cls += ' col-sm-' + (12 - this.labelsm);
42945                 }
42946                 if(this.labelxs > 0){
42947                     label.cls += ' col-xs-' + this.labelxs;
42948                     container.cls += ' col-xs-' + (12 - this.labelxs);
42949                 }
42950             }
42951         }
42952
42953         cfg.cn = [
42954             label,
42955             container,
42956             hiddenInput
42957         ];
42958         
42959         var settings = this;
42960
42961         ['xs','sm','md','lg'].map(function(size){
42962             if (settings[size]) {
42963                 cfg.cls += ' col-' + size + '-' + settings[size];
42964             }
42965         });
42966         
42967         return cfg;
42968     },
42969     
42970     initEvents : function()
42971     {
42972         this.indicator = this.indicatorEl();
42973         
42974         this.initCurrencyEvent();
42975         
42976         this.initNumberEvent();
42977     },
42978     
42979     initCurrencyEvent : function()
42980     {
42981         if (!this.store) {
42982             throw "can not find store for combo";
42983         }
42984         
42985         this.store = Roo.factory(this.store, Roo.data);
42986         this.store.parent = this;
42987         
42988         this.createList();
42989         
42990         this.triggerEl = this.el.select('.input-group-addon', true).first();
42991         
42992         this.triggerEl.on("click", this.onTriggerClick, this, { preventDefault : true });
42993         
42994         var _this = this;
42995         
42996         (function(){
42997             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
42998             _this.list.setWidth(lw);
42999         }).defer(100);
43000         
43001         this.list.on('mouseover', this.onViewOver, this);
43002         this.list.on('mousemove', this.onViewMove, this);
43003         this.list.on('scroll', this.onViewScroll, this);
43004         
43005         if(!this.tpl){
43006             this.tpl = '<li><a href="#">{' + this.currencyField + '}</a></li>';
43007         }
43008         
43009         this.view = new Roo.View(this.list, this.tpl, {
43010             singleSelect:true, store: this.store, selectedClass: this.selectedClass
43011         });
43012         
43013         this.view.on('click', this.onViewClick, this);
43014         
43015         this.store.on('beforeload', this.onBeforeLoad, this);
43016         this.store.on('load', this.onLoad, this);
43017         this.store.on('loadexception', this.onLoadException, this);
43018         
43019         this.keyNav = new Roo.KeyNav(this.currencyEl(), {
43020             "up" : function(e){
43021                 this.inKeyMode = true;
43022                 this.selectPrev();
43023             },
43024
43025             "down" : function(e){
43026                 if(!this.isExpanded()){
43027                     this.onTriggerClick();
43028                 }else{
43029                     this.inKeyMode = true;
43030                     this.selectNext();
43031                 }
43032             },
43033
43034             "enter" : function(e){
43035                 this.collapse();
43036                 
43037                 if(this.fireEvent("specialkey", this, e)){
43038                     this.onViewClick(false);
43039                 }
43040                 
43041                 return true;
43042             },
43043
43044             "esc" : function(e){
43045                 this.collapse();
43046             },
43047
43048             "tab" : function(e){
43049                 this.collapse();
43050                 
43051                 if(this.fireEvent("specialkey", this, e)){
43052                     this.onViewClick(false);
43053                 }
43054                 
43055                 return true;
43056             },
43057
43058             scope : this,
43059
43060             doRelay : function(foo, bar, hname){
43061                 if(hname == 'down' || this.scope.isExpanded()){
43062                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
43063                 }
43064                 return true;
43065             },
43066
43067             forceKeyDown: true
43068         });
43069         
43070         this.currencyEl().on("click", this.onTriggerClick, this, { preventDefault : true });
43071         
43072     },
43073     
43074     initNumberEvent : function(e)
43075     {
43076         this.inputEl().on("keydown" , this.fireKey,  this);
43077         this.inputEl().on("focus", this.onFocus,  this);
43078         this.inputEl().on("blur", this.onBlur,  this);
43079         
43080         this.inputEl().relayEvent('keyup', this);
43081         
43082         if(this.indicator){
43083             this.indicator.addClass('invisible');
43084         }
43085  
43086         this.originalValue = this.getValue();
43087         
43088         if(this.validationEvent == 'keyup'){
43089             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
43090             this.inputEl().on('keyup', this.filterValidation, this);
43091         }
43092         else if(this.validationEvent !== false){
43093             this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
43094         }
43095         
43096         if(this.selectOnFocus){
43097             this.on("focus", this.preFocus, this);
43098             
43099         }
43100         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
43101             this.inputEl().on("keypress", this.filterKeys, this);
43102         } else {
43103             this.inputEl().relayEvent('keypress', this);
43104         }
43105         
43106         var allowed = "0123456789";
43107         
43108         if(this.allowDecimals){
43109             allowed += this.decimalSeparator;
43110         }
43111         
43112         if(this.allowNegative){
43113             allowed += "-";
43114         }
43115         
43116         if(this.thousandsDelimiter) {
43117             allowed += ",";
43118         }
43119         
43120         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
43121         
43122         var keyPress = function(e){
43123             
43124             var k = e.getKey();
43125             
43126             var c = e.getCharCode();
43127             
43128             if(
43129                     (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
43130                     allowed.indexOf(String.fromCharCode(c)) === -1
43131             ){
43132                 e.stopEvent();
43133                 return;
43134             }
43135             
43136             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
43137                 return;
43138             }
43139             
43140             if(allowed.indexOf(String.fromCharCode(c)) === -1){
43141                 e.stopEvent();
43142             }
43143         };
43144         
43145         this.inputEl().on("keypress", keyPress, this);
43146         
43147     },
43148     
43149     onTriggerClick : function(e)
43150     {   
43151         if(this.disabled){
43152             return;
43153         }
43154         
43155         this.page = 0;
43156         this.loadNext = false;
43157         
43158         if(this.isExpanded()){
43159             this.collapse();
43160             return;
43161         }
43162         
43163         this.hasFocus = true;
43164         
43165         if(this.triggerAction == 'all') {
43166             this.doQuery(this.allQuery, true);
43167             return;
43168         }
43169         
43170         this.doQuery(this.getRawValue());
43171     },
43172     
43173     getCurrency : function()
43174     {   
43175         var v = this.currencyEl().getValue();
43176         
43177         return v;
43178     },
43179     
43180     restrictHeight : function()
43181     {
43182         this.list.alignTo(this.currencyEl(), this.listAlign);
43183         this.list.alignTo(this.currencyEl(), this.listAlign);
43184     },
43185     
43186     onViewClick : function(view, doFocus, el, e)
43187     {
43188         var index = this.view.getSelectedIndexes()[0];
43189         
43190         var r = this.store.getAt(index);
43191         
43192         if(r){
43193             this.onSelect(r, index);
43194         }
43195     },
43196     
43197     onSelect : function(record, index){
43198         
43199         if(this.fireEvent('beforeselect', this, record, index) !== false){
43200         
43201             this.setFromCurrencyData(index > -1 ? record.data : false);
43202             
43203             this.collapse();
43204             
43205             this.fireEvent('select', this, record, index);
43206         }
43207     },
43208     
43209     setFromCurrencyData : function(o)
43210     {
43211         var currency = '';
43212         
43213         this.lastCurrency = o;
43214         
43215         if (this.currencyField) {
43216             currency = !o || typeof(o[this.currencyField]) == 'undefined' ? '' : o[this.currencyField];
43217         } else {
43218             Roo.log('no  currencyField value set for '+ (this.name ? this.name : this.id));
43219         }
43220         
43221         this.lastSelectionText = currency;
43222         
43223         //setting default currency
43224         if(o[this.currencyField] * 1 == 0 && this.defaultCurrency) {
43225             this.setCurrency(this.defaultCurrency);
43226             return;
43227         }
43228         
43229         this.setCurrency(currency);
43230     },
43231     
43232     setFromData : function(o)
43233     {
43234         var c = {};
43235         
43236         c[this.currencyField] = !o || typeof(o[this.currencyName]) == 'undefined' ? '' : o[this.currencyName];
43237         
43238         this.setFromCurrencyData(c);
43239         
43240         var value = '';
43241         
43242         if (this.name) {
43243             value = !o || typeof(o[this.name]) == 'undefined' ? '' : o[this.name];
43244         } else {
43245             Roo.log('no value set for '+ (this.name ? this.name : this.id));
43246         }
43247         
43248         this.setValue(value);
43249         
43250     },
43251     
43252     setCurrency : function(v)
43253     {   
43254         this.currencyValue = v;
43255         
43256         if(this.rendered){
43257             this.currencyEl().dom.value = (v === null || v === undefined ? '' : v);
43258             this.validate();
43259         }
43260     },
43261     
43262     setValue : function(v)
43263     {
43264         v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
43265         
43266         this.value = v;
43267         
43268         if(this.rendered){
43269             
43270             this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
43271             
43272             this.inputEl().dom.value = (v == '') ? '' :
43273                 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
43274             
43275             if(!this.allowZero && v === '0') {
43276                 this.hiddenEl().dom.value = '';
43277                 this.inputEl().dom.value = '';
43278             }
43279             
43280             this.validate();
43281         }
43282     },
43283     
43284     getRawValue : function()
43285     {
43286         var v = this.inputEl().getValue();
43287         
43288         return v;
43289     },
43290     
43291     getValue : function()
43292     {
43293         return this.fixPrecision(this.parseValue(this.getRawValue()));
43294     },
43295     
43296     parseValue : function(value)
43297     {
43298         if(this.thousandsDelimiter) {
43299             value += "";
43300             r = new RegExp(",", "g");
43301             value = value.replace(r, "");
43302         }
43303         
43304         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
43305         return isNaN(value) ? '' : value;
43306         
43307     },
43308     
43309     fixPrecision : function(value)
43310     {
43311         if(this.thousandsDelimiter) {
43312             value += "";
43313             r = new RegExp(",", "g");
43314             value = value.replace(r, "");
43315         }
43316         
43317         var nan = isNaN(value);
43318         
43319         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
43320             return nan ? '' : value;
43321         }
43322         return parseFloat(value).toFixed(this.decimalPrecision);
43323     },
43324     
43325     decimalPrecisionFcn : function(v)
43326     {
43327         return Math.floor(v);
43328     },
43329     
43330     validateValue : function(value)
43331     {
43332         if(!Roo.bootstrap.MoneyField.superclass.validateValue.call(this, value)){
43333             return false;
43334         }
43335         
43336         var num = this.parseValue(value);
43337         
43338         if(isNaN(num)){
43339             this.markInvalid(String.format(this.nanText, value));
43340             return false;
43341         }
43342         
43343         if(num < this.minValue){
43344             this.markInvalid(String.format(this.minText, this.minValue));
43345             return false;
43346         }
43347         
43348         if(num > this.maxValue){
43349             this.markInvalid(String.format(this.maxText, this.maxValue));
43350             return false;
43351         }
43352         
43353         return true;
43354     },
43355     
43356     validate : function()
43357     {
43358         if(this.disabled || this.allowBlank){
43359             this.markValid();
43360             return true;
43361         }
43362         
43363         var currency = this.getCurrency();
43364         
43365         if(this.validateValue(this.getRawValue()) && currency.length){
43366             this.markValid();
43367             return true;
43368         }
43369         
43370         this.markInvalid();
43371         return false;
43372     },
43373     
43374     getName: function()
43375     {
43376         return this.name;
43377     },
43378     
43379     beforeBlur : function()
43380     {
43381         if(!this.castInt){
43382             return;
43383         }
43384         
43385         var v = this.parseValue(this.getRawValue());
43386         
43387         if(v || v == 0){
43388             this.setValue(v);
43389         }
43390     },
43391     
43392     onBlur : function()
43393     {
43394         this.beforeBlur();
43395         
43396         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
43397             //this.el.removeClass(this.focusClass);
43398         }
43399         
43400         this.hasFocus = false;
43401         
43402         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
43403             this.validate();
43404         }
43405         
43406         var v = this.getValue();
43407         
43408         if(String(v) !== String(this.startValue)){
43409             this.fireEvent('change', this, v, this.startValue);
43410         }
43411         
43412         this.fireEvent("blur", this);
43413     },
43414     
43415     inputEl : function()
43416     {
43417         return this.el.select('.roo-money-amount-input', true).first();
43418     },
43419     
43420     currencyEl : function()
43421     {
43422         return this.el.select('.roo-money-currency-input', true).first();
43423     },
43424     
43425     hiddenEl : function()
43426     {
43427         return this.el.select('input.hidden-number-input',true).first();
43428     }
43429     
43430 });/**
43431  * @class Roo.bootstrap.BezierSignature
43432  * @extends Roo.bootstrap.Component
43433  * Bootstrap BezierSignature class
43434  * This script refer to:
43435  *    Title: Signature Pad
43436  *    Author: szimek
43437  *    Availability: https://github.com/szimek/signature_pad
43438  *
43439  * @constructor
43440  * Create a new BezierSignature
43441  * @param {Object} config The config object
43442  */
43443
43444 Roo.bootstrap.BezierSignature = function(config){
43445     Roo.bootstrap.BezierSignature.superclass.constructor.call(this, config);
43446     this.addEvents({
43447         "resize" : true
43448     });
43449 };
43450
43451 Roo.extend(Roo.bootstrap.BezierSignature, Roo.bootstrap.Component,
43452 {
43453      
43454     curve_data: [],
43455     
43456     is_empty: true,
43457     
43458     mouse_btn_down: true,
43459     
43460     /**
43461      * @cfg {int} canvas height
43462      */
43463     canvas_height: '200px',
43464     
43465     /**
43466      * @cfg {float|function} Radius of a single dot.
43467      */ 
43468     dot_size: false,
43469     
43470     /**
43471      * @cfg {float} Minimum width of a line. Defaults to 0.5.
43472      */
43473     min_width: 0.5,
43474     
43475     /**
43476      * @cfg {float} Maximum width of a line. Defaults to 2.5.
43477      */
43478     max_width: 2.5,
43479     
43480     /**
43481      * @cfg {integer} Draw the next point at most once per every x milliseconds. Set it to 0 to turn off throttling. Defaults to 16.
43482      */
43483     throttle: 16,
43484     
43485     /**
43486      * @cfg {integer} Add the next point only if the previous one is farther than x pixels. Defaults to 5.
43487      */
43488     min_distance: 5,
43489     
43490     /**
43491      * @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.
43492      */
43493     bg_color: 'rgba(0, 0, 0, 0)',
43494     
43495     /**
43496      * @cfg {string} Color used to draw the lines. Can be any color format accepted by context.fillStyle. Defaults to "black".
43497      */
43498     dot_color: 'black',
43499     
43500     /**
43501      * @cfg {float} Weight used to modify new velocity based on the previous velocity. Defaults to 0.7.
43502      */ 
43503     velocity_filter_weight: 0.7,
43504     
43505     /**
43506      * @cfg {function} Callback when stroke begin. 
43507      */
43508     onBegin: false,
43509     
43510     /**
43511      * @cfg {function} Callback when stroke end.
43512      */
43513     onEnd: false,
43514     
43515     getAutoCreate : function()
43516     {
43517         var cls = 'roo-signature column';
43518         
43519         if(this.cls){
43520             cls += ' ' + this.cls;
43521         }
43522         
43523         var col_sizes = [
43524             'lg',
43525             'md',
43526             'sm',
43527             'xs'
43528         ];
43529         
43530         for(var i = 0; i < col_sizes.length; i++) {
43531             if(this[col_sizes[i]]) {
43532                 cls += " col-"+col_sizes[i]+"-"+this[col_sizes[i]];
43533             }
43534         }
43535         
43536         var cfg = {
43537             tag: 'div',
43538             cls: cls,
43539             cn: [
43540                 {
43541                     tag: 'div',
43542                     cls: 'roo-signature-body',
43543                     cn: [
43544                         {
43545                             tag: 'canvas',
43546                             cls: 'roo-signature-body-canvas',
43547                             height: this.canvas_height,
43548                             width: this.canvas_width
43549                         }
43550                     ]
43551                 },
43552                 {
43553                     tag: 'input',
43554                     type: 'file',
43555                     style: 'display: none'
43556                 }
43557             ]
43558         };
43559         
43560         return cfg;
43561     },
43562     
43563     initEvents: function() 
43564     {
43565         Roo.bootstrap.BezierSignature.superclass.initEvents.call(this);
43566         
43567         var canvas = this.canvasEl();
43568         
43569         // mouse && touch event swapping...
43570         canvas.dom.style.touchAction = 'none';
43571         canvas.dom.style.msTouchAction = 'none';
43572         
43573         this.mouse_btn_down = false;
43574         canvas.on('mousedown', this._handleMouseDown, this);
43575         canvas.on('mousemove', this._handleMouseMove, this);
43576         Roo.select('html').first().on('mouseup', this._handleMouseUp, this);
43577         
43578         if (window.PointerEvent) {
43579             canvas.on('pointerdown', this._handleMouseDown, this);
43580             canvas.on('pointermove', this._handleMouseMove, this);
43581             Roo.select('html').first().on('pointerup', this._handleMouseUp, this);
43582         }
43583         
43584         if ('ontouchstart' in window) {
43585             canvas.on('touchstart', this._handleTouchStart, this);
43586             canvas.on('touchmove', this._handleTouchMove, this);
43587             canvas.on('touchend', this._handleTouchEnd, this);
43588         }
43589         
43590         Roo.EventManager.onWindowResize(this.resize, this, true);
43591         
43592         // file input event
43593         this.fileEl().on('change', this.uploadImage, this);
43594         
43595         this.clear();
43596         
43597         this.resize();
43598     },
43599     
43600     resize: function(){
43601         
43602         var canvas = this.canvasEl().dom;
43603         var ctx = this.canvasElCtx();
43604         var img_data = false;
43605         
43606         if(canvas.width > 0) {
43607             var img_data = ctx.getImageData(0, 0, canvas.width, canvas.height);
43608         }
43609         // setting canvas width will clean img data
43610         canvas.width = 0;
43611         
43612         var style = window.getComputedStyle ? 
43613             getComputedStyle(this.el.dom, null) : this.el.dom.currentStyle;
43614             
43615         var padding_left = parseInt(style.paddingLeft) || 0;
43616         var padding_right = parseInt(style.paddingRight) || 0;
43617         
43618         canvas.width = this.el.dom.clientWidth - padding_left - padding_right;
43619         
43620         if(img_data) {
43621             ctx.putImageData(img_data, 0, 0);
43622         }
43623     },
43624     
43625     _handleMouseDown: function(e)
43626     {
43627         if (e.browserEvent.which === 1) {
43628             this.mouse_btn_down = true;
43629             this.strokeBegin(e);
43630         }
43631     },
43632     
43633     _handleMouseMove: function (e)
43634     {
43635         if (this.mouse_btn_down) {
43636             this.strokeMoveUpdate(e);
43637         }
43638     },
43639     
43640     _handleMouseUp: function (e)
43641     {
43642         if (e.browserEvent.which === 1 && this.mouse_btn_down) {
43643             this.mouse_btn_down = false;
43644             this.strokeEnd(e);
43645         }
43646     },
43647     
43648     _handleTouchStart: function (e) {
43649         
43650         e.preventDefault();
43651         if (e.browserEvent.targetTouches.length === 1) {
43652             // var touch = e.browserEvent.changedTouches[0];
43653             // this.strokeBegin(touch);
43654             
43655              this.strokeBegin(e); // assume e catching the correct xy...
43656         }
43657     },
43658     
43659     _handleTouchMove: function (e) {
43660         e.preventDefault();
43661         // var touch = event.targetTouches[0];
43662         // _this._strokeMoveUpdate(touch);
43663         this.strokeMoveUpdate(e);
43664     },
43665     
43666     _handleTouchEnd: function (e) {
43667         var wasCanvasTouched = e.target === this.canvasEl().dom;
43668         if (wasCanvasTouched) {
43669             e.preventDefault();
43670             // var touch = event.changedTouches[0];
43671             // _this._strokeEnd(touch);
43672             this.strokeEnd(e);
43673         }
43674     },
43675     
43676     reset: function () {
43677         this._lastPoints = [];
43678         this._lastVelocity = 0;
43679         this._lastWidth = (this.min_width + this.max_width) / 2;
43680         this.canvasElCtx().fillStyle = this.dot_color;
43681     },
43682     
43683     strokeMoveUpdate: function(e)
43684     {
43685         this.strokeUpdate(e);
43686         
43687         if (this.throttle) {
43688             this.throttleStroke(this.strokeUpdate, this.throttle);
43689         }
43690         else {
43691             this.strokeUpdate(e);
43692         }
43693     },
43694     
43695     strokeBegin: function(e)
43696     {
43697         var newPointGroup = {
43698             color: this.dot_color,
43699             points: []
43700         };
43701         
43702         if (typeof this.onBegin === 'function') {
43703             this.onBegin(e);
43704         }
43705         
43706         this.curve_data.push(newPointGroup);
43707         this.reset();
43708         this.strokeUpdate(e);
43709     },
43710     
43711     strokeUpdate: function(e)
43712     {
43713         var rect = this.canvasEl().dom.getBoundingClientRect();
43714         var point = new this.Point(e.xy[0] - rect.left, e.xy[1] - rect.top, new Date().getTime());
43715         var lastPointGroup = this.curve_data[this.curve_data.length - 1];
43716         var lastPoints = lastPointGroup.points;
43717         var lastPoint = lastPoints.length > 0 && lastPoints[lastPoints.length - 1];
43718         var isLastPointTooClose = lastPoint
43719             ? point.distanceTo(lastPoint) <= this.min_distance
43720             : false;
43721         var color = lastPointGroup.color;
43722         if (!lastPoint || !(lastPoint && isLastPointTooClose)) {
43723             var curve = this.addPoint(point);
43724             if (!lastPoint) {
43725                 this.drawDot({color: color, point: point});
43726             }
43727             else if (curve) {
43728                 this.drawCurve({color: color, curve: curve});
43729             }
43730             lastPoints.push({
43731                 time: point.time,
43732                 x: point.x,
43733                 y: point.y
43734             });
43735         }
43736     },
43737     
43738     strokeEnd: function(e)
43739     {
43740         this.strokeUpdate(e);
43741         if (typeof this.onEnd === 'function') {
43742             this.onEnd(e);
43743         }
43744     },
43745     
43746     addPoint:  function (point) {
43747         var _lastPoints = this._lastPoints;
43748         _lastPoints.push(point);
43749         if (_lastPoints.length > 2) {
43750             if (_lastPoints.length === 3) {
43751                 _lastPoints.unshift(_lastPoints[0]);
43752             }
43753             var widths = this.calculateCurveWidths(_lastPoints[1], _lastPoints[2]);
43754             var curve = this.Bezier.fromPoints(_lastPoints, widths, this);
43755             _lastPoints.shift();
43756             return curve;
43757         }
43758         return null;
43759     },
43760     
43761     calculateCurveWidths: function (startPoint, endPoint) {
43762         var velocity = this.velocity_filter_weight * endPoint.velocityFrom(startPoint) +
43763             (1 - this.velocity_filter_weight) * this._lastVelocity;
43764
43765         var newWidth = Math.max(this.max_width / (velocity + 1), this.min_width);
43766         var widths = {
43767             end: newWidth,
43768             start: this._lastWidth
43769         };
43770         
43771         this._lastVelocity = velocity;
43772         this._lastWidth = newWidth;
43773         return widths;
43774     },
43775     
43776     drawDot: function (_a) {
43777         var color = _a.color, point = _a.point;
43778         var ctx = this.canvasElCtx();
43779         var width = typeof this.dot_size === 'function' ? this.dot_size() : this.dot_size;
43780         ctx.beginPath();
43781         this.drawCurveSegment(point.x, point.y, width);
43782         ctx.closePath();
43783         ctx.fillStyle = color;
43784         ctx.fill();
43785     },
43786     
43787     drawCurve: function (_a) {
43788         var color = _a.color, curve = _a.curve;
43789         var ctx = this.canvasElCtx();
43790         var widthDelta = curve.endWidth - curve.startWidth;
43791         var drawSteps = Math.floor(curve.length()) * 2;
43792         ctx.beginPath();
43793         ctx.fillStyle = color;
43794         for (var i = 0; i < drawSteps; i += 1) {
43795         var t = i / drawSteps;
43796         var tt = t * t;
43797         var ttt = tt * t;
43798         var u = 1 - t;
43799         var uu = u * u;
43800         var uuu = uu * u;
43801         var x = uuu * curve.startPoint.x;
43802         x += 3 * uu * t * curve.control1.x;
43803         x += 3 * u * tt * curve.control2.x;
43804         x += ttt * curve.endPoint.x;
43805         var y = uuu * curve.startPoint.y;
43806         y += 3 * uu * t * curve.control1.y;
43807         y += 3 * u * tt * curve.control2.y;
43808         y += ttt * curve.endPoint.y;
43809         var width = curve.startWidth + ttt * widthDelta;
43810         this.drawCurveSegment(x, y, width);
43811         }
43812         ctx.closePath();
43813         ctx.fill();
43814     },
43815     
43816     drawCurveSegment: function (x, y, width) {
43817         var ctx = this.canvasElCtx();
43818         ctx.moveTo(x, y);
43819         ctx.arc(x, y, width, 0, 2 * Math.PI, false);
43820         this.is_empty = false;
43821     },
43822     
43823     clear: function()
43824     {
43825         var ctx = this.canvasElCtx();
43826         var canvas = this.canvasEl().dom;
43827         ctx.fillStyle = this.bg_color;
43828         ctx.clearRect(0, 0, canvas.width, canvas.height);
43829         ctx.fillRect(0, 0, canvas.width, canvas.height);
43830         this.curve_data = [];
43831         this.reset();
43832         this.is_empty = true;
43833     },
43834     
43835     fileEl: function()
43836     {
43837         return  this.el.select('input',true).first();
43838     },
43839     
43840     canvasEl: function()
43841     {
43842         return this.el.select('canvas',true).first();
43843     },
43844     
43845     canvasElCtx: function()
43846     {
43847         return this.el.select('canvas',true).first().dom.getContext('2d');
43848     },
43849     
43850     getImage: function(type)
43851     {
43852         if(this.is_empty) {
43853             return false;
43854         }
43855         
43856         // encryption ?
43857         return this.canvasEl().dom.toDataURL('image/'+type, 1);
43858     },
43859     
43860     drawFromImage: function(img_src)
43861     {
43862         var img = new Image();
43863         
43864         img.onload = function(){
43865             this.canvasElCtx().drawImage(img, 0, 0);
43866         }.bind(this);
43867         
43868         img.src = img_src;
43869         
43870         this.is_empty = false;
43871     },
43872     
43873     selectImage: function()
43874     {
43875         this.fileEl().dom.click();
43876     },
43877     
43878     uploadImage: function(e)
43879     {
43880         var reader = new FileReader();
43881         
43882         reader.onload = function(e){
43883             var img = new Image();
43884             img.onload = function(){
43885                 this.reset();
43886                 this.canvasElCtx().drawImage(img, 0, 0);
43887             }.bind(this);
43888             img.src = e.target.result;
43889         }.bind(this);
43890         
43891         reader.readAsDataURL(e.target.files[0]);
43892     },
43893     
43894     // Bezier Point Constructor
43895     Point: (function () {
43896         function Point(x, y, time) {
43897             this.x = x;
43898             this.y = y;
43899             this.time = time || Date.now();
43900         }
43901         Point.prototype.distanceTo = function (start) {
43902             return Math.sqrt(Math.pow(this.x - start.x, 2) + Math.pow(this.y - start.y, 2));
43903         };
43904         Point.prototype.equals = function (other) {
43905             return this.x === other.x && this.y === other.y && this.time === other.time;
43906         };
43907         Point.prototype.velocityFrom = function (start) {
43908             return this.time !== start.time
43909             ? this.distanceTo(start) / (this.time - start.time)
43910             : 0;
43911         };
43912         return Point;
43913     }()),
43914     
43915     
43916     // Bezier Constructor
43917     Bezier: (function () {
43918         function Bezier(startPoint, control2, control1, endPoint, startWidth, endWidth) {
43919             this.startPoint = startPoint;
43920             this.control2 = control2;
43921             this.control1 = control1;
43922             this.endPoint = endPoint;
43923             this.startWidth = startWidth;
43924             this.endWidth = endWidth;
43925         }
43926         Bezier.fromPoints = function (points, widths, scope) {
43927             var c2 = this.calculateControlPoints(points[0], points[1], points[2], scope).c2;
43928             var c3 = this.calculateControlPoints(points[1], points[2], points[3], scope).c1;
43929             return new Bezier(points[1], c2, c3, points[2], widths.start, widths.end);
43930         };
43931         Bezier.calculateControlPoints = function (s1, s2, s3, scope) {
43932             var dx1 = s1.x - s2.x;
43933             var dy1 = s1.y - s2.y;
43934             var dx2 = s2.x - s3.x;
43935             var dy2 = s2.y - s3.y;
43936             var m1 = { x: (s1.x + s2.x) / 2.0, y: (s1.y + s2.y) / 2.0 };
43937             var m2 = { x: (s2.x + s3.x) / 2.0, y: (s2.y + s3.y) / 2.0 };
43938             var l1 = Math.sqrt(dx1 * dx1 + dy1 * dy1);
43939             var l2 = Math.sqrt(dx2 * dx2 + dy2 * dy2);
43940             var dxm = m1.x - m2.x;
43941             var dym = m1.y - m2.y;
43942             var k = l2 / (l1 + l2);
43943             var cm = { x: m2.x + dxm * k, y: m2.y + dym * k };
43944             var tx = s2.x - cm.x;
43945             var ty = s2.y - cm.y;
43946             return {
43947                 c1: new scope.Point(m1.x + tx, m1.y + ty),
43948                 c2: new scope.Point(m2.x + tx, m2.y + ty)
43949             };
43950         };
43951         Bezier.prototype.length = function () {
43952             var steps = 10;
43953             var length = 0;
43954             var px;
43955             var py;
43956             for (var i = 0; i <= steps; i += 1) {
43957                 var t = i / steps;
43958                 var cx = this.point(t, this.startPoint.x, this.control1.x, this.control2.x, this.endPoint.x);
43959                 var cy = this.point(t, this.startPoint.y, this.control1.y, this.control2.y, this.endPoint.y);
43960                 if (i > 0) {
43961                     var xdiff = cx - px;
43962                     var ydiff = cy - py;
43963                     length += Math.sqrt(xdiff * xdiff + ydiff * ydiff);
43964                 }
43965                 px = cx;
43966                 py = cy;
43967             }
43968             return length;
43969         };
43970         Bezier.prototype.point = function (t, start, c1, c2, end) {
43971             return (start * (1.0 - t) * (1.0 - t) * (1.0 - t))
43972             + (3.0 * c1 * (1.0 - t) * (1.0 - t) * t)
43973             + (3.0 * c2 * (1.0 - t) * t * t)
43974             + (end * t * t * t);
43975         };
43976         return Bezier;
43977     }()),
43978     
43979     throttleStroke: function(fn, wait) {
43980       if (wait === void 0) { wait = 250; }
43981       var previous = 0;
43982       var timeout = null;
43983       var result;
43984       var storedContext;
43985       var storedArgs;
43986       var later = function () {
43987           previous = Date.now();
43988           timeout = null;
43989           result = fn.apply(storedContext, storedArgs);
43990           if (!timeout) {
43991               storedContext = null;
43992               storedArgs = [];
43993           }
43994       };
43995       return function wrapper() {
43996           var args = [];
43997           for (var _i = 0; _i < arguments.length; _i++) {
43998               args[_i] = arguments[_i];
43999           }
44000           var now = Date.now();
44001           var remaining = wait - (now - previous);
44002           storedContext = this;
44003           storedArgs = args;
44004           if (remaining <= 0 || remaining > wait) {
44005               if (timeout) {
44006                   clearTimeout(timeout);
44007                   timeout = null;
44008               }
44009               previous = now;
44010               result = fn.apply(storedContext, storedArgs);
44011               if (!timeout) {
44012                   storedContext = null;
44013                   storedArgs = [];
44014               }
44015           }
44016           else if (!timeout) {
44017               timeout = window.setTimeout(later, remaining);
44018           }
44019           return result;
44020       };
44021   }
44022   
44023 });
44024
44025  
44026
44027