roojs-bootstrap.js
[roojs1] / roojs-bootstrap-debug.js
1 /**
2  * set the version of bootstrap based on the stylesheet...
3  *
4  */
5
6 Roo.bootstrap.version = ( function() {
7     var ret=3;
8     Roo.each(document.styleSheets, function(s) {
9         if ( s.href  && s.href.match(/css-bootstrap4/)) {
10             ret=4;
11         }
12     });
13     if (ret > 3) {
14          Roo.Element.prototype.visibilityMode = Roo.Element.DISPLAY;
15     }
16     return ret;
17 })(); /*
18  * Based on:
19  * Ext JS Library 1.1.1
20  * Copyright(c) 2006-2007, Ext JS, LLC.
21  *
22  * Originally Released Under LGPL - original licence link has changed is not relivant.
23  *
24  * Fork - LGPL
25  * <script type="text/javascript">
26  */
27
28
29 /**
30  * @class Roo.Shadow
31  * Simple class that can provide a shadow effect for any element.  Note that the element MUST be absolutely positioned,
32  * and the shadow does not provide any shimming.  This should be used only in simple cases -- for more advanced
33  * functionality that can also provide the same shadow effect, see the {@link Roo.Layer} class.
34  * @constructor
35  * Create a new Shadow
36  * @param {Object} config The config object
37  */
38 Roo.Shadow = function(config){
39     Roo.apply(this, config);
40     if(typeof this.mode != "string"){
41         this.mode = this.defaultMode;
42     }
43     var o = this.offset, a = {h: 0};
44     var rad = Math.floor(this.offset/2);
45     switch(this.mode.toLowerCase()){ // all this hideous nonsense calculates the various offsets for shadows
46         case "drop":
47             a.w = 0;
48             a.l = a.t = o;
49             a.t -= 1;
50             if(Roo.isIE){
51                 a.l -= this.offset + rad;
52                 a.t -= this.offset + rad;
53                 a.w -= rad;
54                 a.h -= rad;
55                 a.t += 1;
56             }
57         break;
58         case "sides":
59             a.w = (o*2);
60             a.l = -o;
61             a.t = o-1;
62             if(Roo.isIE){
63                 a.l -= (this.offset - rad);
64                 a.t -= this.offset + rad;
65                 a.l += 1;
66                 a.w -= (this.offset - rad)*2;
67                 a.w -= rad + 1;
68                 a.h -= 1;
69             }
70         break;
71         case "frame":
72             a.w = a.h = (o*2);
73             a.l = a.t = -o;
74             a.t += 1;
75             a.h -= 2;
76             if(Roo.isIE){
77                 a.l -= (this.offset - rad);
78                 a.t -= (this.offset - rad);
79                 a.l += 1;
80                 a.w -= (this.offset + rad + 1);
81                 a.h -= (this.offset + rad);
82                 a.h += 1;
83             }
84         break;
85     };
86
87     this.adjusts = a;
88 };
89
90 Roo.Shadow.prototype = {
91     /**
92      * @cfg {String} mode
93      * The shadow display mode.  Supports the following options:<br />
94      * sides: Shadow displays on both sides and bottom only<br />
95      * frame: Shadow displays equally on all four sides<br />
96      * drop: Traditional bottom-right drop shadow (default)
97      */
98     /**
99      * @cfg {String} offset
100      * The number of pixels to offset the shadow from the element (defaults to 4)
101      */
102     offset: 4,
103
104     // private
105     defaultMode: "drop",
106
107     /**
108      * Displays the shadow under the target element
109      * @param {String/HTMLElement/Element} targetEl The id or element under which the shadow should display
110      */
111     show : function(target){
112         target = Roo.get(target);
113         if(!this.el){
114             this.el = Roo.Shadow.Pool.pull();
115             if(this.el.dom.nextSibling != target.dom){
116                 this.el.insertBefore(target);
117             }
118         }
119         this.el.setStyle("z-index", this.zIndex || parseInt(target.getStyle("z-index"), 10)-1);
120         if(Roo.isIE){
121             this.el.dom.style.filter="progid:DXImageTransform.Microsoft.alpha(opacity=50) progid:DXImageTransform.Microsoft.Blur(pixelradius="+(this.offset)+")";
122         }
123         this.realign(
124             target.getLeft(true),
125             target.getTop(true),
126             target.getWidth(),
127             target.getHeight()
128         );
129         this.el.dom.style.display = "block";
130     },
131
132     /**
133      * Returns true if the shadow is visible, else false
134      */
135     isVisible : function(){
136         return this.el ? true : false;  
137     },
138
139     /**
140      * Direct alignment when values are already available. Show must be called at least once before
141      * calling this method to ensure it is initialized.
142      * @param {Number} left The target element left position
143      * @param {Number} top The target element top position
144      * @param {Number} width The target element width
145      * @param {Number} height The target element height
146      */
147     realign : function(l, t, w, h){
148         if(!this.el){
149             return;
150         }
151         var a = this.adjusts, d = this.el.dom, s = d.style;
152         var iea = 0;
153         s.left = (l+a.l)+"px";
154         s.top = (t+a.t)+"px";
155         var sw = (w+a.w), sh = (h+a.h), sws = sw +"px", shs = sh + "px";
156  
157         if(s.width != sws || s.height != shs){
158             s.width = sws;
159             s.height = shs;
160             if(!Roo.isIE){
161                 var cn = d.childNodes;
162                 var sww = Math.max(0, (sw-12))+"px";
163                 cn[0].childNodes[1].style.width = sww;
164                 cn[1].childNodes[1].style.width = sww;
165                 cn[2].childNodes[1].style.width = sww;
166                 cn[1].style.height = Math.max(0, (sh-12))+"px";
167             }
168         }
169     },
170
171     /**
172      * Hides this shadow
173      */
174     hide : function(){
175         if(this.el){
176             this.el.dom.style.display = "none";
177             Roo.Shadow.Pool.push(this.el);
178             delete this.el;
179         }
180     },
181
182     /**
183      * Adjust the z-index of this shadow
184      * @param {Number} zindex The new z-index
185      */
186     setZIndex : function(z){
187         this.zIndex = z;
188         if(this.el){
189             this.el.setStyle("z-index", z);
190         }
191     }
192 };
193
194 // Private utility class that manages the internal Shadow cache
195 Roo.Shadow.Pool = function(){
196     var p = [];
197     var markup = Roo.isIE ?
198                  '<div class="x-ie-shadow"></div>' :
199                  '<div class="x-shadow"><div class="xst"><div class="xstl"></div><div class="xstc"></div><div class="xstr"></div></div><div class="xsc"><div class="xsml"></div><div class="xsmc"></div><div class="xsmr"></div></div><div class="xsb"><div class="xsbl"></div><div class="xsbc"></div><div class="xsbr"></div></div></div>';
200     return {
201         pull : function(){
202             var sh = p.shift();
203             if(!sh){
204                 sh = Roo.get(Roo.DomHelper.insertHtml("beforeBegin", document.body.firstChild, markup));
205                 sh.autoBoxAdjust = false;
206             }
207             return sh;
208         },
209
210         push : function(sh){
211             p.push(sh);
212         }
213     };
214 }();/*
215  * - LGPL
216  *
217  * base class for bootstrap elements.
218  * 
219  */
220
221 Roo.bootstrap = Roo.bootstrap || {};
222 /**
223  * @class Roo.bootstrap.Component
224  * @extends Roo.Component
225  * Bootstrap Component base class
226  * @cfg {String} cls css class
227  * @cfg {String} style any extra css
228  * @cfg {Object} xattr extra attributes to add to 'element' (used by builder to store stuff.)
229  * @cfg {Boolean} can_build_overlaid  True if element can be rebuild from a HTML page
230  * @cfg {string} dataId cutomer id
231  * @cfg {string} name Specifies name attribute
232  * @cfg {string} tooltip  Text for the tooltip
233  * @cfg {string} container_method method to fetch parents container element (used by NavHeaderbar -  getHeaderChildContainer)
234  * @cfg {string|object} visibilityEl (el|parent) What element to use for visibility (@see getVisibilityEl())
235  
236  * @constructor
237  * Do not use directly - it does not do anything..
238  * @param {Object} config The config object
239  */
240
241
242
243 Roo.bootstrap.Component = function(config){
244     Roo.bootstrap.Component.superclass.constructor.call(this, config);
245        
246     this.addEvents({
247         /**
248          * @event childrenrendered
249          * Fires when the children have been rendered..
250          * @param {Roo.bootstrap.Component} this
251          */
252         "childrenrendered" : true
253         
254         
255         
256     });
257     
258     
259 };
260
261 Roo.extend(Roo.bootstrap.Component, Roo.BoxComponent,  {
262     
263     
264     allowDomMove : false, // to stop relocations in parent onRender...
265     
266     cls : false,
267     
268     style : false,
269     
270     autoCreate : false,
271     
272     tooltip : null,
273     /**
274      * Initialize Events for the element
275      */
276     initEvents : function() { },
277     
278     xattr : false,
279     
280     parentId : false,
281     
282     can_build_overlaid : true,
283     
284     container_method : false,
285     
286     dataId : false,
287     
288     name : false,
289     
290     parent: function() {
291         // returns the parent component..
292         return Roo.ComponentMgr.get(this.parentId)
293         
294         
295     },
296     
297     // private
298     onRender : function(ct, position)
299     {
300        // Roo.log("Call onRender: " + this.xtype);
301         
302         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
303         
304         if(this.el){
305             if (this.el.attr('xtype')) {
306                 this.el.attr('xtypex', this.el.attr('xtype'));
307                 this.el.dom.removeAttribute('xtype');
308                 
309                 this.initEvents();
310             }
311             
312             return;
313         }
314         
315          
316         
317         var cfg = Roo.apply({},  this.getAutoCreate());
318         
319         cfg.id = this.id || Roo.id();
320         
321         // fill in the extra attributes 
322         if (this.xattr && typeof(this.xattr) =='object') {
323             for (var i in this.xattr) {
324                 cfg[i] = this.xattr[i];
325             }
326         }
327         
328         if(this.dataId){
329             cfg.dataId = this.dataId;
330         }
331         
332         if (this.cls) {
333             cfg.cls = (typeof(cfg.cls) == 'undefined') ? this.cls : cfg.cls + ' ' + this.cls;
334         }
335         
336         if (this.style) { // fixme needs to support more complex style data.
337             cfg.style = this.style;
338         }
339         
340         if(this.name){
341             cfg.name = this.name;
342         }
343         
344         this.el = ct.createChild(cfg, position);
345         
346         if (this.tooltip) {
347             this.tooltipEl().attr('tooltip', this.tooltip);
348         }
349         
350         if(this.tabIndex !== undefined){
351             this.el.dom.setAttribute('tabIndex', this.tabIndex);
352         }
353         
354         this.initEvents();
355         
356     },
357     /**
358      * Fetch the element to add children to
359      * @return {Roo.Element} defaults to this.el
360      */
361     getChildContainer : function()
362     {
363         return this.el;
364     },
365     /**
366      * Fetch the element to display the tooltip on.
367      * @return {Roo.Element} defaults to this.el
368      */
369     tooltipEl : function()
370     {
371         return this.el;
372     },
373         
374     addxtype  : function(tree,cntr)
375     {
376         var cn = this;
377         
378         cn = Roo.factory(tree);
379         //Roo.log(['addxtype', cn]);
380            
381         cn.parentType = this.xtype; //??
382         cn.parentId = this.id;
383         
384         cntr = (typeof(cntr) == 'undefined' ) ? 'getChildContainer' : cntr;
385         if (typeof(cn.container_method) == 'string') {
386             cntr = cn.container_method;
387         }
388         
389         
390         var has_flexy_each =  (typeof(tree['flexy:foreach']) != 'undefined');
391         
392         var has_flexy_if =  (typeof(tree['flexy:if']) != 'undefined');
393         
394         var build_from_html =  Roo.XComponent.build_from_html;
395           
396         var is_body  = (tree.xtype == 'Body') ;
397           
398         var page_has_body = (Roo.get(document.body).attr('xtype') == 'Roo.bootstrap.Body');
399           
400         var self_cntr_el = Roo.get(this[cntr](false));
401         
402         // do not try and build conditional elements 
403         if ((has_flexy_each || has_flexy_if || this.can_build_overlaid == false ) && build_from_html) {
404             return false;
405         }
406         
407         if (!has_flexy_each || !build_from_html || is_body || !page_has_body) {
408             if(!has_flexy_if || typeof(tree.name) == 'undefined' || !build_from_html || is_body || !page_has_body){
409                 return this.addxtypeChild(tree,cntr, is_body);
410             }
411             
412             var echild =self_cntr_el ? self_cntr_el.child('>*[name=' + tree.name + ']') : false;
413                 
414             if(echild){
415                 return this.addxtypeChild(Roo.apply({}, tree),cntr);
416             }
417             
418             Roo.log('skipping render');
419             return cn;
420             
421         }
422         
423         var ret = false;
424         if (!build_from_html) {
425             return false;
426         }
427         
428         // this i think handles overlaying multiple children of the same type
429         // with the sam eelement.. - which might be buggy..
430         while (true) {
431             var echild =self_cntr_el ? self_cntr_el.child('>*[xtype]') : false;
432             
433             if (!echild) {
434                 break;
435             }
436             
437             if (echild && echild.attr('xtype').split('.').pop() != cn.xtype) {
438                 break;
439             }
440             
441             ret = this.addxtypeChild(Roo.apply({}, tree),cntr);
442         }
443        
444         return ret;
445     },
446     
447     
448     addxtypeChild : function (tree, cntr, is_body)
449     {
450         Roo.debug && Roo.log('addxtypeChild:' + cntr);
451         var cn = this;
452         cntr = (typeof(cntr) == 'undefined' ) ? 'getChildContainer' : cntr;
453         
454         
455         var has_flexy = (typeof(tree['flexy:if']) != 'undefined') ||
456                     (typeof(tree['flexy:foreach']) != 'undefined');
457           
458     
459         
460         skip_children = false;
461         // render the element if it's not BODY.
462         if (!is_body) {
463             
464             // if parent was disabled, then do not try and create the children..
465             if(!this[cntr](true)){
466                 tree.items = [];
467                 return tree;
468             }
469            
470             cn = Roo.factory(tree);
471            
472             cn.parentType = this.xtype; //??
473             cn.parentId = this.id;
474             
475             var build_from_html =  Roo.XComponent.build_from_html;
476             
477             
478             // does the container contain child eleemnts with 'xtype' attributes.
479             // that match this xtype..
480             // note - when we render we create these as well..
481             // so we should check to see if body has xtype set.
482             if (build_from_html && Roo.get(document.body).attr('xtype') == 'Roo.bootstrap.Body') {
483                
484                 var self_cntr_el = Roo.get(this[cntr](false));
485                 var echild =self_cntr_el ? self_cntr_el.child('>*[xtype]') : false;
486                 if (echild) { 
487                     //Roo.log(Roo.XComponent.build_from_html);
488                     //Roo.log("got echild:");
489                     //Roo.log(echild);
490                 }
491                 // there is a scenario where some of the child elements are flexy:if (and all of the same type)
492                 // and are not displayed -this causes this to use up the wrong element when matching.
493                 // at present the only work around for this is to nest flexy:if elements in another element that is always rendered.
494                 
495                 
496                 if (echild && echild.attr('xtype').split('.').pop() == cn.xtype) {
497                   //  Roo.log("found child for " + this.xtype +": " + echild.attr('xtype') );
498                   
499                   
500                   
501                     cn.el = echild;
502                   //  Roo.log("GOT");
503                     //echild.dom.removeAttribute('xtype');
504                 } else {
505                     Roo.debug && Roo.log("MISSING " + cn.xtype + " on child of " + (this.el ? this.el.attr('xbuilderid') : 'no parent'));
506                     Roo.debug && Roo.log(self_cntr_el);
507                     Roo.debug && Roo.log(echild);
508                     Roo.debug && Roo.log(cn);
509                 }
510             }
511            
512             
513            
514             // if object has flexy:if - then it may or may not be rendered.
515             if (build_from_html && has_flexy && !cn.el &&  cn.can_build_overlaid) {
516                 // skip a flexy if element.
517                 Roo.debug && Roo.log('skipping render');
518                 Roo.debug && Roo.log(tree);
519                 if (!cn.el) {
520                     Roo.debug && Roo.log('skipping all children');
521                     skip_children = true;
522                 }
523                 
524              } else {
525                  
526                 // actually if flexy:foreach is found, we really want to create 
527                 // multiple copies here...
528                 //Roo.log('render');
529                 //Roo.log(this[cntr]());
530                 // some elements do not have render methods.. like the layouts...
531                 /*
532                 if(this[cntr](true) === false){
533                     cn.items = [];
534                     return cn;
535                 }
536                 */
537                 cn.render && cn.render(this[cntr](true));
538                 
539              }
540             // then add the element..
541         }
542          
543         // handle the kids..
544         
545         var nitems = [];
546         /*
547         if (typeof (tree.menu) != 'undefined') {
548             tree.menu.parentType = cn.xtype;
549             tree.menu.triggerEl = cn.el;
550             nitems.push(cn.addxtype(Roo.apply({}, tree.menu)));
551             
552         }
553         */
554         if (!tree.items || !tree.items.length) {
555             cn.items = nitems;
556             //Roo.log(["no children", this]);
557             
558             return cn;
559         }
560          
561         var items = tree.items;
562         delete tree.items;
563         
564         //Roo.log(items.length);
565             // add the items..
566         if (!skip_children) {    
567             for(var i =0;i < items.length;i++) {
568               //  Roo.log(['add child', items[i]]);
569                 nitems.push(cn.addxtype(Roo.apply({}, items[i])));
570             }
571         }
572         
573         cn.items = nitems;
574         
575         //Roo.log("fire childrenrendered");
576         
577         cn.fireEvent('childrenrendered', this);
578         
579         return cn;
580     },
581     
582     /**
583      * Set the element that will be used to show or hide
584      */
585     setVisibilityEl : function(el)
586     {
587         this.visibilityEl = el;
588     },
589     
590      /**
591      * Get the element that will be used to show or hide
592      */
593     getVisibilityEl : function()
594     {
595         if (typeof(this.visibilityEl) == 'object') {
596             return this.visibilityEl;
597         }
598         
599         if (typeof(this.visibilityEl) == 'string') {
600             return this.visibilityEl == 'parent' ? this.parent().getEl() : this.getEl();
601         }
602         
603         return this.getEl();
604     },
605     
606     /**
607      * Show a component - removes 'hidden' class
608      */
609     show : function()
610     {
611         if(!this.getVisibilityEl()){
612             return;
613         }
614          
615         this.getVisibilityEl().removeClass(['hidden','d-none']);
616         
617         this.fireEvent('show', this);
618         
619         
620     },
621     /**
622      * Hide a component - adds 'hidden' class
623      */
624     hide: function()
625     {
626         if(!this.getVisibilityEl()){
627             return;
628         }
629         
630         this.getVisibilityEl().addClass(['hidden','d-none']);
631         
632         this.fireEvent('hide', this);
633         
634     }
635 });
636
637  /*
638  * - LGPL
639  *
640  * element
641  * 
642  */
643
644 /**
645  * @class Roo.bootstrap.Element
646  * @extends Roo.bootstrap.Component
647  * Bootstrap Element class
648  * @cfg {String} html contents of the element
649  * @cfg {String} tag tag of the element
650  * @cfg {String} cls class of the element
651  * @cfg {Boolean} preventDefault (true|false) default false
652  * @cfg {Boolean} clickable (true|false) default false
653  * 
654  * @constructor
655  * Create a new Element
656  * @param {Object} config The config object
657  */
658
659 Roo.bootstrap.Element = function(config){
660     Roo.bootstrap.Element.superclass.constructor.call(this, config);
661     
662     this.addEvents({
663         // raw events
664         /**
665          * @event click
666          * When a element is chick
667          * @param {Roo.bootstrap.Element} this
668          * @param {Roo.EventObject} e
669          */
670         "click" : true
671     });
672 };
673
674 Roo.extend(Roo.bootstrap.Element, Roo.bootstrap.Component,  {
675     
676     tag: 'div',
677     cls: '',
678     html: '',
679     preventDefault: false, 
680     clickable: false,
681     
682     getAutoCreate : function(){
683         
684         var cfg = {
685             tag: this.tag,
686             // cls: this.cls, double assign in parent class Component.js :: onRender
687             html: this.html
688         };
689         
690         return cfg;
691     },
692     
693     initEvents: function() 
694     {
695         Roo.bootstrap.Element.superclass.initEvents.call(this);
696         
697         if(this.clickable){
698             this.el.on('click', this.onClick, this);
699         }
700         
701     },
702     
703     onClick : function(e)
704     {
705         if(this.preventDefault){
706             e.preventDefault();
707         }
708         
709         this.fireEvent('click', this, e);
710     },
711     
712     getValue : function()
713     {
714         return this.el.dom.innerHTML;
715     },
716     
717     setValue : function(value)
718     {
719         this.el.dom.innerHTML = value;
720     }
721    
722 });
723
724  
725
726  /*
727  * - LGPL
728  *
729  * dropable area
730  * 
731  */
732
733 /**
734  * @class Roo.bootstrap.DropTarget
735  * @extends Roo.bootstrap.Element
736  * Bootstrap DropTarget class
737  
738  * @cfg {string} name dropable name
739  * 
740  * @constructor
741  * Create a new Dropable Area
742  * @param {Object} config The config object
743  */
744
745 Roo.bootstrap.DropTarget = function(config){
746     Roo.bootstrap.DropTarget.superclass.constructor.call(this, config);
747     
748     this.addEvents({
749         // raw events
750         /**
751          * @event click
752          * When a element is chick
753          * @param {Roo.bootstrap.Element} this
754          * @param {Roo.EventObject} e
755          */
756         "drop" : true
757     });
758 };
759
760 Roo.extend(Roo.bootstrap.DropTarget, Roo.bootstrap.Element,  {
761     
762     
763     getAutoCreate : function(){
764         
765          
766     },
767     
768     initEvents: function() 
769     {
770         Roo.bootstrap.DropTarget.superclass.initEvents.call(this);
771         this.dropZone = new Roo.dd.DropTarget(this.getEl(), {
772             ddGroup: this.name,
773             listeners : {
774                 drop : this.dragDrop.createDelegate(this),
775                 enter : this.dragEnter.createDelegate(this),
776                 out : this.dragOut.createDelegate(this),
777                 over : this.dragOver.createDelegate(this)
778             }
779             
780         });
781         this.dropZone.DDM.useCache = false // so data gets refreshed when we resize stuff
782     },
783     
784     dragDrop : function(source,e,data)
785     {
786         // user has to decide how to impliment this.
787         Roo.log('drop');
788         Roo.log(this);
789         //this.fireEvent('drop', this, source, e ,data);
790         return false;
791     },
792     
793     dragEnter : function(n, dd, e, data)
794     {
795         // probably want to resize the element to match the dropped element..
796         Roo.log("enter");
797         this.originalSize = this.el.getSize();
798         this.el.setSize( n.el.getSize());
799         this.dropZone.DDM.refreshCache(this.name);
800         Roo.log([n, dd, e, data]);
801     },
802     
803     dragOut : function(value)
804     {
805         // resize back to normal
806         Roo.log("out");
807         this.el.setSize(this.originalSize);
808         this.dropZone.resetConstraints();
809     },
810     
811     dragOver : function()
812     {
813         // ??? do nothing?
814     }
815    
816 });
817
818  
819
820  /*
821  * - LGPL
822  *
823  * Body
824  *
825  */
826
827 /**
828  * @class Roo.bootstrap.Body
829  * @extends Roo.bootstrap.Component
830  * Bootstrap Body class
831  *
832  * @constructor
833  * Create a new body
834  * @param {Object} config The config object
835  */
836
837 Roo.bootstrap.Body = function(config){
838
839     config = config || {};
840
841     Roo.bootstrap.Body.superclass.constructor.call(this, config);
842     this.el = Roo.get(config.el ? config.el : document.body );
843     if (this.cls && this.cls.length) {
844         Roo.get(document.body).addClass(this.cls);
845     }
846 };
847
848 Roo.extend(Roo.bootstrap.Body, Roo.bootstrap.Component,  {
849
850     is_body : true,// just to make sure it's constructed?
851
852         autoCreate : {
853         cls: 'container'
854     },
855     onRender : function(ct, position)
856     {
857        /* Roo.log("Roo.bootstrap.Body - onRender");
858         if (this.cls && this.cls.length) {
859             Roo.get(document.body).addClass(this.cls);
860         }
861         // style??? xttr???
862         */
863     }
864
865
866
867
868 });
869 /*
870  * - LGPL
871  *
872  * button group
873  * 
874  */
875
876
877 /**
878  * @class Roo.bootstrap.ButtonGroup
879  * @extends Roo.bootstrap.Component
880  * Bootstrap ButtonGroup class
881  * @cfg {String} size lg | sm | xs (default empty normal)
882  * @cfg {String} align vertical | justified  (default none)
883  * @cfg {String} direction up | down (default down)
884  * @cfg {Boolean} toolbar false | true
885  * @cfg {Boolean} btn true | false
886  * 
887  * 
888  * @constructor
889  * Create a new Input
890  * @param {Object} config The config object
891  */
892
893 Roo.bootstrap.ButtonGroup = function(config){
894     Roo.bootstrap.ButtonGroup.superclass.constructor.call(this, config);
895 };
896
897 Roo.extend(Roo.bootstrap.ButtonGroup, Roo.bootstrap.Component,  {
898     
899     size: '',
900     align: '',
901     direction: '',
902     toolbar: false,
903     btn: true,
904
905     getAutoCreate : function(){
906         var cfg = {
907             cls: 'btn-group',
908             html : null
909         };
910         
911         cfg.html = this.html || cfg.html;
912         
913         if (this.toolbar) {
914             cfg = {
915                 cls: 'btn-toolbar',
916                 html: null
917             };
918             
919             return cfg;
920         }
921         
922         if (['vertical','justified'].indexOf(this.align)!==-1) {
923             cfg.cls = 'btn-group-' + this.align;
924             
925             if (this.align == 'justified') {
926                 console.log(this.items);
927             }
928         }
929         
930         if (['lg','sm','xs'].indexOf(this.size)!==-1) {
931             cfg.cls += ' btn-group-' + this.size;
932         }
933         
934         if (this.direction == 'up') {
935             cfg.cls += ' dropup' ;
936         }
937         
938         return cfg;
939     },
940     /**
941      * Add a button to the group (similar to NavItem API.)
942      */
943     addItem : function(cfg)
944     {
945         var cn = new Roo.bootstrap.Button(cfg);
946         //this.register(cn);
947         cn.parentId = this.id;
948         cn.onRender(this.el, null);
949         return cn;
950     }
951    
952 });
953
954  /*
955  * - LGPL
956  *
957  * button
958  * 
959  */
960
961 /**
962  * @class Roo.bootstrap.Button
963  * @extends Roo.bootstrap.Component
964  * Bootstrap Button class
965  * @cfg {String} html The button content
966  * @cfg {String} weight (default|primary|secondary|success|info|warning|danger|link|light|dark) default
967  * @cfg {String} badge_weight (default|primary|secondary|success|info|warning|danger|link|light|dark) default (same as button)
968  * @cfg {Boolean} outline default false (except for weight=default which emulates old behaveiour with an outline)
969  * @cfg {String} size (lg|sm|xs)
970  * @cfg {String} tag (a|input|submit)
971  * @cfg {String} href empty or href
972  * @cfg {Boolean} disabled default false;
973  * @cfg {Boolean} isClose default false;
974  * @cfg {String} glyphicon depricated - use fa
975  * @cfg {String} fa fontawesome icon - eg. 'comment' - without the fa/fas etc..
976  * @cfg {String} badge text for badge
977  * @cfg {String} theme (default|glow)  
978  * @cfg {Boolean} inverse dark themed version
979  * @cfg {Boolean} toggle is it a slidy toggle button
980  * @cfg {Boolean} pressed   default null - if the button ahs active state
981  * @cfg {String} ontext text for on slidy toggle state
982  * @cfg {String} offtext text for off slidy toggle state
983  * @cfg {Boolean} preventDefault  default true (stop click event triggering the URL if it's a link.)
984  * @cfg {Boolean} removeClass remove the standard class..
985  * @cfg {String} target (_self|_blank|_parent|_top|other) target for a href. 
986  * @cfg {Boolean} grpup if parent is a btn group - then it turns it into a toogleGroup.
987  * 
988  * @constructor
989  * Create a new button
990  * @param {Object} config The config object
991  */
992
993
994 Roo.bootstrap.Button = function(config){
995     Roo.bootstrap.Button.superclass.constructor.call(this, config);
996     
997     this.addEvents({
998         // raw events
999         /**
1000          * @event click
1001          * When a button is pressed
1002          * @param {Roo.bootstrap.Button} btn
1003          * @param {Roo.EventObject} e
1004          */
1005         "click" : true,
1006         /**
1007          * @event dblclick
1008          * When a button is double clicked
1009          * @param {Roo.bootstrap.Button} btn
1010          * @param {Roo.EventObject} e
1011          */
1012         "dblclick" : true,
1013          /**
1014          * @event toggle
1015          * After the button has been toggles
1016          * @param {Roo.bootstrap.Button} btn
1017          * @param {Roo.EventObject} e
1018          * @param {boolean} pressed (also available as button.pressed)
1019          */
1020         "toggle" : true
1021     });
1022 };
1023
1024 Roo.extend(Roo.bootstrap.Button, Roo.bootstrap.Component,  {
1025     html: false,
1026     active: false,
1027     weight: '',
1028     badge_weight: '',
1029     outline : false,
1030     size: '',
1031     tag: 'button',
1032     href: '',
1033     disabled: false,
1034     isClose: false,
1035     glyphicon: '',
1036     fa: '',
1037     badge: '',
1038     theme: 'default',
1039     inverse: false,
1040     
1041     toggle: false,
1042     ontext: 'ON',
1043     offtext: 'OFF',
1044     defaulton: true,
1045     preventDefault: true,
1046     removeClass: false,
1047     name: false,
1048     target: false,
1049     group : false,
1050      
1051     pressed : null,
1052      
1053     
1054     getAutoCreate : function(){
1055         
1056         var cfg = {
1057             tag : 'button',
1058             cls : 'roo-button',
1059             html: ''
1060         };
1061         
1062         if (['a', 'button', 'input', 'submit'].indexOf(this.tag) < 0) {
1063             throw "Invalid value for tag: " + this.tag + ". must be a, button, input or submit.";
1064             this.tag = 'button';
1065         } else {
1066             cfg.tag = this.tag;
1067         }
1068         cfg.html = '<span class="roo-button-text">' + (this.html || cfg.html) + '</span>';
1069         
1070         if (this.toggle == true) {
1071             cfg={
1072                 tag: 'div',
1073                 cls: 'slider-frame roo-button',
1074                 cn: [
1075                     {
1076                         tag: 'span',
1077                         'data-on-text':'ON',
1078                         'data-off-text':'OFF',
1079                         cls: 'slider-button',
1080                         html: this.offtext
1081                     }
1082                 ]
1083             };
1084             // why are we validating the weights?
1085             if (Roo.bootstrap.Button.weights.indexOf(this.weight) > -1) {
1086                 cfg.cls +=  ' ' + this.weight;
1087             }
1088             
1089             return cfg;
1090         }
1091         
1092         if (this.isClose) {
1093             cfg.cls += ' close';
1094             
1095             cfg["aria-hidden"] = true;
1096             
1097             cfg.html = "&times;";
1098             
1099             return cfg;
1100         }
1101              
1102         
1103         if (this.theme==='default') {
1104             cfg.cls = 'btn roo-button';
1105             
1106             //if (this.parentType != 'Navbar') {
1107             this.weight = this.weight.length ?  this.weight : 'default';
1108             //}
1109             if (Roo.bootstrap.Button.weights.indexOf(this.weight) > -1) {
1110                 
1111                 var outline = this.outline || this.weight == 'default' ? 'outline-' : '';
1112                 var weight = this.weight == 'default' ? 'secondary' : this.weight;
1113                 cfg.cls += ' btn-' + outline + weight;
1114                 if (this.weight == 'default') {
1115                     // BC
1116                     cfg.cls += ' btn-' + this.weight;
1117                 }
1118             }
1119         } else if (this.theme==='glow') {
1120             
1121             cfg.tag = 'a';
1122             cfg.cls = 'btn-glow roo-button';
1123             
1124             if (Roo.bootstrap.Button.weights.indexOf(this.weight) > -1) {
1125                 
1126                 cfg.cls += ' ' + this.weight;
1127             }
1128         }
1129    
1130         
1131         if (this.inverse) {
1132             this.cls += ' inverse';
1133         }
1134         
1135         
1136         if (this.active || this.pressed === true) {
1137             cfg.cls += ' active';
1138         }
1139         
1140         if (this.disabled) {
1141             cfg.disabled = 'disabled';
1142         }
1143         
1144         if (this.items) {
1145             Roo.log('changing to ul' );
1146             cfg.tag = 'ul';
1147             this.glyphicon = 'caret';
1148             if (Roo.bootstrap.version == 4) {
1149                 this.fa = 'caret-down';
1150             }
1151             
1152         }
1153         
1154         cfg.cls += this.size.length ? (' btn-' + this.size) : '';
1155          
1156         //gsRoo.log(this.parentType);
1157         if (this.parentType === 'Navbar' && !this.parent().bar) {
1158             Roo.log('changing to li?');
1159             
1160             cfg.tag = 'li';
1161             
1162             cfg.cls = '';
1163             cfg.cn =  [{
1164                 tag : 'a',
1165                 cls : 'roo-button',
1166                 html : this.html,
1167                 href : this.href || '#'
1168             }];
1169             if (this.menu) {
1170                 cfg.cn[0].html = this.html  + ' <span class="caret"></span>';
1171                 cfg.cls += ' dropdown';
1172             }   
1173             
1174             delete cfg.html;
1175             
1176         }
1177         
1178        cfg.cls += this.parentType === 'Navbar' ?  ' navbar-btn' : '';
1179         
1180         if (this.glyphicon) {
1181             cfg.html = ' ' + cfg.html;
1182             
1183             cfg.cn = [
1184                 {
1185                     tag: 'span',
1186                     cls: 'glyphicon glyphicon-' + this.glyphicon
1187                 }
1188             ];
1189         }
1190         if (this.fa) {
1191             cfg.html = ' ' + cfg.html;
1192             
1193             cfg.cn = [
1194                 {
1195                     tag: 'i',
1196                     cls: 'fa fas fa-' + this.fa
1197                 }
1198             ];
1199         }
1200         
1201         if (this.badge) {
1202             cfg.html += ' ';
1203             
1204             cfg.tag = 'a';
1205             
1206 //            cfg.cls='btn roo-button';
1207             
1208             cfg.href=this.href;
1209             
1210             var value = cfg.html;
1211             
1212             if(this.glyphicon){
1213                 value = {
1214                     tag: 'span',
1215                     cls: 'glyphicon glyphicon-' + this.glyphicon,
1216                     html: this.html
1217                 };
1218             }
1219             if(this.fa){
1220                 value = {
1221                     tag: 'i',
1222                     cls: 'fa fas fa-' + this.fa,
1223                     html: this.html
1224                 };
1225             }
1226             
1227             var bw = this.badge_weight.length ? this.badge_weight :
1228                 (this.weight.length ? this.weight : 'secondary');
1229             bw = bw == 'default' ? 'secondary' : bw;
1230             
1231             cfg.cn = [
1232                 value,
1233                 {
1234                     tag: 'span',
1235                     cls: 'badge badge-' + bw,
1236                     html: this.badge
1237                 }
1238             ];
1239             
1240             cfg.html='';
1241         }
1242         
1243         if (this.menu) {
1244             cfg.cls += ' dropdown';
1245             cfg.html = typeof(cfg.html) != 'undefined' ?
1246                     cfg.html + ' <span class="caret"></span>' : '<span class="caret"></span>';
1247         }
1248         
1249         if (cfg.tag !== 'a' && this.href !== '') {
1250             throw "Tag must be a to set href.";
1251         } else if (this.href.length > 0) {
1252             cfg.href = this.href;
1253         }
1254         
1255         if(this.removeClass){
1256             cfg.cls = '';
1257         }
1258         
1259         if(this.target){
1260             cfg.target = this.target;
1261         }
1262         
1263         return cfg;
1264     },
1265     initEvents: function() {
1266        // Roo.log('init events?');
1267 //        Roo.log(this.el.dom);
1268         // add the menu...
1269         
1270         if (typeof (this.menu) != 'undefined') {
1271             this.menu.parentType = this.xtype;
1272             this.menu.triggerEl = this.el;
1273             this.addxtype(Roo.apply({}, this.menu));
1274         }
1275
1276
1277         if (this.el.hasClass('roo-button')) {
1278              this.el.on('click', this.onClick, this);
1279              this.el.on('dblclick', this.onDblClick, this);
1280         } else {
1281              this.el.select('.roo-button').on('click', this.onClick, this);
1282              this.el.select('.roo-button').on('dblclick', this.onDblClick, this);
1283              
1284         }
1285         // why?
1286         if(this.removeClass){
1287             this.el.on('click', this.onClick, this);
1288         }
1289         
1290         if (this.group === true) {
1291              if (this.pressed === false || this.pressed === true) {
1292                 // nothing
1293             } else {
1294                 this.pressed = false;
1295                 this.setActive(this.pressed);
1296             }
1297             
1298         }
1299         
1300         this.el.enableDisplayMode();
1301         
1302     },
1303     onClick : function(e)
1304     {
1305         if (this.disabled) {
1306             return;
1307         }
1308         
1309         Roo.log('button on click ');
1310         if(this.preventDefault){
1311             e.preventDefault();
1312         }
1313         
1314         if (this.group) {
1315             if (this.pressed) {
1316                 // do nothing -
1317                 return;
1318             }
1319             this.setActive(true);
1320             var pi = this.parent().items;
1321             for (var i = 0;i < pi.length;i++) {
1322                 if (this == pi[i]) {
1323                     continue;
1324                 }
1325                 if (pi[i].el.hasClass('roo-button')) {
1326                     pi[i].setActive(false);
1327                 }
1328             }
1329             this.fireEvent('click', this, e);            
1330             return;
1331         }
1332         
1333         if (this.pressed === true || this.pressed === false) {
1334             this.toggleActive(e);
1335         }
1336         
1337         
1338         this.fireEvent('click', this, e);
1339     },
1340     onDblClick: function(e)
1341     {
1342         if (this.disabled) {
1343             return;
1344         }
1345         if(this.preventDefault){
1346             e.preventDefault();
1347         }
1348         this.fireEvent('dblclick', this, e);
1349     },
1350     /**
1351      * Enables this button
1352      */
1353     enable : function()
1354     {
1355         this.disabled = false;
1356         this.el.removeClass('disabled');
1357     },
1358     
1359     /**
1360      * Disable this button
1361      */
1362     disable : function()
1363     {
1364         this.disabled = true;
1365         this.el.addClass('disabled');
1366     },
1367      /**
1368      * sets the active state on/off, 
1369      * @param {Boolean} state (optional) Force a particular state
1370      */
1371     setActive : function(v) {
1372         
1373         this.el[v ? 'addClass' : 'removeClass']('active');
1374         this.pressed = v;
1375     },
1376      /**
1377      * toggles the current active state 
1378      */
1379     toggleActive : function(e)
1380     {
1381         this.setActive(!this.pressed); // this modifies pressed...
1382         this.fireEvent('toggle', this, e, this.pressed);
1383     },
1384      /**
1385      * get the current active state
1386      * @return {boolean} true if it's active
1387      */
1388     isActive : function()
1389     {
1390         return this.el.hasClass('active');
1391     },
1392     /**
1393      * set the text of the first selected button
1394      */
1395     setText : function(str)
1396     {
1397         this.el.select('.roo-button-text',true).first().dom.innerHTML = str;
1398     },
1399     /**
1400      * get the text of the first selected button
1401      */
1402     getText : function()
1403     {
1404         return this.el.select('.roo-button-text',true).first().dom.innerHTML;
1405     },
1406     
1407     setWeight : function(str)
1408     {
1409         this.el.removeClass(Roo.bootstrap.Button.weights.map(function(w) { return 'btn-' + w; } ) );
1410         this.el.removeClass(Roo.bootstrap.Button.weights.map(function(w) { return 'btn-outline-' + w; } ) );
1411         this.weight = str;
1412         var outline = this.outline ? 'outline-' : '';
1413         if (str == 'default') {
1414             this.el.addClass('btn-default btn-outline-secondary');        
1415             return;
1416         }
1417         this.el.addClass('btn-' + outline + str);        
1418     }
1419     
1420     
1421 });
1422 // fixme - this is probably generic bootstrap - should go in some kind of enum file.. - like sizes.
1423
1424 Roo.bootstrap.Button.weights = [
1425     'default',
1426     'secondary' ,
1427     'primary',
1428     'success',
1429     'info',
1430     'warning',
1431     'danger',
1432     'link',
1433     'light',
1434     'dark'              
1435    
1436 ];/*
1437  * - LGPL
1438  *
1439  * column
1440  * 
1441  */
1442
1443 /**
1444  * @class Roo.bootstrap.Column
1445  * @extends Roo.bootstrap.Component
1446  * Bootstrap Column class
1447  * @cfg {Number} xs colspan out of 12 for mobile-sized screens or 0 for hidden
1448  * @cfg {Number} sm colspan out of 12 for tablet-sized screens or 0 for hidden
1449  * @cfg {Number} md colspan out of 12 for computer-sized screens or 0 for hidden
1450  * @cfg {Number} lg colspan out of 12 for large computer-sized screens or 0 for hidden
1451  * @cfg {Number} xsoff colspan offset out of 12 for mobile-sized screens or 0 for hidden
1452  * @cfg {Number} smoff colspan offset out of 12 for tablet-sized screens or 0 for hidden
1453  * @cfg {Number} mdoff colspan offset out of 12 for computer-sized screens or 0 for hidden
1454  * @cfg {Number} lgoff colspan offset out of 12 for large computer-sized screens or 0 for hidden
1455  *
1456  * 
1457  * @cfg {Boolean} hidden (true|false) hide the element
1458  * @cfg {String} alert (success|info|warning|danger) type alert (changes background / border...)
1459  * @cfg {String} fa (ban|check|...) font awesome icon
1460  * @cfg {Number} fasize (1|2|....) font awsome size
1461
1462  * @cfg {String} icon (info-sign|check|...) glyphicon name
1463
1464  * @cfg {String} html content of column.
1465  * 
1466  * @constructor
1467  * Create a new Column
1468  * @param {Object} config The config object
1469  */
1470
1471 Roo.bootstrap.Column = function(config){
1472     Roo.bootstrap.Column.superclass.constructor.call(this, config);
1473 };
1474
1475 Roo.extend(Roo.bootstrap.Column, Roo.bootstrap.Component,  {
1476     
1477     xs: false,
1478     sm: false,
1479     md: false,
1480     lg: false,
1481     xsoff: false,
1482     smoff: false,
1483     mdoff: false,
1484     lgoff: false,
1485     html: '',
1486     offset: 0,
1487     alert: false,
1488     fa: false,
1489     icon : false,
1490     hidden : false,
1491     fasize : 1,
1492     
1493     getAutoCreate : function(){
1494         var cfg = Roo.apply({}, Roo.bootstrap.Column.superclass.getAutoCreate.call(this));
1495         
1496         cfg = {
1497             tag: 'div',
1498             cls: 'column'
1499         };
1500         
1501         var settings=this;
1502         var sizes =   ['xs','sm','md','lg'];
1503         sizes.map(function(size ,ix){
1504             //Roo.log( size + ':' + settings[size]);
1505             
1506             if (settings[size+'off'] !== false) {
1507                 cfg.cls += ' col-' + size + '-offset-' + settings[size+'off'] ;
1508             }
1509             
1510             if (settings[size] === false) {
1511                 return;
1512             }
1513             
1514             if (!settings[size]) { // 0 = hidden
1515                 cfg.cls += ' hidden-' + size + ' hidden-' + size + '-down';
1516                 // bootsrap4
1517                 for (var i = ix; i > -1; i--) {
1518                     cfg.cls +=  ' d-' + sizes[i] + '-none'; 
1519                 }
1520                 
1521                 
1522                 return;
1523             }
1524             cfg.cls += ' col-' + size + '-' + settings[size] + (
1525                 size == 'xs' ? (' col-' + settings[size] ) : '' // bs4 col-{num} replaces col-xs
1526             );
1527             
1528         });
1529         
1530         if (this.hidden) {
1531             cfg.cls += ' hidden';
1532         }
1533         
1534         if (this.alert && ["success","info","warning", "danger"].indexOf(this.alert) > -1) {
1535             cfg.cls +=' alert alert-' + this.alert;
1536         }
1537         
1538         
1539         if (this.html.length) {
1540             cfg.html = this.html;
1541         }
1542         if (this.fa) {
1543             var fasize = '';
1544             if (this.fasize > 1) {
1545                 fasize = ' fa-' + this.fasize + 'x';
1546             }
1547             cfg.html = '<i class="fa fa-'+this.fa + fasize + '"></i>' + (cfg.html || '');
1548             
1549             
1550         }
1551         if (this.icon) {
1552             cfg.html = '<i class="glyphicon glyphicon-'+this.icon + '"></i>' +  (cfg.html || '');
1553         }
1554         
1555         return cfg;
1556     }
1557    
1558 });
1559
1560  
1561
1562  /*
1563  * - LGPL
1564  *
1565  * page container.
1566  * 
1567  */
1568
1569
1570 /**
1571  * @class Roo.bootstrap.Container
1572  * @extends Roo.bootstrap.Component
1573  * Bootstrap Container class
1574  * @cfg {Boolean} jumbotron is it a jumbotron element
1575  * @cfg {String} html content of element
1576  * @cfg {String} well (lg|sm|md) a well, large, small or medium.
1577  * @cfg {String} panel (default|primary|success|info|warning|danger) render as panel  - type - primary/success.....
1578  * @cfg {String} header content of header (for panel)
1579  * @cfg {String} footer content of footer (for panel)
1580  * @cfg {String} sticky (footer|wrap|push) block to use as footer or body- needs css-bootstrap/sticky-footer.css
1581  * @cfg {String} tag (header|aside|section) type of HTML tag.
1582  * @cfg {String} alert (success|info|warning|danger) type alert (changes background / border...)
1583  * @cfg {String} fa font awesome icon
1584  * @cfg {String} icon (info-sign|check|...) glyphicon name
1585  * @cfg {Boolean} hidden (true|false) hide the element
1586  * @cfg {Boolean} expandable (true|false) default false
1587  * @cfg {Boolean} expanded (true|false) default true
1588  * @cfg {String} rheader contet on the right of header
1589  * @cfg {Boolean} clickable (true|false) default false
1590
1591  *     
1592  * @constructor
1593  * Create a new Container
1594  * @param {Object} config The config object
1595  */
1596
1597 Roo.bootstrap.Container = function(config){
1598     Roo.bootstrap.Container.superclass.constructor.call(this, config);
1599     
1600     this.addEvents({
1601         // raw events
1602          /**
1603          * @event expand
1604          * After the panel has been expand
1605          * 
1606          * @param {Roo.bootstrap.Container} this
1607          */
1608         "expand" : true,
1609         /**
1610          * @event collapse
1611          * After the panel has been collapsed
1612          * 
1613          * @param {Roo.bootstrap.Container} this
1614          */
1615         "collapse" : true,
1616         /**
1617          * @event click
1618          * When a element is chick
1619          * @param {Roo.bootstrap.Container} this
1620          * @param {Roo.EventObject} e
1621          */
1622         "click" : true
1623     });
1624 };
1625
1626 Roo.extend(Roo.bootstrap.Container, Roo.bootstrap.Component,  {
1627     
1628     jumbotron : false,
1629     well: '',
1630     panel : '',
1631     header: '',
1632     footer : '',
1633     sticky: '',
1634     tag : false,
1635     alert : false,
1636     fa: false,
1637     icon : false,
1638     expandable : false,
1639     rheader : '',
1640     expanded : true,
1641     clickable: false,
1642   
1643      
1644     getChildContainer : function() {
1645         
1646         if(!this.el){
1647             return false;
1648         }
1649         
1650         if (this.panel.length) {
1651             return this.el.select('.panel-body',true).first();
1652         }
1653         
1654         return this.el;
1655     },
1656     
1657     
1658     getAutoCreate : function(){
1659         
1660         var cfg = {
1661             tag : this.tag || 'div',
1662             html : '',
1663             cls : ''
1664         };
1665         if (this.jumbotron) {
1666             cfg.cls = 'jumbotron';
1667         }
1668         
1669         
1670         
1671         // - this is applied by the parent..
1672         //if (this.cls) {
1673         //    cfg.cls = this.cls + '';
1674         //}
1675         
1676         if (this.sticky.length) {
1677             
1678             var bd = Roo.get(document.body);
1679             if (!bd.hasClass('bootstrap-sticky')) {
1680                 bd.addClass('bootstrap-sticky');
1681                 Roo.select('html',true).setStyle('height', '100%');
1682             }
1683              
1684             cfg.cls += 'bootstrap-sticky-' + this.sticky;
1685         }
1686         
1687         
1688         if (this.well.length) {
1689             switch (this.well) {
1690                 case 'lg':
1691                 case 'sm':
1692                     cfg.cls +=' well well-' +this.well;
1693                     break;
1694                 default:
1695                     cfg.cls +=' well';
1696                     break;
1697             }
1698         }
1699         
1700         if (this.hidden) {
1701             cfg.cls += ' hidden';
1702         }
1703         
1704         
1705         if (this.alert && ["success","info","warning", "danger"].indexOf(this.alert) > -1) {
1706             cfg.cls +=' alert alert-' + this.alert;
1707         }
1708         
1709         var body = cfg;
1710         
1711         if (this.panel.length) {
1712             cfg.cls += ' panel panel-' + this.panel;
1713             cfg.cn = [];
1714             if (this.header.length) {
1715                 
1716                 var h = [];
1717                 
1718                 if(this.expandable){
1719                     
1720                     cfg.cls = cfg.cls + ' expandable';
1721                     
1722                     h.push({
1723                         tag: 'i',
1724                         cls: (this.expanded ? 'fa fa-minus' : 'fa fa-plus') 
1725                     });
1726                     
1727                 }
1728                 
1729                 h.push(
1730                     {
1731                         tag: 'span',
1732                         cls : 'panel-title',
1733                         html : (this.expandable ? '&nbsp;' : '') + this.header
1734                     },
1735                     {
1736                         tag: 'span',
1737                         cls: 'panel-header-right',
1738                         html: this.rheader
1739                     }
1740                 );
1741                 
1742                 cfg.cn.push({
1743                     cls : 'panel-heading',
1744                     style : this.expandable ? 'cursor: pointer' : '',
1745                     cn : h
1746                 });
1747                 
1748             }
1749             
1750             body = false;
1751             cfg.cn.push({
1752                 cls : 'panel-body' + (this.expanded ? '' : ' hide'),
1753                 html : this.html
1754             });
1755             
1756             
1757             if (this.footer.length) {
1758                 cfg.cn.push({
1759                     cls : 'panel-footer',
1760                     html : this.footer
1761                     
1762                 });
1763             }
1764             
1765         }
1766         
1767         if (body) {
1768             body.html = this.html || cfg.html;
1769             // prefix with the icons..
1770             if (this.fa) {
1771                 body.html = '<i class="fa fa-'+this.fa + '"></i>' + body.html ;
1772             }
1773             if (this.icon) {
1774                 body.html = '<i class="glyphicon glyphicon-'+this.icon + '"></i>' + body.html ;
1775             }
1776             
1777             
1778         }
1779         if ((!this.cls || !this.cls.length) && (!cfg.cls || !cfg.cls.length)) {
1780             cfg.cls =  'container';
1781         }
1782         
1783         return cfg;
1784     },
1785     
1786     initEvents: function() 
1787     {
1788         if(this.expandable){
1789             var headerEl = this.headerEl();
1790         
1791             if(headerEl){
1792                 headerEl.on('click', this.onToggleClick, this);
1793             }
1794         }
1795         
1796         if(this.clickable){
1797             this.el.on('click', this.onClick, this);
1798         }
1799         
1800     },
1801     
1802     onToggleClick : function()
1803     {
1804         var headerEl = this.headerEl();
1805         
1806         if(!headerEl){
1807             return;
1808         }
1809         
1810         if(this.expanded){
1811             this.collapse();
1812             return;
1813         }
1814         
1815         this.expand();
1816     },
1817     
1818     expand : function()
1819     {
1820         if(this.fireEvent('expand', this)) {
1821             
1822             this.expanded = true;
1823             
1824             //this.el.select('.panel-body',true).first().setVisibilityMode(Roo.Element.DISPLAY).show();
1825             
1826             this.el.select('.panel-body',true).first().removeClass('hide');
1827             
1828             var toggleEl = this.toggleEl();
1829
1830             if(!toggleEl){
1831                 return;
1832             }
1833
1834             toggleEl.removeClass(['fa-minus', 'fa-plus']).addClass(['fa-minus']);
1835         }
1836         
1837     },
1838     
1839     collapse : function()
1840     {
1841         if(this.fireEvent('collapse', this)) {
1842             
1843             this.expanded = false;
1844             
1845             //this.el.select('.panel-body',true).first().setVisibilityMode(Roo.Element.DISPLAY).hide();
1846             this.el.select('.panel-body',true).first().addClass('hide');
1847         
1848             var toggleEl = this.toggleEl();
1849
1850             if(!toggleEl){
1851                 return;
1852             }
1853
1854             toggleEl.removeClass(['fa-minus', 'fa-plus']).addClass(['fa-plus']);
1855         }
1856     },
1857     
1858     toggleEl : function()
1859     {
1860         if(!this.el || !this.panel.length || !this.header.length || !this.expandable){
1861             return;
1862         }
1863         
1864         return this.el.select('.panel-heading .fa',true).first();
1865     },
1866     
1867     headerEl : function()
1868     {
1869         if(!this.el || !this.panel.length || !this.header.length){
1870             return;
1871         }
1872         
1873         return this.el.select('.panel-heading',true).first()
1874     },
1875     
1876     bodyEl : function()
1877     {
1878         if(!this.el || !this.panel.length){
1879             return;
1880         }
1881         
1882         return this.el.select('.panel-body',true).first()
1883     },
1884     
1885     titleEl : function()
1886     {
1887         if(!this.el || !this.panel.length || !this.header.length){
1888             return;
1889         }
1890         
1891         return this.el.select('.panel-title',true).first();
1892     },
1893     
1894     setTitle : function(v)
1895     {
1896         var titleEl = this.titleEl();
1897         
1898         if(!titleEl){
1899             return;
1900         }
1901         
1902         titleEl.dom.innerHTML = v;
1903     },
1904     
1905     getTitle : function()
1906     {
1907         
1908         var titleEl = this.titleEl();
1909         
1910         if(!titleEl){
1911             return '';
1912         }
1913         
1914         return titleEl.dom.innerHTML;
1915     },
1916     
1917     setRightTitle : function(v)
1918     {
1919         var t = this.el.select('.panel-header-right',true).first();
1920         
1921         if(!t){
1922             return;
1923         }
1924         
1925         t.dom.innerHTML = v;
1926     },
1927     
1928     onClick : function(e)
1929     {
1930         e.preventDefault();
1931         
1932         this.fireEvent('click', this, e);
1933     }
1934 });
1935
1936  /*
1937  *  - LGPL
1938  *
1939  *  This is BS4's Card element.. - similar to our containers probably..
1940  * 
1941  */
1942 /**
1943  * @class Roo.bootstrap.Card
1944  * @extends Roo.bootstrap.Component
1945  * Bootstrap Card class
1946  *
1947  *
1948  * possible... may not be implemented..
1949  * @cfg {String} header_image  src url of image.
1950  * @cfg {String|Object} header
1951  * @cfg {Number} header_size (0|1|2|3|4|5) H1 or H2 etc.. 0 indicates default
1952  * 
1953  * @cfg {String} title
1954  * @cfg {String} subtitle
1955  * @cfg {String|Boolean} html -- html contents - or just use children.. use false to hide it..
1956  * @cfg {String} footer
1957  
1958  * @cfg {String} weight (primary|warning|info|danger|secondary|success|light|dark)
1959  * 
1960  * @cfg {String} margin (0|1|2|3|4|5|auto)
1961  * @cfg {String} margin_top (0|1|2|3|4|5|auto)
1962  * @cfg {String} margin_bottom (0|1|2|3|4|5|auto)
1963  * @cfg {String} margin_left (0|1|2|3|4|5|auto)
1964  * @cfg {String} margin_right (0|1|2|3|4|5|auto)
1965  * @cfg {String} margin_x (0|1|2|3|4|5|auto)
1966  * @cfg {String} margin_y (0|1|2|3|4|5|auto)
1967  *
1968  * @cfg {String} padding (0|1|2|3|4|5)
1969  * @cfg {String} padding_top (0|1|2|3|4|5)next_to_card
1970  * @cfg {String} padding_bottom (0|1|2|3|4|5)
1971  * @cfg {String} padding_left (0|1|2|3|4|5)
1972  * @cfg {String} padding_right (0|1|2|3|4|5)
1973  * @cfg {String} padding_x (0|1|2|3|4|5)
1974  * @cfg {String} padding_y (0|1|2|3|4|5)
1975  *
1976  * @cfg {String} display (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
1977  * @cfg {String} display_xs (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
1978  * @cfg {String} display_sm (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
1979  * @cfg {String} display_lg (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
1980  * @cfg {String} display_xl (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
1981  
1982  * @config {Boolean} dragable  if this card can be dragged.
1983  * @config {String} drag_group  group for drag
1984  * @config {Boolean} dropable  if this card can recieve other cards being dropped onto it..
1985  * @config {String} drop_group  group for drag
1986  * 
1987  * @config {Boolean} collapsable can the body be collapsed.
1988  * @config {Boolean} collapsed is the body collapsed when rendered...
1989  * @config {Boolean} rotateable can the body be rotated by clicking on it..
1990  * @config {Boolean} rotated is the body rotated when rendered...
1991  * 
1992  * @constructor
1993  * Create a new Container
1994  * @param {Object} config The config object
1995  */
1996
1997 Roo.bootstrap.Card = function(config){
1998     Roo.bootstrap.Card.superclass.constructor.call(this, config);
1999     
2000     this.addEvents({
2001          // raw events
2002         /**
2003          * @event drop
2004          * When a element a card is dropped
2005          * @param {Roo.bootstrap.Card} this
2006          *
2007          * 
2008          * @param {Roo.bootstrap.Card} move_card the card being dropped?
2009          * @param {String} position 'above' or 'below'
2010          * @param {Roo.bootstrap.Card} next_to_card What card position is relative to of 'false' for empty list.
2011         
2012          */
2013         'drop' : true,
2014          /**
2015          * @event rotate
2016          * When a element a card is rotate
2017          * @param {Roo.bootstrap.Element} this
2018          * @param {Roo.Element} n the node being dropped?
2019          * @param {Boolean} rotate status
2020          */
2021         'rotate' : true
2022         
2023     });
2024 };
2025
2026
2027 Roo.extend(Roo.bootstrap.Card, Roo.bootstrap.Component,  {
2028     
2029     
2030     weight : '',
2031     
2032     margin: '', /// may be better in component?
2033     margin_top: '', 
2034     margin_bottom: '', 
2035     margin_left: '',
2036     margin_right: '',
2037     margin_x: '',
2038     margin_y: '',
2039     
2040     padding : '',
2041     padding_top: '', 
2042     padding_bottom: '', 
2043     padding_left: '',
2044     padding_right: '',
2045     padding_x: '',
2046     padding_y: '',
2047     
2048     display: '', 
2049     display_xs: '', 
2050     display_sm: '', 
2051     display_lg: '',
2052     display_xl: '',
2053  
2054     header_image  : '',
2055     header : '',
2056     header_size : 0,
2057     title : '',
2058     subtitle : '',
2059     html : '',
2060     footer: '',
2061
2062     collapsable : false,
2063     collapsed : false,
2064     rotateable : false,
2065     rotated : false,
2066     
2067     dragable : false,
2068     drag_group : false,
2069     dropable : false,
2070     drop_group : false,
2071     childContainer : false,
2072     dropEl : false, /// the dom placeholde element that indicates drop location.
2073     containerEl: false, // body container
2074     bodyEl: false, // card-body
2075     headerContainerEl : false, //
2076     headerEl : false,
2077     
2078     layoutCls : function()
2079     {
2080         var cls = '';
2081         var t = this;
2082         Roo.log(this.margin_bottom.length);
2083         ['', 'top', 'bottom', 'left', 'right', 'x', 'y' ].forEach(function(v) {
2084             // in theory these can do margin_top : ml-xs-3 ??? but we don't support that yet
2085             
2086             if (('' + t['margin' + (v.length ? '_' : '') + v]).length) {
2087                 cls += ' m' +  (v.length ? v[0]  : '') + '-' +  t['margin' + (v.length ? '_' : '') + v];
2088             }
2089             if (('' + t['padding' + (v.length ? '_' : '') + v]).length) {
2090                 cls += ' p' +  (v.length ? v[0]  : '') + '-' +  t['padding' + (v.length ? '_' : '') + v];
2091             }
2092         });
2093         
2094         ['', 'xs', 'sm', 'lg', 'xl'].forEach(function(v) {
2095             if (('' + t['display' + (v.length ? '_' : '') + v]).length) {
2096                 cls += ' d' +  (v.length ? '-' : '') + v + '-' + t['margin' + (v.length ? '_' : '') + v]
2097             }
2098         });
2099         
2100         // more generic support?
2101         if (this.hidden) {
2102             cls += ' d-none';
2103         }
2104         
2105         return cls;
2106     },
2107  
2108        // Roo.log("Call onRender: " + this.xtype);
2109         /*  We are looking at something like this.
2110 <div class="card">
2111     <img src="..." class="card-img-top" alt="...">
2112     <div class="card-body">
2113         <h5 class="card-title">Card title</h5>
2114          <h6 class="card-subtitle mb-2 text-muted">Card subtitle</h6>
2115
2116         >> this bit is really the body...
2117         <div> << we will ad dthis in hopefully it will not break shit.
2118         
2119         ** card text does not actually have any styling...
2120         
2121             <p class="card-text">This is a wider card with supporting text below as a natural lead-in to additional content. This content is a little bit longer.</p>
2122         
2123         </div> <<
2124           <a href="#" class="card-link">Card link</a>
2125           
2126     </div>
2127     <div class="card-footer">
2128         <small class="text-muted">Last updated 3 mins ago</small>
2129     </div>
2130 </div>
2131          */
2132     getAutoCreate : function(){
2133         
2134         var cfg = {
2135             tag : 'div',
2136             cls : 'card',
2137             cn : [ ]
2138         };
2139         
2140         if (this.weight.length && this.weight != 'light') {
2141             cfg.cls += ' text-white';
2142         } else {
2143             cfg.cls += ' text-dark'; // need as it's nested..
2144         }
2145         if (this.weight.length) {
2146             cfg.cls += ' bg-' + this.weight;
2147         }
2148         
2149         cfg.cls += this.layoutCls(); 
2150         
2151         var hdr = false;
2152         var hdr_ctr = false;
2153         if (this.header.length) {
2154             hdr = {
2155                 tag : this.header_size > 0 ? 'h' + this.header_size : 'div',
2156                 cls : 'card-header',
2157                 cn : []
2158             };
2159             cfg.cn.push(hdr);
2160             hdr_ctr = hdr;
2161         } else {
2162             hdr = {
2163                 tag : 'div',
2164                 cls : 'card-header d-none',
2165                 cn : []
2166             };
2167             cfg.cn.push(hdr);
2168             hdr_ctr = hdr;
2169         }
2170         if (this.collapsable) {
2171             hdr_ctr = {
2172             tag : 'a',
2173             cls : 'd-block user-select-none',
2174             cn: [
2175                     {
2176                         tag: 'i',
2177                         cls : 'roo-collapse-toggle fa fa-chevron-down float-right ' + (this.collapsed ? 'collapsed' : '')
2178                     }
2179                    
2180                 ]
2181             };
2182             hdr.cn.push(hdr_ctr);
2183         }
2184         
2185         hdr_ctr.cn.push(        {
2186             tag: 'span',
2187             cls: 'roo-card-header-ctr' + ( this.header.length ? '' : ' d-none'),
2188             html : this.header
2189         });
2190         
2191         
2192         if (this.header_image.length) {
2193             cfg.cn.push({
2194                 tag : 'img',
2195                 cls : 'card-img-top',
2196                 src: this.header_image // escape?
2197             });
2198         } else {
2199             cfg.cn.push({
2200                     tag : 'div',
2201                     cls : 'card-img-top d-none' 
2202                 });
2203         }
2204             
2205         var body = {
2206             tag : 'div',
2207             cls : 'card-body' + (this.html === false  ? ' d-none' : ''),
2208             cn : []
2209         };
2210         var obody = body;
2211         if (this.collapsable || this.rotateable) {
2212             obody = {
2213                 tag: 'div',
2214                 cls : 'roo-collapsable collapse ' + (this.collapsed || this.rotated ? '' : 'show'),
2215                 cn : [  body ]
2216             };
2217         }
2218         
2219         cfg.cn.push(obody);
2220         
2221         if (this.title.length) {
2222             body.cn.push({
2223                 tag : 'div',
2224                 cls : 'card-title',
2225                 src: this.title // escape?
2226             });
2227         }  
2228         
2229         if (this.subtitle.length) {
2230             body.cn.push({
2231                 tag : 'div',
2232                 cls : 'card-title',
2233                 src: this.subtitle // escape?
2234             });
2235         }
2236         
2237         body.cn.push({
2238             tag : 'div',
2239             cls : 'roo-card-body-ctr'
2240         });
2241         
2242         if (this.html.length) {
2243             body.cn.push({
2244                 tag: 'div',
2245                 html : this.html
2246             });
2247         }
2248         // fixme ? handle objects?
2249         
2250         if (this.footer.length) {
2251            
2252             cfg.cn.push({
2253                 cls : 'card-footer ' + (this.rotated ? 'd-none' : ''),
2254                 html : this.footer
2255             });
2256             
2257         } else {
2258             cfg.cn.push({cls : 'card-footer d-none'});
2259         }
2260         
2261         // footer...
2262         
2263         return cfg;
2264     },
2265     
2266     
2267     getCardHeader : function()
2268     {
2269         var  ret = this.el.select('.card-header',true).first();
2270         if (ret.hasClass('d-none')) {
2271             ret.removeClass('d-none');
2272         }
2273         
2274         return ret;
2275     },
2276     getCardFooter : function()
2277     {
2278         var  ret = this.el.select('.card-footer',true).first();
2279         if (ret.hasClass('d-none')) {
2280             ret.removeClass('d-none');
2281         }
2282         
2283         return ret;
2284     },
2285     getCardImageTop : function()
2286     {
2287         var  ret = this.el.select('.card-img-top',true).first();
2288         if (ret.hasClass('d-none')) {
2289             ret.removeClass('d-none');
2290         }
2291             
2292         return ret;
2293     },
2294     
2295     getChildContainer : function()
2296     {
2297         
2298         if(!this.el){
2299             return false;
2300         }
2301         return this.el.select('.roo-card-body-ctr',true).first();    
2302     },
2303     
2304     initEvents: function() 
2305     {
2306         this.bodyEl = this.el.select('.card-body',true).first(); 
2307         this.containerEl = this.getChildContainer();
2308         if(this.dragable){
2309             this.dragZone = new Roo.dd.DragZone(this.getEl(), {
2310                     containerScroll: true,
2311                     ddGroup: this.drag_group || 'default_card_drag_group'
2312             });
2313             this.dragZone.getDragData = this.getDragData.createDelegate(this);
2314         }
2315         if (this.dropable) {
2316             this.dropZone = new Roo.dd.DropZone(this.el.select('.card-body',true).first() , {
2317                 containerScroll: true,
2318                 ddGroup: this.drop_group || 'default_card_drag_group'
2319             });
2320             this.dropZone.getTargetFromEvent = this.getTargetFromEvent.createDelegate(this);
2321             this.dropZone.onNodeEnter = this.onNodeEnter.createDelegate(this);
2322             this.dropZone.onNodeOver = this.onNodeOver.createDelegate(this);
2323             this.dropZone.onNodeOut = this.onNodeOut.createDelegate(this);
2324             this.dropZone.onNodeDrop = this.onNodeDrop.createDelegate(this);
2325         }
2326         
2327         if (this.collapsable) {
2328             this.el.select('.card-header',true).on('click', this.onToggleCollapse, this);
2329         }
2330         if (this.rotateable) {
2331             this.el.select('.card-header',true).on('click', this.onToggleRotate, this);
2332         }
2333         this.collapsableEl = this.el.select('.roo-collapsable').first();
2334          
2335         this.footerEl = this.el.select('.card-footer').first();
2336         this.collapsableToggleEl = this.el.select('.roo-collapse-toggle');
2337         this.headerContainerEl = this.el.select('.roo-card-header-ctr').first();
2338         this.headerEl = this.el.select('.card-header',true).first();
2339         
2340         if (this.rotated) {
2341             this.el.addClass('roo-card-rotated');
2342             this.fireEvent('rotate', this, true);
2343         }
2344         
2345     },
2346     getDragData : function(e)
2347     {
2348         var target = this.getEl();
2349         if (target) {
2350             //this.handleSelection(e);
2351             
2352             var dragData = {
2353                 source: this,
2354                 copy: false,
2355                 nodes: this.getEl(),
2356                 records: []
2357             };
2358             
2359             
2360             dragData.ddel = target.dom ;    // the div element
2361             Roo.log(target.getWidth( ));
2362             dragData.ddel.style.width = target.getWidth() + 'px';
2363             
2364             return dragData;
2365         }
2366         return false;
2367     },
2368     /**
2369     *    Part of the Roo.dd.DropZone interface. If no target node is found, the
2370     *    whole Element becomes the target, and this causes the drop gesture to append.
2371     */
2372     getTargetFromEvent : function(e, dragged_card_el)
2373     {
2374         var target = e.getTarget();
2375         while ((target !== null) && (target.parentNode != this.containerEl.dom)) {
2376             target = target.parentNode;
2377         }
2378         
2379         var ret = {
2380             position: '',
2381             cards : [],
2382             card_n : -1,
2383             items_n : -1,
2384             card : false 
2385         };
2386         
2387         //Roo.log([ 'target' , target ? target.id : '--nothing--']);
2388         // see if target is one of the 'cards'...
2389         
2390         
2391         //Roo.log(this.items.length);
2392         var pos = false;
2393         
2394         var last_card_n = 0;
2395         var cards_len  = 0;
2396         for (var i = 0;i< this.items.length;i++) {
2397             
2398             if (!this.items[i].el.hasClass('card')) {
2399                  continue;
2400             }
2401             pos = this.getDropPoint(e, this.items[i].el.dom);
2402             
2403             cards_len = ret.cards.length;
2404             //Roo.log(this.items[i].el.dom.id);
2405             ret.cards.push(this.items[i]);
2406             last_card_n  = i;
2407             if (ret.card_n < 0 && pos == 'above') {
2408                 ret.position = cards_len > 0 ? 'below' : pos;
2409                 ret.items_n = i > 0 ? i - 1 : 0;
2410                 ret.card_n  = cards_len  > 0 ? cards_len - 1 : 0;
2411                 ret.card = ret.cards[ret.card_n];
2412             }
2413         }
2414         if (!ret.cards.length) {
2415             ret.card = true;
2416             ret.position = 'below';
2417             ret.items_n;
2418             return ret;
2419         }
2420         // could not find a card.. stick it at the end..
2421         if (ret.card_n < 0) {
2422             ret.card_n = last_card_n;
2423             ret.card = ret.cards[last_card_n];
2424             ret.items_n = this.items.indexOf(ret.cards[last_card_n]);
2425             ret.position = 'below';
2426         }
2427         
2428         if (this.items[ret.items_n].el == dragged_card_el) {
2429             return false;
2430         }
2431         
2432         if (ret.position == 'below') {
2433             var card_after = ret.card_n+1 == ret.cards.length ? false : ret.cards[ret.card_n+1];
2434             
2435             if (card_after  && card_after.el == dragged_card_el) {
2436                 return false;
2437             }
2438             return ret;
2439         }
2440         
2441         // its's after ..
2442         var card_before = ret.card_n > 0 ? ret.cards[ret.card_n-1] : false;
2443         
2444         if (card_before  && card_before.el == dragged_card_el) {
2445             return false;
2446         }
2447         
2448         return ret;
2449     },
2450     
2451     onNodeEnter : function(n, dd, e, data){
2452         return false;
2453     },
2454     onNodeOver : function(n, dd, e, data)
2455     {
2456        
2457         var target_info = this.getTargetFromEvent(e,data.source.el);
2458         if (target_info === false) {
2459             this.dropPlaceHolder('hide');
2460             return false;
2461         }
2462         Roo.log(['getTargetFromEvent', target_info ]);
2463         
2464          
2465         this.dropPlaceHolder('show', target_info,data);
2466         
2467         return false; 
2468     },
2469     onNodeOut : function(n, dd, e, data){
2470         this.dropPlaceHolder('hide');
2471      
2472     },
2473     onNodeDrop : function(n, dd, e, data)
2474     {
2475         
2476         // call drop - return false if
2477         
2478         // this could actually fail - if the Network drops..
2479         // we will ignore this at present..- client should probably reload
2480         // the whole set of cards if stuff like that fails.
2481         
2482         
2483         var info = this.getTargetFromEvent(e,data.source.el);
2484         if (info === false) {
2485             return false;
2486         }
2487         this.dropPlaceHolder('hide');
2488   
2489          
2490     
2491     
2492     
2493         this.acceptCard(data.source, info.position, info.card, info.items_n);
2494         return true;
2495          
2496     },
2497     firstChildCard : function()
2498     {
2499         for (var i = 0;i< this.items.length;i++) {
2500             
2501             if (!this.items[i].el.hasClass('card')) {
2502                  continue;
2503             }
2504             return this.items[i];
2505         }
2506         return this.items.length ? this.items[this.items.length-1] : false; // don't try and put stuff after the cards...
2507     },
2508     /**
2509      * accept card
2510      *
2511      * -        card.acceptCard(move_card, info.position, info.card, info.items_n);
2512      */
2513     acceptCard : function(move_card,  position, next_to_card )
2514     {
2515         if (this.fireEvent("drop", this, move_card, position, next_to_card) === false) {
2516             return false;
2517         }
2518         
2519         var to_items_n = next_to_card ? this.items.indexOf(next_to_card) : 0;
2520         
2521         
2522         var dom = move_card.el.dom;
2523         dom.parentNode.removeChild(dom);
2524         dom.style.width = ''; // clear with - which is set by drag.
2525         
2526         if (next_to_card !== false && next_to_card !== true && next_to_card.el.dom.parentNode) {
2527             var cardel = next_to_card.el.dom;
2528             
2529             if (position == 'above' ) {
2530                 cardel.parentNode.insertBefore(dom, cardel);
2531             } else if (cardel.nextSibling) {
2532                 cardel.parentNode.insertBefore(dom,cardel.nextSibling);
2533             } else {
2534                 cardel.parentNode.append(dom);
2535             }
2536         } else {
2537             // card container???
2538             this.containerEl.dom.append(dom);
2539         }
2540         
2541         //FIXME HANDLE card = true 
2542         
2543         // add this to the correct place in items.
2544         
2545         
2546         
2547         // remove Card from items.
2548         
2549         var old_parent = move_card.parent();
2550         
2551         old_parent.items = old_parent.items.filter(function(e) { return e != move_card });
2552         
2553         if (this.items.length) {
2554             var nitems = [];
2555             //Roo.log([info.items_n, info.position, this.items.length]);
2556             for (var i =0; i < this.items.length; i++) {
2557                 if (i == to_items_n && position == 'above') {
2558                     nitems.push(move_card);
2559                 }
2560                 nitems.push(this.items[i]);
2561                 if (i == to_items_n && position == 'below') {
2562                     nitems.push(move_card);
2563                 }
2564             }
2565             this.items = nitems;
2566             Roo.log(this.items);
2567         } else {
2568             this.items.push(move_card);
2569         }
2570         
2571         move_card.parentId = this.id;
2572         
2573         return true;
2574         
2575         
2576     },
2577     
2578     
2579     /**    Decide whether to drop above or below a View node. */
2580     getDropPoint : function(e, n, dd)
2581     {
2582         if (dd) {
2583              return false;
2584         }
2585         if (n == this.containerEl.dom) {
2586             return "above";
2587         }
2588         var t = Roo.lib.Dom.getY(n), b = t + n.offsetHeight;
2589         var c = t + (b - t) / 2;
2590         var y = Roo.lib.Event.getPageY(e);
2591         if(y <= c) {
2592             return "above";
2593         }else{
2594             return "below";
2595         }
2596     },
2597     onToggleCollapse : function(e)
2598         {
2599         if (this.collapsed) {
2600             this.el.select('.roo-collapse-toggle').removeClass('collapsed');
2601             this.collapsableEl.addClass('show');
2602             this.collapsed = false;
2603             return;
2604         }
2605         this.el.select('.roo-collapse-toggle').addClass('collapsed');
2606         this.collapsableEl.removeClass('show');
2607         this.collapsed = true;
2608         
2609     
2610     },
2611     
2612     onToggleRotate : function(e)
2613     {
2614         this.collapsableEl.removeClass('show');
2615         this.footerEl.removeClass('d-none');
2616         this.el.removeClass('roo-card-rotated');
2617         this.el.removeClass('d-none');
2618         if (this.rotated) {
2619             
2620             this.collapsableEl.addClass('show');
2621             this.rotated = false;
2622             this.fireEvent('rotate', this, this.rotated);
2623             return;
2624         }
2625         this.el.addClass('roo-card-rotated');
2626         this.footerEl.addClass('d-none');
2627         this.el.select('.roo-collapsable').removeClass('show');
2628         
2629         this.rotated = true;
2630         this.fireEvent('rotate', this, this.rotated);
2631     
2632     },
2633     
2634     dropPlaceHolder: function (action, info, data)
2635     {
2636         if (this.dropEl === false) {
2637             this.dropEl = Roo.DomHelper.append(this.containerEl, {
2638             cls : 'd-none'
2639             },true);
2640         }
2641         this.dropEl.removeClass(['d-none', 'd-block']);        
2642         if (action == 'hide') {
2643             
2644             this.dropEl.addClass('d-none');
2645             return;
2646         }
2647         // FIXME - info.card == true!!!
2648         this.dropEl.dom.parentNode.removeChild(this.dropEl.dom);
2649         
2650         if (info.card !== true) {
2651             var cardel = info.card.el.dom;
2652             
2653             if (info.position == 'above') {
2654                 cardel.parentNode.insertBefore(this.dropEl.dom, cardel);
2655             } else if (cardel.nextSibling) {
2656                 cardel.parentNode.insertBefore(this.dropEl.dom,cardel.nextSibling);
2657             } else {
2658                 cardel.parentNode.append(this.dropEl.dom);
2659             }
2660         } else {
2661             // card container???
2662             this.containerEl.dom.append(this.dropEl.dom);
2663         }
2664         
2665         this.dropEl.addClass('d-block roo-card-dropzone');
2666         
2667         this.dropEl.setHeight( Roo.get(data.ddel).getHeight() );
2668         
2669         
2670     
2671     
2672     
2673     },
2674     setHeaderText: function(html)
2675     {
2676         this.headerContainerEl.dom.innerHTML = html;
2677     }
2678
2679     
2680 });
2681
2682 /*
2683  * - LGPL
2684  *
2685  * Card header - holder for the card header elements.
2686  * 
2687  */
2688
2689 /**
2690  * @class Roo.bootstrap.CardHeader
2691  * @extends Roo.bootstrap.Element
2692  * Bootstrap CardHeader class
2693  * @constructor
2694  * Create a new Card Header - that you can embed children into
2695  * @param {Object} config The config object
2696  */
2697
2698 Roo.bootstrap.CardHeader = function(config){
2699     Roo.bootstrap.CardHeader.superclass.constructor.call(this, config);
2700 };
2701
2702 Roo.extend(Roo.bootstrap.CardHeader, Roo.bootstrap.Element,  {
2703     
2704     
2705     container_method : 'getCardHeader' 
2706     
2707      
2708     
2709     
2710    
2711 });
2712
2713  
2714
2715  /*
2716  * - LGPL
2717  *
2718  * Card footer - holder for the card footer elements.
2719  * 
2720  */
2721
2722 /**
2723  * @class Roo.bootstrap.CardFooter
2724  * @extends Roo.bootstrap.Element
2725  * Bootstrap CardFooter class
2726  * @constructor
2727  * Create a new Card Footer - that you can embed children into
2728  * @param {Object} config The config object
2729  */
2730
2731 Roo.bootstrap.CardFooter = function(config){
2732     Roo.bootstrap.CardFooter.superclass.constructor.call(this, config);
2733 };
2734
2735 Roo.extend(Roo.bootstrap.CardFooter, Roo.bootstrap.Element,  {
2736     
2737     
2738     container_method : 'getCardFooter' 
2739     
2740      
2741     
2742     
2743    
2744 });
2745
2746  
2747
2748  /*
2749  * - LGPL
2750  *
2751  * Card header - holder for the card header elements.
2752  * 
2753  */
2754
2755 /**
2756  * @class Roo.bootstrap.CardImageTop
2757  * @extends Roo.bootstrap.Element
2758  * Bootstrap CardImageTop class
2759  * @constructor
2760  * Create a new Card Image Top container
2761  * @param {Object} config The config object
2762  */
2763
2764 Roo.bootstrap.CardImageTop = function(config){
2765     Roo.bootstrap.CardImageTop.superclass.constructor.call(this, config);
2766 };
2767
2768 Roo.extend(Roo.bootstrap.CardImageTop, Roo.bootstrap.Element,  {
2769     
2770    
2771     container_method : 'getCardImageTop' 
2772     
2773      
2774     
2775    
2776 });
2777
2778  
2779
2780  /*
2781  * - LGPL
2782  *
2783  * image
2784  * 
2785  */
2786
2787
2788 /**
2789  * @class Roo.bootstrap.Img
2790  * @extends Roo.bootstrap.Component
2791  * Bootstrap Img class
2792  * @cfg {Boolean} imgResponsive false | true
2793  * @cfg {String} border rounded | circle | thumbnail
2794  * @cfg {String} src image source
2795  * @cfg {String} alt image alternative text
2796  * @cfg {String} href a tag href
2797  * @cfg {String} target (_self|_blank|_parent|_top)target for a href.
2798  * @cfg {String} xsUrl xs image source
2799  * @cfg {String} smUrl sm image source
2800  * @cfg {String} mdUrl md image source
2801  * @cfg {String} lgUrl lg image source
2802  * 
2803  * @constructor
2804  * Create a new Input
2805  * @param {Object} config The config object
2806  */
2807
2808 Roo.bootstrap.Img = function(config){
2809     Roo.bootstrap.Img.superclass.constructor.call(this, config);
2810     
2811     this.addEvents({
2812         // img events
2813         /**
2814          * @event click
2815          * The img click event for the img.
2816          * @param {Roo.EventObject} e
2817          */
2818         "click" : true
2819     });
2820 };
2821
2822 Roo.extend(Roo.bootstrap.Img, Roo.bootstrap.Component,  {
2823     
2824     imgResponsive: true,
2825     border: '',
2826     src: 'about:blank',
2827     href: false,
2828     target: false,
2829     xsUrl: '',
2830     smUrl: '',
2831     mdUrl: '',
2832     lgUrl: '',
2833
2834     getAutoCreate : function()
2835     {   
2836         if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
2837             return this.createSingleImg();
2838         }
2839         
2840         var cfg = {
2841             tag: 'div',
2842             cls: 'roo-image-responsive-group',
2843             cn: []
2844         };
2845         var _this = this;
2846         
2847         Roo.each(['xs', 'sm', 'md', 'lg'], function(size){
2848             
2849             if(!_this[size + 'Url']){
2850                 return;
2851             }
2852             
2853             var img = {
2854                 tag: 'img',
2855                 cls: (_this.imgResponsive) ? 'img-responsive' : '',
2856                 html: _this.html || cfg.html,
2857                 src: _this[size + 'Url']
2858             };
2859             
2860             img.cls += ' roo-image-responsive-' + size;
2861             
2862             var s = ['xs', 'sm', 'md', 'lg'];
2863             
2864             s.splice(s.indexOf(size), 1);
2865             
2866             Roo.each(s, function(ss){
2867                 img.cls += ' hidden-' + ss;
2868             });
2869             
2870             if (['rounded','circle','thumbnail'].indexOf(_this.border)>-1) {
2871                 cfg.cls += ' img-' + _this.border;
2872             }
2873             
2874             if(_this.alt){
2875                 cfg.alt = _this.alt;
2876             }
2877             
2878             if(_this.href){
2879                 var a = {
2880                     tag: 'a',
2881                     href: _this.href,
2882                     cn: [
2883                         img
2884                     ]
2885                 };
2886
2887                 if(this.target){
2888                     a.target = _this.target;
2889                 }
2890             }
2891             
2892             cfg.cn.push((_this.href) ? a : img);
2893             
2894         });
2895         
2896         return cfg;
2897     },
2898     
2899     createSingleImg : function()
2900     {
2901         var cfg = {
2902             tag: 'img',
2903             cls: (this.imgResponsive) ? 'img-responsive' : '',
2904             html : null,
2905             src : 'about:blank'  // just incase src get's set to undefined?!?
2906         };
2907         
2908         cfg.html = this.html || cfg.html;
2909         
2910         cfg.src = this.src || cfg.src;
2911         
2912         if (['rounded','circle','thumbnail'].indexOf(this.border)>-1) {
2913             cfg.cls += ' img-' + this.border;
2914         }
2915         
2916         if(this.alt){
2917             cfg.alt = this.alt;
2918         }
2919         
2920         if(this.href){
2921             var a = {
2922                 tag: 'a',
2923                 href: this.href,
2924                 cn: [
2925                     cfg
2926                 ]
2927             };
2928             
2929             if(this.target){
2930                 a.target = this.target;
2931             }
2932             
2933         }
2934         
2935         return (this.href) ? a : cfg;
2936     },
2937     
2938     initEvents: function() 
2939     {
2940         if(!this.href){
2941             this.el.on('click', this.onClick, this);
2942         }
2943         
2944     },
2945     
2946     onClick : function(e)
2947     {
2948         Roo.log('img onclick');
2949         this.fireEvent('click', this, e);
2950     },
2951     /**
2952      * Sets the url of the image - used to update it
2953      * @param {String} url the url of the image
2954      */
2955     
2956     setSrc : function(url)
2957     {
2958         this.src =  url;
2959         
2960         if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
2961             this.el.dom.src =  url;
2962             return;
2963         }
2964         
2965         this.el.select('img', true).first().dom.src =  url;
2966     }
2967     
2968     
2969    
2970 });
2971
2972  /*
2973  * - LGPL
2974  *
2975  * image
2976  * 
2977  */
2978
2979
2980 /**
2981  * @class Roo.bootstrap.Link
2982  * @extends Roo.bootstrap.Component
2983  * Bootstrap Link Class
2984  * @cfg {String} alt image alternative text
2985  * @cfg {String} href a tag href
2986  * @cfg {String} target (_self|_blank|_parent|_top) target for a href.
2987  * @cfg {String} html the content of the link.
2988  * @cfg {String} anchor name for the anchor link
2989  * @cfg {String} fa - favicon
2990
2991  * @cfg {Boolean} preventDefault (true | false) default false
2992
2993  * 
2994  * @constructor
2995  * Create a new Input
2996  * @param {Object} config The config object
2997  */
2998
2999 Roo.bootstrap.Link = function(config){
3000     Roo.bootstrap.Link.superclass.constructor.call(this, config);
3001     
3002     this.addEvents({
3003         // img events
3004         /**
3005          * @event click
3006          * The img click event for the img.
3007          * @param {Roo.EventObject} e
3008          */
3009         "click" : true
3010     });
3011 };
3012
3013 Roo.extend(Roo.bootstrap.Link, Roo.bootstrap.Component,  {
3014     
3015     href: false,
3016     target: false,
3017     preventDefault: false,
3018     anchor : false,
3019     alt : false,
3020     fa: false,
3021
3022
3023     getAutoCreate : function()
3024     {
3025         var html = this.html || '';
3026         
3027         if (this.fa !== false) {
3028             html = '<i class="fa fa-' + this.fa + '"></i>';
3029         }
3030         var cfg = {
3031             tag: 'a'
3032         };
3033         // anchor's do not require html/href...
3034         if (this.anchor === false) {
3035             cfg.html = html;
3036             cfg.href = this.href || '#';
3037         } else {
3038             cfg.name = this.anchor;
3039             if (this.html !== false || this.fa !== false) {
3040                 cfg.html = html;
3041             }
3042             if (this.href !== false) {
3043                 cfg.href = this.href;
3044             }
3045         }
3046         
3047         if(this.alt !== false){
3048             cfg.alt = this.alt;
3049         }
3050         
3051         
3052         if(this.target !== false) {
3053             cfg.target = this.target;
3054         }
3055         
3056         return cfg;
3057     },
3058     
3059     initEvents: function() {
3060         
3061         if(!this.href || this.preventDefault){
3062             this.el.on('click', this.onClick, this);
3063         }
3064     },
3065     
3066     onClick : function(e)
3067     {
3068         if(this.preventDefault){
3069             e.preventDefault();
3070         }
3071         //Roo.log('img onclick');
3072         this.fireEvent('click', this, e);
3073     }
3074    
3075 });
3076
3077  /*
3078  * - LGPL
3079  *
3080  * header
3081  * 
3082  */
3083
3084 /**
3085  * @class Roo.bootstrap.Header
3086  * @extends Roo.bootstrap.Component
3087  * Bootstrap Header class
3088  * @cfg {String} html content of header
3089  * @cfg {Number} level (1|2|3|4|5|6) default 1
3090  * 
3091  * @constructor
3092  * Create a new Header
3093  * @param {Object} config The config object
3094  */
3095
3096
3097 Roo.bootstrap.Header  = function(config){
3098     Roo.bootstrap.Header.superclass.constructor.call(this, config);
3099 };
3100
3101 Roo.extend(Roo.bootstrap.Header, Roo.bootstrap.Component,  {
3102     
3103     //href : false,
3104     html : false,
3105     level : 1,
3106     
3107     
3108     
3109     getAutoCreate : function(){
3110         
3111         
3112         
3113         var cfg = {
3114             tag: 'h' + (1 *this.level),
3115             html: this.html || ''
3116         } ;
3117         
3118         return cfg;
3119     }
3120    
3121 });
3122
3123  
3124
3125  /*
3126  * Based on:
3127  * Ext JS Library 1.1.1
3128  * Copyright(c) 2006-2007, Ext JS, LLC.
3129  *
3130  * Originally Released Under LGPL - original licence link has changed is not relivant.
3131  *
3132  * Fork - LGPL
3133  * <script type="text/javascript">
3134  */
3135  
3136 /**
3137  * @class Roo.bootstrap.MenuMgr
3138  * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
3139  * @singleton
3140  */
3141 Roo.bootstrap.MenuMgr = function(){
3142    var menus, active, groups = {}, attached = false, lastShow = new Date();
3143
3144    // private - called when first menu is created
3145    function init(){
3146        menus = {};
3147        active = new Roo.util.MixedCollection();
3148        Roo.get(document).addKeyListener(27, function(){
3149            if(active.length > 0){
3150                hideAll();
3151            }
3152        });
3153    }
3154
3155    // private
3156    function hideAll(){
3157        if(active && active.length > 0){
3158            var c = active.clone();
3159            c.each(function(m){
3160                m.hide();
3161            });
3162        }
3163    }
3164
3165    // private
3166    function onHide(m){
3167        active.remove(m);
3168        if(active.length < 1){
3169            Roo.get(document).un("mouseup", onMouseDown);
3170             
3171            attached = false;
3172        }
3173    }
3174
3175    // private
3176    function onShow(m){
3177        var last = active.last();
3178        lastShow = new Date();
3179        active.add(m);
3180        if(!attached){
3181           Roo.get(document).on("mouseup", onMouseDown);
3182            
3183            attached = true;
3184        }
3185        if(m.parentMenu){
3186           //m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
3187           m.parentMenu.activeChild = m;
3188        }else if(last && last.isVisible()){
3189           //m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
3190        }
3191    }
3192
3193    // private
3194    function onBeforeHide(m){
3195        if(m.activeChild){
3196            m.activeChild.hide();
3197        }
3198        if(m.autoHideTimer){
3199            clearTimeout(m.autoHideTimer);
3200            delete m.autoHideTimer;
3201        }
3202    }
3203
3204    // private
3205    function onBeforeShow(m){
3206        var pm = m.parentMenu;
3207        if(!pm && !m.allowOtherMenus){
3208            hideAll();
3209        }else if(pm && pm.activeChild && active != m){
3210            pm.activeChild.hide();
3211        }
3212    }
3213
3214    // private this should really trigger on mouseup..
3215    function onMouseDown(e){
3216         Roo.log("on Mouse Up");
3217         
3218         if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".dropdown-menu") && !e.getTarget('.user-menu')){
3219             Roo.log("MenuManager hideAll");
3220             hideAll();
3221             e.stopEvent();
3222         }
3223         
3224         
3225    }
3226
3227    // private
3228    function onBeforeCheck(mi, state){
3229        if(state){
3230            var g = groups[mi.group];
3231            for(var i = 0, l = g.length; i < l; i++){
3232                if(g[i] != mi){
3233                    g[i].setChecked(false);
3234                }
3235            }
3236        }
3237    }
3238
3239    return {
3240
3241        /**
3242         * Hides all menus that are currently visible
3243         */
3244        hideAll : function(){
3245             hideAll();  
3246        },
3247
3248        // private
3249        register : function(menu){
3250            if(!menus){
3251                init();
3252            }
3253            menus[menu.id] = menu;
3254            menu.on("beforehide", onBeforeHide);
3255            menu.on("hide", onHide);
3256            menu.on("beforeshow", onBeforeShow);
3257            menu.on("show", onShow);
3258            var g = menu.group;
3259            if(g && menu.events["checkchange"]){
3260                if(!groups[g]){
3261                    groups[g] = [];
3262                }
3263                groups[g].push(menu);
3264                menu.on("checkchange", onCheck);
3265            }
3266        },
3267
3268         /**
3269          * Returns a {@link Roo.menu.Menu} object
3270          * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
3271          * be used to generate and return a new Menu instance.
3272          */
3273        get : function(menu){
3274            if(typeof menu == "string"){ // menu id
3275                return menus[menu];
3276            }else if(menu.events){  // menu instance
3277                return menu;
3278            }
3279            /*else if(typeof menu.length == 'number'){ // array of menu items?
3280                return new Roo.bootstrap.Menu({items:menu});
3281            }else{ // otherwise, must be a config
3282                return new Roo.bootstrap.Menu(menu);
3283            }
3284            */
3285            return false;
3286        },
3287
3288        // private
3289        unregister : function(menu){
3290            delete menus[menu.id];
3291            menu.un("beforehide", onBeforeHide);
3292            menu.un("hide", onHide);
3293            menu.un("beforeshow", onBeforeShow);
3294            menu.un("show", onShow);
3295            var g = menu.group;
3296            if(g && menu.events["checkchange"]){
3297                groups[g].remove(menu);
3298                menu.un("checkchange", onCheck);
3299            }
3300        },
3301
3302        // private
3303        registerCheckable : function(menuItem){
3304            var g = menuItem.group;
3305            if(g){
3306                if(!groups[g]){
3307                    groups[g] = [];
3308                }
3309                groups[g].push(menuItem);
3310                menuItem.on("beforecheckchange", onBeforeCheck);
3311            }
3312        },
3313
3314        // private
3315        unregisterCheckable : function(menuItem){
3316            var g = menuItem.group;
3317            if(g){
3318                groups[g].remove(menuItem);
3319                menuItem.un("beforecheckchange", onBeforeCheck);
3320            }
3321        }
3322    };
3323 }();/*
3324  * - LGPL
3325  *
3326  * menu
3327  * 
3328  */
3329
3330 /**
3331  * @class Roo.bootstrap.Menu
3332  * @extends Roo.bootstrap.Component
3333  * Bootstrap Menu class - container for MenuItems
3334  * @cfg {String} type (dropdown|treeview|submenu) type of menu
3335  * @cfg {bool} hidden  if the menu should be hidden when rendered.
3336  * @cfg {bool} stopEvent (true|false)  Stop event after trigger press (default true)
3337  * @cfg {bool} isLink (true|false)  the menu has link disable auto expand and collaspe (default false)
3338  * 
3339  * @constructor
3340  * Create a new Menu
3341  * @param {Object} config The config object
3342  */
3343
3344
3345 Roo.bootstrap.Menu = function(config){
3346     Roo.bootstrap.Menu.superclass.constructor.call(this, config);
3347     if (this.registerMenu && this.type != 'treeview')  {
3348         Roo.bootstrap.MenuMgr.register(this);
3349     }
3350     
3351     
3352     this.addEvents({
3353         /**
3354          * @event beforeshow
3355          * Fires before this menu is displayed (return false to block)
3356          * @param {Roo.menu.Menu} this
3357          */
3358         beforeshow : true,
3359         /**
3360          * @event beforehide
3361          * Fires before this menu is hidden (return false to block)
3362          * @param {Roo.menu.Menu} this
3363          */
3364         beforehide : true,
3365         /**
3366          * @event show
3367          * Fires after this menu is displayed
3368          * @param {Roo.menu.Menu} this
3369          */
3370         show : true,
3371         /**
3372          * @event hide
3373          * Fires after this menu is hidden
3374          * @param {Roo.menu.Menu} this
3375          */
3376         hide : true,
3377         /**
3378          * @event click
3379          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
3380          * @param {Roo.menu.Menu} this
3381          * @param {Roo.menu.Item} menuItem The menu item that was clicked
3382          * @param {Roo.EventObject} e
3383          */
3384         click : true,
3385         /**
3386          * @event mouseover
3387          * Fires when the mouse is hovering over this menu
3388          * @param {Roo.menu.Menu} this
3389          * @param {Roo.EventObject} e
3390          * @param {Roo.menu.Item} menuItem The menu item that was clicked
3391          */
3392         mouseover : true,
3393         /**
3394          * @event mouseout
3395          * Fires when the mouse exits this menu
3396          * @param {Roo.menu.Menu} this
3397          * @param {Roo.EventObject} e
3398          * @param {Roo.menu.Item} menuItem The menu item that was clicked
3399          */
3400         mouseout : true,
3401         /**
3402          * @event itemclick
3403          * Fires when a menu item contained in this menu is clicked
3404          * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
3405          * @param {Roo.EventObject} e
3406          */
3407         itemclick: true
3408     });
3409     this.menuitems = new Roo.util.MixedCollection(false, function(o) { return o.el.id; });
3410 };
3411
3412 Roo.extend(Roo.bootstrap.Menu, Roo.bootstrap.Component,  {
3413     
3414    /// html : false,
3415     //align : '',
3416     triggerEl : false,  // is this set by component builder? -- it should really be fetched from parent()???
3417     type: false,
3418     /**
3419      * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
3420      */
3421     registerMenu : true,
3422     
3423     menuItems :false, // stores the menu items..
3424     
3425     hidden:true,
3426         
3427     parentMenu : false,
3428     
3429     stopEvent : true,
3430     
3431     isLink : false,
3432     
3433     getChildContainer : function() {
3434         return this.el;  
3435     },
3436     
3437     getAutoCreate : function(){
3438          
3439         //if (['right'].indexOf(this.align)!==-1) {
3440         //    cfg.cn[1].cls += ' pull-right'
3441         //}
3442         
3443         
3444         var cfg = {
3445             tag : 'ul',
3446             cls : 'dropdown-menu' ,
3447             style : 'z-index:1000'
3448             
3449         };
3450         
3451         if (this.type === 'submenu') {
3452             cfg.cls = 'submenu active';
3453         }
3454         if (this.type === 'treeview') {
3455             cfg.cls = 'treeview-menu';
3456         }
3457         
3458         return cfg;
3459     },
3460     initEvents : function() {
3461         
3462        // Roo.log("ADD event");
3463        // Roo.log(this.triggerEl.dom);
3464         
3465         this.triggerEl.on('click', this.onTriggerClick, this);
3466         
3467         this.triggerEl.on(Roo.isTouch ? 'touchstart' : 'mouseup', this.onTriggerPress, this);
3468         
3469         
3470         if (this.triggerEl.hasClass('nav-item')) {
3471             // dropdown toggle on the 'a' in BS4?
3472             this.triggerEl.select('.nav-link',true).first().addClass('dropdown-toggle');
3473         } else {
3474             this.triggerEl.addClass('dropdown-toggle');
3475         }
3476         if (Roo.isTouch) {
3477             this.el.on('touchstart'  , this.onTouch, this);
3478         }
3479         this.el.on('click' , this.onClick, this);
3480
3481         this.el.on("mouseover", this.onMouseOver, this);
3482         this.el.on("mouseout", this.onMouseOut, this);
3483         
3484     },
3485     
3486     findTargetItem : function(e)
3487     {
3488         var t = e.getTarget(".dropdown-menu-item", this.el,  true);
3489         if(!t){
3490             return false;
3491         }
3492         //Roo.log(t);         Roo.log(t.id);
3493         if(t && t.id){
3494             //Roo.log(this.menuitems);
3495             return this.menuitems.get(t.id);
3496             
3497             //return this.items.get(t.menuItemId);
3498         }
3499         
3500         return false;
3501     },
3502     
3503     onTouch : function(e) 
3504     {
3505         Roo.log("menu.onTouch");
3506         //e.stopEvent(); this make the user popdown broken
3507         this.onClick(e);
3508     },
3509     
3510     onClick : function(e)
3511     {
3512         Roo.log("menu.onClick");
3513         
3514         var t = this.findTargetItem(e);
3515         if(!t || t.isContainer){
3516             return;
3517         }
3518         Roo.log(e);
3519         /*
3520         if (Roo.isTouch && e.type == 'touchstart' && t.menu  && !t.disabled) {
3521             if(t == this.activeItem && t.shouldDeactivate(e)){
3522                 this.activeItem.deactivate();
3523                 delete this.activeItem;
3524                 return;
3525             }
3526             if(t.canActivate){
3527                 this.setActiveItem(t, true);
3528             }
3529             return;
3530             
3531             
3532         }
3533         */
3534        
3535         Roo.log('pass click event');
3536         
3537         t.onClick(e);
3538         
3539         this.fireEvent("click", this, t, e);
3540         
3541         var _this = this;
3542         
3543         if(!t.href.length || t.href == '#'){
3544             (function() { _this.hide(); }).defer(100);
3545         }
3546         
3547     },
3548     
3549     onMouseOver : function(e){
3550         var t  = this.findTargetItem(e);
3551         //Roo.log(t);
3552         //if(t){
3553         //    if(t.canActivate && !t.disabled){
3554         //        this.setActiveItem(t, true);
3555         //    }
3556         //}
3557         
3558         this.fireEvent("mouseover", this, e, t);
3559     },
3560     isVisible : function(){
3561         return !this.hidden;
3562     },
3563     onMouseOut : function(e){
3564         var t  = this.findTargetItem(e);
3565         
3566         //if(t ){
3567         //    if(t == this.activeItem && t.shouldDeactivate(e)){
3568         //        this.activeItem.deactivate();
3569         //        delete this.activeItem;
3570         //    }
3571         //}
3572         this.fireEvent("mouseout", this, e, t);
3573     },
3574     
3575     
3576     /**
3577      * Displays this menu relative to another element
3578      * @param {String/HTMLElement/Roo.Element} element The element to align to
3579      * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
3580      * the element (defaults to this.defaultAlign)
3581      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
3582      */
3583     show : function(el, pos, parentMenu)
3584     {
3585         if (false === this.fireEvent("beforeshow", this)) {
3586             Roo.log("show canceled");
3587             return;
3588         }
3589         this.parentMenu = parentMenu;
3590         if(!this.el){
3591             this.render();
3592         }
3593         
3594         this.showAt(this.el.getAlignToXY(el, pos || this.defaultAlign), parentMenu, false);
3595     },
3596      /**
3597      * Displays this menu at a specific xy position
3598      * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
3599      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
3600      */
3601     showAt : function(xy, parentMenu, /* private: */_e){
3602         this.parentMenu = parentMenu;
3603         if(!this.el){
3604             this.render();
3605         }
3606         if(_e !== false){
3607             this.fireEvent("beforeshow", this);
3608             //xy = this.el.adjustForConstraints(xy);
3609         }
3610         
3611         //this.el.show();
3612         this.hideMenuItems();
3613         this.hidden = false;
3614         this.triggerEl.addClass('open');
3615         this.el.addClass('show');
3616         
3617         // reassign x when hitting right
3618         if(this.el.getWidth() + xy[0] >= Roo.lib.Dom.getViewWidth()){
3619             xy[0] = xy[0] - this.el.getWidth() + this.triggerEl.getWidth();
3620         }
3621         
3622         // reassign y when hitting bottom
3623         if(this.el.getHeight() + xy[1] >= Roo.lib.Dom.getViewHeight()){
3624             xy[1] = xy[1] - this.el.getHeight() - this.triggerEl.getHeight();
3625         }
3626         
3627         // but the list may align on trigger left or trigger top... should it be a properity?
3628         
3629         if(this.el.getStyle('top') != 'auto' && this.el.getStyle('top').slice(-1) != "%"){
3630             this.el.setXY(xy);
3631         }
3632         
3633         this.focus();
3634         this.fireEvent("show", this);
3635     },
3636     
3637     focus : function(){
3638         return;
3639         if(!this.hidden){
3640             this.doFocus.defer(50, this);
3641         }
3642     },
3643
3644     doFocus : function(){
3645         if(!this.hidden){
3646             this.focusEl.focus();
3647         }
3648     },
3649
3650     /**
3651      * Hides this menu and optionally all parent menus
3652      * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
3653      */
3654     hide : function(deep)
3655     {
3656         if (false === this.fireEvent("beforehide", this)) {
3657             Roo.log("hide canceled");
3658             return;
3659         }
3660         this.hideMenuItems();
3661         if(this.el && this.isVisible()){
3662            
3663             if(this.activeItem){
3664                 this.activeItem.deactivate();
3665                 this.activeItem = null;
3666             }
3667             this.triggerEl.removeClass('open');;
3668             this.el.removeClass('show');
3669             this.hidden = true;
3670             this.fireEvent("hide", this);
3671         }
3672         if(deep === true && this.parentMenu){
3673             this.parentMenu.hide(true);
3674         }
3675     },
3676     
3677     onTriggerClick : function(e)
3678     {
3679         Roo.log('trigger click');
3680         
3681         var target = e.getTarget();
3682         
3683         Roo.log(target.nodeName.toLowerCase());
3684         
3685         if(target.nodeName.toLowerCase() === 'i'){
3686             e.preventDefault();
3687         }
3688         
3689     },
3690     
3691     onTriggerPress  : function(e)
3692     {
3693         Roo.log('trigger press');
3694         //Roo.log(e.getTarget());
3695        // Roo.log(this.triggerEl.dom);
3696        
3697         // trigger only occurs on normal menu's -- if it's a treeview or dropdown... do not hide/show..
3698         var pel = Roo.get(e.getTarget());
3699         if (pel.findParent('.dropdown-menu') || pel.findParent('.treeview-menu') ) {
3700             Roo.log('is treeview or dropdown?');
3701             return;
3702         }
3703         
3704         if(e.getTarget().nodeName.toLowerCase() !== 'i' && this.isLink){
3705             return;
3706         }
3707         
3708         if (this.isVisible()) {
3709             Roo.log('hide');
3710             this.hide();
3711         } else {
3712             Roo.log('show');
3713             this.show(this.triggerEl, '?', false);
3714         }
3715         
3716         if(this.stopEvent || e.getTarget().nodeName.toLowerCase() === 'i'){
3717             e.stopEvent();
3718         }
3719         
3720     },
3721        
3722     
3723     hideMenuItems : function()
3724     {
3725         Roo.log("hide Menu Items");
3726         if (!this.el) { 
3727             return;
3728         }
3729         
3730         this.el.select('.open',true).each(function(aa) {
3731             
3732             aa.removeClass('open');
3733          
3734         });
3735     },
3736     addxtypeChild : function (tree, cntr) {
3737         var comp= Roo.bootstrap.Menu.superclass.addxtypeChild.call(this, tree, cntr);
3738           
3739         this.menuitems.add(comp);
3740         return comp;
3741
3742     },
3743     getEl : function()
3744     {
3745         Roo.log(this.el);
3746         return this.el;
3747     },
3748     
3749     clear : function()
3750     {
3751         this.getEl().dom.innerHTML = '';
3752         this.menuitems.clear();
3753     }
3754 });
3755
3756  
3757  /*
3758  * - LGPL
3759  *
3760  * menu item
3761  * 
3762  */
3763
3764
3765 /**
3766  * @class Roo.bootstrap.MenuItem
3767  * @extends Roo.bootstrap.Component
3768  * Bootstrap MenuItem class
3769  * @cfg {String} html the menu label
3770  * @cfg {String} href the link
3771  * @cfg {Boolean} preventDefault do not trigger A href on clicks (default false).
3772  * @cfg {Boolean} isContainer is it a container - just returns a drop down item..
3773  * @cfg {Boolean} active  used on sidebars to highlight active itesm
3774  * @cfg {String} fa favicon to show on left of menu item.
3775  * @cfg {Roo.bootsrap.Menu} menu the child menu.
3776  * 
3777  * 
3778  * @constructor
3779  * Create a new MenuItem
3780  * @param {Object} config The config object
3781  */
3782
3783
3784 Roo.bootstrap.MenuItem = function(config){
3785     Roo.bootstrap.MenuItem.superclass.constructor.call(this, config);
3786     this.addEvents({
3787         // raw events
3788         /**
3789          * @event click
3790          * The raw click event for the entire grid.
3791          * @param {Roo.bootstrap.MenuItem} this
3792          * @param {Roo.EventObject} e
3793          */
3794         "click" : true
3795     });
3796 };
3797
3798 Roo.extend(Roo.bootstrap.MenuItem, Roo.bootstrap.Component,  {
3799     
3800     href : false,
3801     html : false,
3802     preventDefault: false,
3803     isContainer : false,
3804     active : false,
3805     fa: false,
3806     
3807     getAutoCreate : function(){
3808         
3809         if(this.isContainer){
3810             return {
3811                 tag: 'li',
3812                 cls: 'dropdown-menu-item '
3813             };
3814         }
3815         var ctag = {
3816             tag: 'span',
3817             html: 'Link'
3818         };
3819         
3820         var anc = {
3821             tag : 'a',
3822             cls : 'dropdown-item',
3823             href : '#',
3824             cn : [  ]
3825         };
3826         
3827         if (this.fa !== false) {
3828             anc.cn.push({
3829                 tag : 'i',
3830                 cls : 'fa fa-' + this.fa
3831             });
3832         }
3833         
3834         anc.cn.push(ctag);
3835         
3836         
3837         var cfg= {
3838             tag: 'li',
3839             cls: 'dropdown-menu-item',
3840             cn: [ anc ]
3841         };
3842         if (this.parent().type == 'treeview') {
3843             cfg.cls = 'treeview-menu';
3844         }
3845         if (this.active) {
3846             cfg.cls += ' active';
3847         }
3848         
3849         
3850         
3851         anc.href = this.href || cfg.cn[0].href ;
3852         ctag.html = this.html || cfg.cn[0].html ;
3853         return cfg;
3854     },
3855     
3856     initEvents: function()
3857     {
3858         if (this.parent().type == 'treeview') {
3859             this.el.select('a').on('click', this.onClick, this);
3860         }
3861         
3862         if (this.menu) {
3863             this.menu.parentType = this.xtype;
3864             this.menu.triggerEl = this.el;
3865             this.menu = this.addxtype(Roo.apply({}, this.menu));
3866         }
3867         
3868     },
3869     onClick : function(e)
3870     {
3871         Roo.log('item on click ');
3872         
3873         if(this.preventDefault){
3874             e.preventDefault();
3875         }
3876         //this.parent().hideMenuItems();
3877         
3878         this.fireEvent('click', this, e);
3879     },
3880     getEl : function()
3881     {
3882         return this.el;
3883     } 
3884 });
3885
3886  
3887
3888  /*
3889  * - LGPL
3890  *
3891  * menu separator
3892  * 
3893  */
3894
3895
3896 /**
3897  * @class Roo.bootstrap.MenuSeparator
3898  * @extends Roo.bootstrap.Component
3899  * Bootstrap MenuSeparator class
3900  * 
3901  * @constructor
3902  * Create a new MenuItem
3903  * @param {Object} config The config object
3904  */
3905
3906
3907 Roo.bootstrap.MenuSeparator = function(config){
3908     Roo.bootstrap.MenuSeparator.superclass.constructor.call(this, config);
3909 };
3910
3911 Roo.extend(Roo.bootstrap.MenuSeparator, Roo.bootstrap.Component,  {
3912     
3913     getAutoCreate : function(){
3914         var cfg = {
3915             cls: 'divider',
3916             tag : 'li'
3917         };
3918         
3919         return cfg;
3920     }
3921    
3922 });
3923
3924  
3925
3926  
3927 /*
3928 * Licence: LGPL
3929 */
3930
3931 /**
3932  * @class Roo.bootstrap.Modal
3933  * @extends Roo.bootstrap.Component
3934  * Bootstrap Modal class
3935  * @cfg {String} title Title of dialog
3936  * @cfg {String} html - the body of the dialog (for simple ones) - you can also use template..
3937  * @cfg {Roo.Template} tmpl - a template with variables. to use it, add a handler in show:method  adn
3938  * @cfg {Boolean} specificTitle default false
3939  * @cfg {Array} buttons Array of buttons or standard button set..
3940  * @cfg {String} buttonPosition (left|right|center) default right (DEPRICATED) - use mr-auto on buttons to put them on the left
3941  * @cfg {Boolean} animate default true
3942  * @cfg {Boolean} allow_close default true
3943  * @cfg {Boolean} fitwindow default false
3944  * @cfg {Number} width fixed width - usefull for chrome extension only really.
3945  * @cfg {Number} height fixed height - usefull for chrome extension only really.
3946  * @cfg {String} size (sm|lg|xl) default empty
3947  * @cfg {Number} max_width set the max width of modal
3948  * @cfg {Boolean} editableTitle can the title be edited
3949
3950  *
3951  *
3952  * @constructor
3953  * Create a new Modal Dialog
3954  * @param {Object} config The config object
3955  */
3956
3957 Roo.bootstrap.Modal = function(config){
3958     Roo.bootstrap.Modal.superclass.constructor.call(this, config);
3959     this.addEvents({
3960         // raw events
3961         /**
3962          * @event btnclick
3963          * The raw btnclick event for the button
3964          * @param {Roo.EventObject} e
3965          */
3966         "btnclick" : true,
3967         /**
3968          * @event resize
3969          * Fire when dialog resize
3970          * @param {Roo.bootstrap.Modal} this
3971          * @param {Roo.EventObject} e
3972          */
3973         "resize" : true,
3974         /**
3975          * @event titlechanged
3976          * Fire when the editable title has been changed
3977          * @param {Roo.bootstrap.Modal} this
3978          * @param {Roo.EventObject} value
3979          */
3980         "titlechanged" : true 
3981         
3982     });
3983     this.buttons = this.buttons || [];
3984
3985     if (this.tmpl) {
3986         this.tmpl = Roo.factory(this.tmpl);
3987     }
3988
3989 };
3990
3991 Roo.extend(Roo.bootstrap.Modal, Roo.bootstrap.Component,  {
3992
3993     title : 'test dialog',
3994
3995     buttons : false,
3996
3997     // set on load...
3998
3999     html: false,
4000
4001     tmp: false,
4002
4003     specificTitle: false,
4004
4005     buttonPosition: 'right',
4006
4007     allow_close : true,
4008
4009     animate : true,
4010
4011     fitwindow: false,
4012     
4013      // private
4014     dialogEl: false,
4015     bodyEl:  false,
4016     footerEl:  false,
4017     titleEl:  false,
4018     closeEl:  false,
4019
4020     size: '',
4021     
4022     max_width: 0,
4023     
4024     max_height: 0,
4025     
4026     fit_content: false,
4027     editableTitle  : false,
4028
4029     onRender : function(ct, position)
4030     {
4031         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
4032
4033         if(!this.el){
4034             var cfg = Roo.apply({},  this.getAutoCreate());
4035             cfg.id = Roo.id();
4036             //if(!cfg.name){
4037             //    cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
4038             //}
4039             //if (!cfg.name.length) {
4040             //    delete cfg.name;
4041            // }
4042             if (this.cls) {
4043                 cfg.cls += ' ' + this.cls;
4044             }
4045             if (this.style) {
4046                 cfg.style = this.style;
4047             }
4048             this.el = Roo.get(document.body).createChild(cfg, position);
4049         }
4050         //var type = this.el.dom.type;
4051
4052
4053         if(this.tabIndex !== undefined){
4054             this.el.dom.setAttribute('tabIndex', this.tabIndex);
4055         }
4056
4057         this.dialogEl = this.el.select('.modal-dialog',true).first();
4058         this.bodyEl = this.el.select('.modal-body',true).first();
4059         this.closeEl = this.el.select('.modal-header .close', true).first();
4060         this.headerEl = this.el.select('.modal-header',true).first();
4061         this.titleEl = this.el.select('.modal-title',true).first();
4062         this.footerEl = this.el.select('.modal-footer',true).first();
4063
4064         this.maskEl = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
4065         
4066         //this.el.addClass("x-dlg-modal");
4067
4068         if (this.buttons.length) {
4069             Roo.each(this.buttons, function(bb) {
4070                 var b = Roo.apply({}, bb);
4071                 b.xns = b.xns || Roo.bootstrap;
4072                 b.xtype = b.xtype || 'Button';
4073                 if (typeof(b.listeners) == 'undefined') {
4074                     b.listeners = { click : this.onButtonClick.createDelegate(this)  };
4075                 }
4076
4077                 var btn = Roo.factory(b);
4078
4079                 btn.render(this.getButtonContainer());
4080
4081             },this);
4082         }
4083         // render the children.
4084         var nitems = [];
4085
4086         if(typeof(this.items) != 'undefined'){
4087             var items = this.items;
4088             delete this.items;
4089
4090             for(var i =0;i < items.length;i++) {
4091                 nitems.push(this.addxtype(Roo.apply({}, items[i])));
4092             }
4093         }
4094
4095         this.items = nitems;
4096
4097         // where are these used - they used to be body/close/footer
4098
4099
4100         this.initEvents();
4101         //this.el.addClass([this.fieldClass, this.cls]);
4102
4103     },
4104
4105     getAutoCreate : function()
4106     {
4107         // we will default to modal-body-overflow - might need to remove or make optional later.
4108         var bdy = {
4109                 cls : 'modal-body enable-modal-body-overflow ', 
4110                 html : this.html || ''
4111         };
4112
4113         var title = {
4114             tag: 'h4',
4115             cls : 'modal-title',
4116             html : this.title
4117         };
4118
4119         if(this.specificTitle){ // WTF is this?
4120             title = this.title;
4121         }
4122
4123         var header = [];
4124         if (this.allow_close && Roo.bootstrap.version == 3) {
4125             header.push({
4126                 tag: 'button',
4127                 cls : 'close',
4128                 html : '&times'
4129             });
4130         }
4131
4132         header.push(title);
4133
4134         if (this.editableTitle) {
4135             header.push({
4136                 cls: 'form-control roo-editable-title d-none',
4137                 tag: 'input',
4138                 type: 'text'
4139             });
4140         }
4141         
4142         if (this.allow_close && Roo.bootstrap.version == 4) {
4143             header.push({
4144                 tag: 'button',
4145                 cls : 'close',
4146                 html : '&times'
4147             });
4148         }
4149         
4150         var size = '';
4151
4152         if(this.size.length){
4153             size = 'modal-' + this.size;
4154         }
4155         
4156         var footer = Roo.bootstrap.version == 3 ?
4157             {
4158                 cls : 'modal-footer',
4159                 cn : [
4160                     {
4161                         tag: 'div',
4162                         cls: 'btn-' + this.buttonPosition
4163                     }
4164                 ]
4165
4166             } :
4167             {  // BS4 uses mr-auto on left buttons....
4168                 cls : 'modal-footer'
4169             };
4170
4171             
4172
4173         
4174         
4175         var modal = {
4176             cls: "modal",
4177              cn : [
4178                 {
4179                     cls: "modal-dialog " + size,
4180                     cn : [
4181                         {
4182                             cls : "modal-content",
4183                             cn : [
4184                                 {
4185                                     cls : 'modal-header',
4186                                     cn : header
4187                                 },
4188                                 bdy,
4189                                 footer
4190                             ]
4191
4192                         }
4193                     ]
4194
4195                 }
4196             ]
4197         };
4198
4199         if(this.animate){
4200             modal.cls += ' fade';
4201         }
4202
4203         return modal;
4204
4205     },
4206     getChildContainer : function() {
4207
4208          return this.bodyEl;
4209
4210     },
4211     getButtonContainer : function() {
4212         
4213          return Roo.bootstrap.version == 4 ?
4214             this.el.select('.modal-footer',true).first()
4215             : this.el.select('.modal-footer div',true).first();
4216
4217     },
4218     initEvents : function()
4219     {
4220         if (this.allow_close) {
4221             this.closeEl.on('click', this.hide, this);
4222         }
4223         Roo.EventManager.onWindowResize(this.resize, this, true);
4224         if (this.editableTitle) {
4225             this.headerEditEl =  this.headerEl.select('.form-control',true).first();
4226             this.headerEl.on('click', function() { this.toggleHeaderInput(true) } , this);
4227             this.headerEditEl.on('keyup', function(e) {
4228                     if([  e.RETURN , e.TAB , e.ESC ].indexOf(e.keyCode) > -1) {
4229                         this.toggleHeaderInput(false)
4230                     }
4231                 }, this);
4232             this.headerEditEl.on('blur', function(e) {
4233                 this.toggleHeaderInput(false)
4234             },this);
4235         }
4236
4237     },
4238   
4239
4240     resize : function()
4241     {
4242         this.maskEl.setSize(
4243             Roo.lib.Dom.getViewWidth(true),
4244             Roo.lib.Dom.getViewHeight(true)
4245         );
4246         
4247         if (this.fitwindow) {
4248             
4249            
4250             this.setSize(
4251                 this.width || Roo.lib.Dom.getViewportWidth(true) - 30,
4252                 this.height || Roo.lib.Dom.getViewportHeight(true) // catering margin-top 30 margin-bottom 30
4253             );
4254             return;
4255         }
4256         
4257         if(this.max_width !== 0) {
4258             
4259             var w = Math.min(this.max_width, Roo.lib.Dom.getViewportWidth(true) - 30);
4260             
4261             if(this.height) {
4262                 this.setSize(w, this.height);
4263                 return;
4264             }
4265             
4266             if(this.max_height) {
4267                 this.setSize(w,Math.min(
4268                     this.max_height,
4269                     Roo.lib.Dom.getViewportHeight(true) - 60
4270                 ));
4271                 
4272                 return;
4273             }
4274             
4275             if(!this.fit_content) {
4276                 this.setSize(w, Roo.lib.Dom.getViewportHeight(true) - 60);
4277                 return;
4278             }
4279             
4280             this.setSize(w, Math.min(
4281                 60 +
4282                 this.headerEl.getHeight() + 
4283                 this.footerEl.getHeight() + 
4284                 this.getChildHeight(this.bodyEl.dom.childNodes),
4285                 Roo.lib.Dom.getViewportHeight(true) - 60)
4286             );
4287         }
4288         
4289     },
4290
4291     setSize : function(w,h)
4292     {
4293         if (!w && !h) {
4294             return;
4295         }
4296         
4297         this.resizeTo(w,h);
4298     },
4299
4300     show : function() {
4301
4302         if (!this.rendered) {
4303             this.render();
4304         }
4305         this.toggleHeaderInput(false);
4306         //this.el.setStyle('display', 'block');
4307         this.el.removeClass('hideing');
4308         this.el.dom.style.display='block';
4309         
4310         Roo.get(document.body).addClass('modal-open');
4311  
4312         if(this.animate){  // element has 'fade'  - so stuff happens after .3s ?- not sure why the delay?
4313             
4314             (function(){
4315                 this.el.addClass('show');
4316                 this.el.addClass('in');
4317             }).defer(50, this);
4318         }else{
4319             this.el.addClass('show');
4320             this.el.addClass('in');
4321         }
4322
4323         // not sure how we can show data in here..
4324         //if (this.tmpl) {
4325         //    this.getChildContainer().dom.innerHTML = this.tmpl.applyTemplate(this);
4326         //}
4327
4328         Roo.get(document.body).addClass("x-body-masked");
4329         
4330         this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true),   Roo.lib.Dom.getViewHeight(true));
4331         this.maskEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
4332         this.maskEl.dom.style.display = 'block';
4333         this.maskEl.addClass('show');
4334         
4335         
4336         this.resize();
4337         
4338         this.fireEvent('show', this);
4339
4340         // set zindex here - otherwise it appears to be ignored...
4341         this.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
4342
4343         (function () {
4344             this.items.forEach( function(e) {
4345                 e.layout ? e.layout() : false;
4346
4347             });
4348         }).defer(100,this);
4349
4350     },
4351     hide : function()
4352     {
4353         if(this.fireEvent("beforehide", this) !== false){
4354             
4355             this.maskEl.removeClass('show');
4356             
4357             this.maskEl.dom.style.display = '';
4358             Roo.get(document.body).removeClass("x-body-masked");
4359             this.el.removeClass('in');
4360             this.el.select('.modal-dialog', true).first().setStyle('transform','');
4361
4362             if(this.animate){ // why
4363                 this.el.addClass('hideing');
4364                 this.el.removeClass('show');
4365                 (function(){
4366                     if (!this.el.hasClass('hideing')) {
4367                         return; // it's been shown again...
4368                     }
4369                     
4370                     this.el.dom.style.display='';
4371
4372                     Roo.get(document.body).removeClass('modal-open');
4373                     this.el.removeClass('hideing');
4374                 }).defer(150,this);
4375                 
4376             }else{
4377                 this.el.removeClass('show');
4378                 this.el.dom.style.display='';
4379                 Roo.get(document.body).removeClass('modal-open');
4380
4381             }
4382             this.fireEvent('hide', this);
4383         }
4384     },
4385     isVisible : function()
4386     {
4387         
4388         return this.el.hasClass('show') && !this.el.hasClass('hideing');
4389         
4390     },
4391
4392     addButton : function(str, cb)
4393     {
4394
4395
4396         var b = Roo.apply({}, { html : str } );
4397         b.xns = b.xns || Roo.bootstrap;
4398         b.xtype = b.xtype || 'Button';
4399         if (typeof(b.listeners) == 'undefined') {
4400             b.listeners = { click : cb.createDelegate(this)  };
4401         }
4402
4403         var btn = Roo.factory(b);
4404
4405         btn.render(this.getButtonContainer());
4406
4407         return btn;
4408
4409     },
4410
4411     setDefaultButton : function(btn)
4412     {
4413         //this.el.select('.modal-footer').()
4414     },
4415
4416     resizeTo: function(w,h)
4417     {
4418         this.dialogEl.setWidth(w);
4419         
4420         var diff = this.headerEl.getHeight() + this.footerEl.getHeight() + 60; // dialog margin-bottom: 30  
4421
4422         this.bodyEl.setHeight(h - diff);
4423         
4424         this.fireEvent('resize', this);
4425     },
4426     
4427     setContentSize  : function(w, h)
4428     {
4429
4430     },
4431     onButtonClick: function(btn,e)
4432     {
4433         //Roo.log([a,b,c]);
4434         this.fireEvent('btnclick', btn.name, e);
4435     },
4436      /**
4437      * Set the title of the Dialog
4438      * @param {String} str new Title
4439      */
4440     setTitle: function(str) {
4441         this.titleEl.dom.innerHTML = str;
4442         this.title = str;
4443     },
4444     /**
4445      * Set the body of the Dialog
4446      * @param {String} str new Title
4447      */
4448     setBody: function(str) {
4449         this.bodyEl.dom.innerHTML = str;
4450     },
4451     /**
4452      * Set the body of the Dialog using the template
4453      * @param {Obj} data - apply this data to the template and replace the body contents.
4454      */
4455     applyBody: function(obj)
4456     {
4457         if (!this.tmpl) {
4458             Roo.log("Error - using apply Body without a template");
4459             //code
4460         }
4461         this.tmpl.overwrite(this.bodyEl, obj);
4462     },
4463     
4464     getChildHeight : function(child_nodes)
4465     {
4466         if(
4467             !child_nodes ||
4468             child_nodes.length == 0
4469         ) {
4470             return 0;
4471         }
4472         
4473         var child_height = 0;
4474         
4475         for(var i = 0; i < child_nodes.length; i++) {
4476             
4477             /*
4478             * for modal with tabs...
4479             if(child_nodes[i].classList.contains('roo-layout-panel')) {
4480                 
4481                 var layout_childs = child_nodes[i].childNodes;
4482                 
4483                 for(var j = 0; j < layout_childs.length; j++) {
4484                     
4485                     if(layout_childs[j].classList.contains('roo-layout-panel-body')) {
4486                         
4487                         var layout_body_childs = layout_childs[j].childNodes;
4488                         
4489                         for(var k = 0; k < layout_body_childs.length; k++) {
4490                             
4491                             if(layout_body_childs[k].classList.contains('navbar')) {
4492                                 child_height += layout_body_childs[k].offsetHeight;
4493                                 continue;
4494                             }
4495                             
4496                             if(layout_body_childs[k].classList.contains('roo-layout-tabs-body')) {
4497                                 
4498                                 var layout_body_tab_childs = layout_body_childs[k].childNodes;
4499                                 
4500                                 for(var m = 0; m < layout_body_tab_childs.length; m++) {
4501                                     
4502                                     if(layout_body_tab_childs[m].classList.contains('roo-layout-active-content')) {
4503                                         child_height += this.getChildHeight(layout_body_tab_childs[m].childNodes);
4504                                         continue;
4505                                     }
4506                                     
4507                                 }
4508                                 
4509                             }
4510                             
4511                         }
4512                     }
4513                 }
4514                 continue;
4515             }
4516             */
4517             
4518             child_height += child_nodes[i].offsetHeight;
4519             // Roo.log(child_nodes[i].offsetHeight);
4520         }
4521         
4522         return child_height;
4523     },
4524     toggleHeaderInput : function(is_edit)
4525     {
4526         if (!this.editableTitle) {
4527             return; // not editable.
4528         }
4529         if (is_edit && this.is_header_editing) {
4530             return; // already editing..
4531         }
4532         if (is_edit) {
4533     
4534             this.headerEditEl.dom.value = this.title;
4535             this.headerEditEl.removeClass('d-none');
4536             this.headerEditEl.dom.focus();
4537             this.titleEl.addClass('d-none');
4538             
4539             this.is_header_editing = true;
4540             return
4541         }
4542         // flip back to not editing.
4543         this.title = this.headerEditEl.dom.value;
4544         this.headerEditEl.addClass('d-none');
4545         this.titleEl.removeClass('d-none');
4546         this.titleEl.dom.innerHTML = String.format('{0}', this.title);
4547         this.is_header_editing = false;
4548         this.fireEvent('titlechanged', this, this.title);
4549     
4550             
4551         
4552     }
4553
4554 });
4555
4556
4557 Roo.apply(Roo.bootstrap.Modal,  {
4558     /**
4559          * Button config that displays a single OK button
4560          * @type Object
4561          */
4562         OK :  [{
4563             name : 'ok',
4564             weight : 'primary',
4565             html : 'OK'
4566         }],
4567         /**
4568          * Button config that displays Yes and No buttons
4569          * @type Object
4570          */
4571         YESNO : [
4572             {
4573                 name  : 'no',
4574                 html : 'No'
4575             },
4576             {
4577                 name  :'yes',
4578                 weight : 'primary',
4579                 html : 'Yes'
4580             }
4581         ],
4582
4583         /**
4584          * Button config that displays OK and Cancel buttons
4585          * @type Object
4586          */
4587         OKCANCEL : [
4588             {
4589                name : 'cancel',
4590                 html : 'Cancel'
4591             },
4592             {
4593                 name : 'ok',
4594                 weight : 'primary',
4595                 html : 'OK'
4596             }
4597         ],
4598         /**
4599          * Button config that displays Yes, No and Cancel buttons
4600          * @type Object
4601          */
4602         YESNOCANCEL : [
4603             {
4604                 name : 'yes',
4605                 weight : 'primary',
4606                 html : 'Yes'
4607             },
4608             {
4609                 name : 'no',
4610                 html : 'No'
4611             },
4612             {
4613                 name : 'cancel',
4614                 html : 'Cancel'
4615             }
4616         ],
4617         
4618         zIndex : 10001
4619 });
4620
4621 /*
4622  * - LGPL
4623  *
4624  * messagebox - can be used as a replace
4625  * 
4626  */
4627 /**
4628  * @class Roo.MessageBox
4629  * Utility class for generating different styles of message boxes.  The alias Roo.Msg can also be used.
4630  * Example usage:
4631  *<pre><code>
4632 // Basic alert:
4633 Roo.Msg.alert('Status', 'Changes saved successfully.');
4634
4635 // Prompt for user data:
4636 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
4637     if (btn == 'ok'){
4638         // process text value...
4639     }
4640 });
4641
4642 // Show a dialog using config options:
4643 Roo.Msg.show({
4644    title:'Save Changes?',
4645    msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
4646    buttons: Roo.Msg.YESNOCANCEL,
4647    fn: processResult,
4648    animEl: 'elId'
4649 });
4650 </code></pre>
4651  * @singleton
4652  */
4653 Roo.bootstrap.MessageBox = function(){
4654     var dlg, opt, mask, waitTimer;
4655     var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
4656     var buttons, activeTextEl, bwidth;
4657
4658     
4659     // private
4660     var handleButton = function(button){
4661         dlg.hide();
4662         Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
4663     };
4664
4665     // private
4666     var handleHide = function(){
4667         if(opt && opt.cls){
4668             dlg.el.removeClass(opt.cls);
4669         }
4670         //if(waitTimer){
4671         //    Roo.TaskMgr.stop(waitTimer);
4672         //    waitTimer = null;
4673         //}
4674     };
4675
4676     // private
4677     var updateButtons = function(b){
4678         var width = 0;
4679         if(!b){
4680             buttons["ok"].hide();
4681             buttons["cancel"].hide();
4682             buttons["yes"].hide();
4683             buttons["no"].hide();
4684             dlg.footerEl.hide();
4685             
4686             return width;
4687         }
4688         dlg.footerEl.show();
4689         for(var k in buttons){
4690             if(typeof buttons[k] != "function"){
4691                 if(b[k]){
4692                     buttons[k].show();
4693                     buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.bootstrap.MessageBox.buttonText[k]);
4694                     width += buttons[k].el.getWidth()+15;
4695                 }else{
4696                     buttons[k].hide();
4697                 }
4698             }
4699         }
4700         return width;
4701     };
4702
4703     // private
4704     var handleEsc = function(d, k, e){
4705         if(opt && opt.closable !== false){
4706             dlg.hide();
4707         }
4708         if(e){
4709             e.stopEvent();
4710         }
4711     };
4712
4713     return {
4714         /**
4715          * Returns a reference to the underlying {@link Roo.BasicDialog} element
4716          * @return {Roo.BasicDialog} The BasicDialog element
4717          */
4718         getDialog : function(){
4719            if(!dlg){
4720                 dlg = new Roo.bootstrap.Modal( {
4721                     //draggable: true,
4722                     //resizable:false,
4723                     //constraintoviewport:false,
4724                     //fixedcenter:true,
4725                     //collapsible : false,
4726                     //shim:true,
4727                     //modal: true,
4728                 //    width: 'auto',
4729                   //  height:100,
4730                     //buttonAlign:"center",
4731                     closeClick : function(){
4732                         if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
4733                             handleButton("no");
4734                         }else{
4735                             handleButton("cancel");
4736                         }
4737                     }
4738                 });
4739                 dlg.render();
4740                 dlg.on("hide", handleHide);
4741                 mask = dlg.mask;
4742                 //dlg.addKeyListener(27, handleEsc);
4743                 buttons = {};
4744                 this.buttons = buttons;
4745                 var bt = this.buttonText;
4746                 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
4747                 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
4748                 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
4749                 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
4750                 //Roo.log(buttons);
4751                 bodyEl = dlg.bodyEl.createChild({
4752
4753                     html:'<span class="roo-mb-text"></span><br /><input type="text" class="roo-mb-input" />' +
4754                         '<textarea class="roo-mb-textarea"></textarea>' +
4755                         '<div class="roo-mb-progress-wrap"><div class="roo-mb-progress"><div class="roo-mb-progress-bar">&#160;</div></div></div>'
4756                 });
4757                 msgEl = bodyEl.dom.firstChild;
4758                 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
4759                 textboxEl.enableDisplayMode();
4760                 textboxEl.addKeyListener([10,13], function(){
4761                     if(dlg.isVisible() && opt && opt.buttons){
4762                         if(opt.buttons.ok){
4763                             handleButton("ok");
4764                         }else if(opt.buttons.yes){
4765                             handleButton("yes");
4766                         }
4767                     }
4768                 });
4769                 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
4770                 textareaEl.enableDisplayMode();
4771                 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
4772                 progressEl.enableDisplayMode();
4773                 
4774                 // This is supposed to be the progessElement.. but I think it's controlling the height of everything..
4775                 var pf = progressEl.dom.firstChild;
4776                 if (pf) {
4777                     pp = Roo.get(pf.firstChild);
4778                     pp.setHeight(pf.offsetHeight);
4779                 }
4780                 
4781             }
4782             return dlg;
4783         },
4784
4785         /**
4786          * Updates the message box body text
4787          * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
4788          * the XHTML-compliant non-breaking space character '&amp;#160;')
4789          * @return {Roo.MessageBox} This message box
4790          */
4791         updateText : function(text)
4792         {
4793             if(!dlg.isVisible() && !opt.width){
4794                 dlg.dialogEl.setStyle({ 'max-width' : this.maxWidth});
4795                 // dlg.resizeTo(this.maxWidth, 100); // forcing the height breaks long alerts()
4796             }
4797             msgEl.innerHTML = text || '&#160;';
4798       
4799             var cw =  Math.max(msgEl.offsetWidth, msgEl.parentNode.scrollWidth);
4800             //Roo.log("guesed size: " + JSON.stringify([cw,msgEl.offsetWidth, msgEl.parentNode.scrollWidth]));
4801             var w = Math.max(
4802                     Math.min(opt.width || cw , this.maxWidth), 
4803                     Math.max(opt.minWidth || this.minWidth, bwidth)
4804             );
4805             if(opt.prompt){
4806                 activeTextEl.setWidth(w);
4807             }
4808             if(dlg.isVisible()){
4809                 dlg.fixedcenter = false;
4810             }
4811             // to big, make it scroll. = But as usual stupid IE does not support
4812             // !important..
4813             
4814             if ( bodyEl.getHeight() > (Roo.lib.Dom.getViewHeight() - 100)) {
4815                 bodyEl.setHeight ( Roo.lib.Dom.getViewHeight() - 100 );
4816                 bodyEl.dom.style.overflowY = 'auto' + ( Roo.isIE ? '' : ' !important');
4817             } else {
4818                 bodyEl.dom.style.height = '';
4819                 bodyEl.dom.style.overflowY = '';
4820             }
4821             if (cw > w) {
4822                 bodyEl.dom.style.get = 'auto' + ( Roo.isIE ? '' : ' !important');
4823             } else {
4824                 bodyEl.dom.style.overflowX = '';
4825             }
4826             
4827             dlg.setContentSize(w, bodyEl.getHeight());
4828             if(dlg.isVisible()){
4829                 dlg.fixedcenter = true;
4830             }
4831             return this;
4832         },
4833
4834         /**
4835          * Updates a progress-style message box's text and progress bar.  Only relevant on message boxes
4836          * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
4837          * @param {Number} value Any number between 0 and 1 (e.g., .5)
4838          * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
4839          * @return {Roo.MessageBox} This message box
4840          */
4841         updateProgress : function(value, text){
4842             if(text){
4843                 this.updateText(text);
4844             }
4845             
4846             if (pp) { // weird bug on my firefox - for some reason this is not defined
4847                 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
4848                 pp.setHeight(Math.floor(progressEl.dom.firstChild.offsetHeight));
4849             }
4850             return this;
4851         },        
4852
4853         /**
4854          * Returns true if the message box is currently displayed
4855          * @return {Boolean} True if the message box is visible, else false
4856          */
4857         isVisible : function(){
4858             return dlg && dlg.isVisible();  
4859         },
4860
4861         /**
4862          * Hides the message box if it is displayed
4863          */
4864         hide : function(){
4865             if(this.isVisible()){
4866                 dlg.hide();
4867             }  
4868         },
4869
4870         /**
4871          * Displays a new message box, or reinitializes an existing message box, based on the config options
4872          * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
4873          * The following config object properties are supported:
4874          * <pre>
4875 Property    Type             Description
4876 ----------  ---------------  ------------------------------------------------------------------------------------
4877 animEl            String/Element   An id or Element from which the message box should animate as it opens and
4878                                    closes (defaults to undefined)
4879 buttons           Object/Boolean   A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
4880                                    cancel:'Bar'}), or false to not show any buttons (defaults to false)
4881 closable          Boolean          False to hide the top-right close button (defaults to true).  Note that
4882                                    progress and wait dialogs will ignore this property and always hide the
4883                                    close button as they can only be closed programmatically.
4884 cls               String           A custom CSS class to apply to the message box element
4885 defaultTextHeight Number           The default height in pixels of the message box's multiline textarea if
4886                                    displayed (defaults to 75)
4887 fn                Function         A callback function to execute after closing the dialog.  The arguments to the
4888                                    function will be btn (the name of the button that was clicked, if applicable,
4889                                    e.g. "ok"), and text (the value of the active text field, if applicable).
4890                                    Progress and wait dialogs will ignore this option since they do not respond to
4891                                    user actions and can only be closed programmatically, so any required function
4892                                    should be called by the same code after it closes the dialog.
4893 icon              String           A CSS class that provides a background image to be used as an icon for
4894                                    the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
4895 maxWidth          Number           The maximum width in pixels of the message box (defaults to 600)
4896 minWidth          Number           The minimum width in pixels of the message box (defaults to 100)
4897 modal             Boolean          False to allow user interaction with the page while the message box is
4898                                    displayed (defaults to true)
4899 msg               String           A string that will replace the existing message box body text (defaults
4900                                    to the XHTML-compliant non-breaking space character '&#160;')
4901 multiline         Boolean          True to prompt the user to enter multi-line text (defaults to false)
4902 progress          Boolean          True to display a progress bar (defaults to false)
4903 progressText      String           The text to display inside the progress bar if progress = true (defaults to '')
4904 prompt            Boolean          True to prompt the user to enter single-line text (defaults to false)
4905 proxyDrag         Boolean          True to display a lightweight proxy while dragging (defaults to false)
4906 title             String           The title text
4907 value             String           The string value to set into the active textbox element if displayed
4908 wait              Boolean          True to display a progress bar (defaults to false)
4909 width             Number           The width of the dialog in pixels
4910 </pre>
4911          *
4912          * Example usage:
4913          * <pre><code>
4914 Roo.Msg.show({
4915    title: 'Address',
4916    msg: 'Please enter your address:',
4917    width: 300,
4918    buttons: Roo.MessageBox.OKCANCEL,
4919    multiline: true,
4920    fn: saveAddress,
4921    animEl: 'addAddressBtn'
4922 });
4923 </code></pre>
4924          * @param {Object} config Configuration options
4925          * @return {Roo.MessageBox} This message box
4926          */
4927         show : function(options)
4928         {
4929             
4930             // this causes nightmares if you show one dialog after another
4931             // especially on callbacks..
4932              
4933             if(this.isVisible()){
4934                 
4935                 this.hide();
4936                 Roo.log("[Roo.Messagebox] Show called while message displayed:" );
4937                 Roo.log("Old Dialog Message:" +  msgEl.innerHTML );
4938                 Roo.log("New Dialog Message:" +  options.msg )
4939                 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
4940                 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
4941                 
4942             }
4943             var d = this.getDialog();
4944             opt = options;
4945             d.setTitle(opt.title || "&#160;");
4946             d.closeEl.setDisplayed(opt.closable !== false);
4947             activeTextEl = textboxEl;
4948             opt.prompt = opt.prompt || (opt.multiline ? true : false);
4949             if(opt.prompt){
4950                 if(opt.multiline){
4951                     textboxEl.hide();
4952                     textareaEl.show();
4953                     textareaEl.setHeight(typeof opt.multiline == "number" ?
4954                         opt.multiline : this.defaultTextHeight);
4955                     activeTextEl = textareaEl;
4956                 }else{
4957                     textboxEl.show();
4958                     textareaEl.hide();
4959                 }
4960             }else{
4961                 textboxEl.hide();
4962                 textareaEl.hide();
4963             }
4964             progressEl.setDisplayed(opt.progress === true);
4965             if (opt.progress) {
4966                 d.animate = false; // do not animate progress, as it may not have finished animating before we close it..
4967             }
4968             this.updateProgress(0);
4969             activeTextEl.dom.value = opt.value || "";
4970             if(opt.prompt){
4971                 dlg.setDefaultButton(activeTextEl);
4972             }else{
4973                 var bs = opt.buttons;
4974                 var db = null;
4975                 if(bs && bs.ok){
4976                     db = buttons["ok"];
4977                 }else if(bs && bs.yes){
4978                     db = buttons["yes"];
4979                 }
4980                 dlg.setDefaultButton(db);
4981             }
4982             bwidth = updateButtons(opt.buttons);
4983             this.updateText(opt.msg);
4984             if(opt.cls){
4985                 d.el.addClass(opt.cls);
4986             }
4987             d.proxyDrag = opt.proxyDrag === true;
4988             d.modal = opt.modal !== false;
4989             d.mask = opt.modal !== false ? mask : false;
4990             if(!d.isVisible()){
4991                 // force it to the end of the z-index stack so it gets a cursor in FF
4992                 document.body.appendChild(dlg.el.dom);
4993                 d.animateTarget = null;
4994                 d.show(options.animEl);
4995             }
4996             return this;
4997         },
4998
4999         /**
5000          * Displays a message box with a progress bar.  This message box has no buttons and is not closeable by
5001          * the user.  You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
5002          * and closing the message box when the process is complete.
5003          * @param {String} title The title bar text
5004          * @param {String} msg The message box body text
5005          * @return {Roo.MessageBox} This message box
5006          */
5007         progress : function(title, msg){
5008             this.show({
5009                 title : title,
5010                 msg : msg,
5011                 buttons: false,
5012                 progress:true,
5013                 closable:false,
5014                 minWidth: this.minProgressWidth,
5015                 modal : true
5016             });
5017             return this;
5018         },
5019
5020         /**
5021          * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
5022          * If a callback function is passed it will be called after the user clicks the button, and the
5023          * id of the button that was clicked will be passed as the only parameter to the callback
5024          * (could also be the top-right close button).
5025          * @param {String} title The title bar text
5026          * @param {String} msg The message box body text
5027          * @param {Function} fn (optional) The callback function invoked after the message box is closed
5028          * @param {Object} scope (optional) The scope of the callback function
5029          * @return {Roo.MessageBox} This message box
5030          */
5031         alert : function(title, msg, fn, scope)
5032         {
5033             this.show({
5034                 title : title,
5035                 msg : msg,
5036                 buttons: this.OK,
5037                 fn: fn,
5038                 closable : false,
5039                 scope : scope,
5040                 modal : true
5041             });
5042             return this;
5043         },
5044
5045         /**
5046          * Displays a message box with an infinitely auto-updating progress bar.  This can be used to block user
5047          * interaction while waiting for a long-running process to complete that does not have defined intervals.
5048          * You are responsible for closing the message box when the process is complete.
5049          * @param {String} msg The message box body text
5050          * @param {String} title (optional) The title bar text
5051          * @return {Roo.MessageBox} This message box
5052          */
5053         wait : function(msg, title){
5054             this.show({
5055                 title : title,
5056                 msg : msg,
5057                 buttons: false,
5058                 closable:false,
5059                 progress:true,
5060                 modal:true,
5061                 width:300,
5062                 wait:true
5063             });
5064             waitTimer = Roo.TaskMgr.start({
5065                 run: function(i){
5066                     Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
5067                 },
5068                 interval: 1000
5069             });
5070             return this;
5071         },
5072
5073         /**
5074          * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
5075          * If a callback function is passed it will be called after the user clicks either button, and the id of the
5076          * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
5077          * @param {String} title The title bar text
5078          * @param {String} msg The message box body text
5079          * @param {Function} fn (optional) The callback function invoked after the message box is closed
5080          * @param {Object} scope (optional) The scope of the callback function
5081          * @return {Roo.MessageBox} This message box
5082          */
5083         confirm : function(title, msg, fn, scope){
5084             this.show({
5085                 title : title,
5086                 msg : msg,
5087                 buttons: this.YESNO,
5088                 fn: fn,
5089                 scope : scope,
5090                 modal : true
5091             });
5092             return this;
5093         },
5094
5095         /**
5096          * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
5097          * JavaScript's Window.prompt).  The prompt can be a single-line or multi-line textbox.  If a callback function
5098          * is passed it will be called after the user clicks either button, and the id of the button that was clicked
5099          * (could also be the top-right close button) and the text that was entered will be passed as the two
5100          * parameters to the callback.
5101          * @param {String} title The title bar text
5102          * @param {String} msg The message box body text
5103          * @param {Function} fn (optional) The callback function invoked after the message box is closed
5104          * @param {Object} scope (optional) The scope of the callback function
5105          * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
5106          * property, or the height in pixels to create the textbox (defaults to false / single-line)
5107          * @return {Roo.MessageBox} This message box
5108          */
5109         prompt : function(title, msg, fn, scope, multiline){
5110             this.show({
5111                 title : title,
5112                 msg : msg,
5113                 buttons: this.OKCANCEL,
5114                 fn: fn,
5115                 minWidth:250,
5116                 scope : scope,
5117                 prompt:true,
5118                 multiline: multiline,
5119                 modal : true
5120             });
5121             return this;
5122         },
5123
5124         /**
5125          * Button config that displays a single OK button
5126          * @type Object
5127          */
5128         OK : {ok:true},
5129         /**
5130          * Button config that displays Yes and No buttons
5131          * @type Object
5132          */
5133         YESNO : {yes:true, no:true},
5134         /**
5135          * Button config that displays OK and Cancel buttons
5136          * @type Object
5137          */
5138         OKCANCEL : {ok:true, cancel:true},
5139         /**
5140          * Button config that displays Yes, No and Cancel buttons
5141          * @type Object
5142          */
5143         YESNOCANCEL : {yes:true, no:true, cancel:true},
5144
5145         /**
5146          * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
5147          * @type Number
5148          */
5149         defaultTextHeight : 75,
5150         /**
5151          * The maximum width in pixels of the message box (defaults to 600)
5152          * @type Number
5153          */
5154         maxWidth : 600,
5155         /**
5156          * The minimum width in pixels of the message box (defaults to 100)
5157          * @type Number
5158          */
5159         minWidth : 100,
5160         /**
5161          * The minimum width in pixels of the message box if it is a progress-style dialog.  This is useful
5162          * for setting a different minimum width than text-only dialogs may need (defaults to 250)
5163          * @type Number
5164          */
5165         minProgressWidth : 250,
5166         /**
5167          * An object containing the default button text strings that can be overriden for localized language support.
5168          * Supported properties are: ok, cancel, yes and no.
5169          * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
5170          * @type Object
5171          */
5172         buttonText : {
5173             ok : "OK",
5174             cancel : "Cancel",
5175             yes : "Yes",
5176             no : "No"
5177         }
5178     };
5179 }();
5180
5181 /**
5182  * Shorthand for {@link Roo.MessageBox}
5183  */
5184 Roo.MessageBox = Roo.MessageBox || Roo.bootstrap.MessageBox;
5185 Roo.Msg = Roo.Msg || Roo.MessageBox;
5186 /*
5187  * - LGPL
5188  *
5189  * navbar
5190  * 
5191  */
5192
5193 /**
5194  * @class Roo.bootstrap.Navbar
5195  * @extends Roo.bootstrap.Component
5196  * Bootstrap Navbar class
5197
5198  * @constructor
5199  * Create a new Navbar
5200  * @param {Object} config The config object
5201  */
5202
5203
5204 Roo.bootstrap.Navbar = function(config){
5205     Roo.bootstrap.Navbar.superclass.constructor.call(this, config);
5206     this.addEvents({
5207         // raw events
5208         /**
5209          * @event beforetoggle
5210          * Fire before toggle the menu
5211          * @param {Roo.EventObject} e
5212          */
5213         "beforetoggle" : true
5214     });
5215 };
5216
5217 Roo.extend(Roo.bootstrap.Navbar, Roo.bootstrap.Component,  {
5218     
5219     
5220    
5221     // private
5222     navItems : false,
5223     loadMask : false,
5224     
5225     
5226     getAutoCreate : function(){
5227         
5228         
5229         throw { message : "nav bar is now a abstract base class - use NavSimplebar / NavHeaderbar / NavSidebar etc..."};
5230         
5231     },
5232     
5233     initEvents :function ()
5234     {
5235         //Roo.log(this.el.select('.navbar-toggle',true));
5236         this.el.select('.navbar-toggle',true).on('click', this.onToggle , this);
5237         
5238         var mark = {
5239             tag: "div",
5240             cls:"x-dlg-mask"
5241         };
5242         
5243         this.maskEl = Roo.DomHelper.append(this.el, mark, true);
5244         
5245         var size = this.el.getSize();
5246         this.maskEl.setSize(size.width, size.height);
5247         this.maskEl.enableDisplayMode("block");
5248         this.maskEl.hide();
5249         
5250         if(this.loadMask){
5251             this.maskEl.show();
5252         }
5253     },
5254     
5255     
5256     getChildContainer : function()
5257     {
5258         if (this.el && this.el.select('.collapse').getCount()) {
5259             return this.el.select('.collapse',true).first();
5260         }
5261         
5262         return this.el;
5263     },
5264     
5265     mask : function()
5266     {
5267         this.maskEl.show();
5268     },
5269     
5270     unmask : function()
5271     {
5272         this.maskEl.hide();
5273     },
5274     onToggle : function()
5275     {
5276         
5277         if(this.fireEvent('beforetoggle', this) === false){
5278             return;
5279         }
5280         var ce = this.el.select('.navbar-collapse',true).first();
5281       
5282         if (!ce.hasClass('show')) {
5283            this.expand();
5284         } else {
5285             this.collapse();
5286         }
5287         
5288         
5289     
5290     },
5291     /**
5292      * Expand the navbar pulldown 
5293      */
5294     expand : function ()
5295     {
5296        
5297         var ce = this.el.select('.navbar-collapse',true).first();
5298         if (ce.hasClass('collapsing')) {
5299             return;
5300         }
5301         ce.dom.style.height = '';
5302                // show it...
5303         ce.addClass('in'); // old...
5304         ce.removeClass('collapse');
5305         ce.addClass('show');
5306         var h = ce.getHeight();
5307         Roo.log(h);
5308         ce.removeClass('show');
5309         // at this point we should be able to see it..
5310         ce.addClass('collapsing');
5311         
5312         ce.setHeight(0); // resize it ...
5313         ce.on('transitionend', function() {
5314             //Roo.log('done transition');
5315             ce.removeClass('collapsing');
5316             ce.addClass('show');
5317             ce.removeClass('collapse');
5318
5319             ce.dom.style.height = '';
5320         }, this, { single: true} );
5321         ce.setHeight(h);
5322         ce.dom.scrollTop = 0;
5323     },
5324     /**
5325      * Collapse the navbar pulldown 
5326      */
5327     collapse : function()
5328     {
5329          var ce = this.el.select('.navbar-collapse',true).first();
5330        
5331         if (ce.hasClass('collapsing') || ce.hasClass('collapse') ) {
5332             // it's collapsed or collapsing..
5333             return;
5334         }
5335         ce.removeClass('in'); // old...
5336         ce.setHeight(ce.getHeight());
5337         ce.removeClass('show');
5338         ce.addClass('collapsing');
5339         
5340         ce.on('transitionend', function() {
5341             ce.dom.style.height = '';
5342             ce.removeClass('collapsing');
5343             ce.addClass('collapse');
5344         }, this, { single: true} );
5345         ce.setHeight(0);
5346     }
5347     
5348     
5349     
5350 });
5351
5352
5353
5354  
5355
5356  /*
5357  * - LGPL
5358  *
5359  * navbar
5360  * 
5361  */
5362
5363 /**
5364  * @class Roo.bootstrap.NavSimplebar
5365  * @extends Roo.bootstrap.Navbar
5366  * Bootstrap Sidebar class
5367  *
5368  * @cfg {Boolean} inverse is inverted color
5369  * 
5370  * @cfg {String} type (nav | pills | tabs)
5371  * @cfg {Boolean} arrangement stacked | justified
5372  * @cfg {String} align (left | right) alignment
5373  * 
5374  * @cfg {Boolean} main (true|false) main nav bar? default false
5375  * @cfg {Boolean} loadMask (true|false) loadMask on the bar
5376  * 
5377  * @cfg {String} tag (header|footer|nav|div) default is nav 
5378
5379  * @cfg {String} weight (light|primary|secondary|success|danger|warning|info|dark|white) default is light.
5380  * 
5381  * 
5382  * @constructor
5383  * Create a new Sidebar
5384  * @param {Object} config The config object
5385  */
5386
5387
5388 Roo.bootstrap.NavSimplebar = function(config){
5389     Roo.bootstrap.NavSimplebar.superclass.constructor.call(this, config);
5390 };
5391
5392 Roo.extend(Roo.bootstrap.NavSimplebar, Roo.bootstrap.Navbar,  {
5393     
5394     inverse: false,
5395     
5396     type: false,
5397     arrangement: '',
5398     align : false,
5399     
5400     weight : 'light',
5401     
5402     main : false,
5403     
5404     
5405     tag : false,
5406     
5407     
5408     getAutoCreate : function(){
5409         
5410         
5411         var cfg = {
5412             tag : this.tag || 'div',
5413             cls : 'navbar roo-navbar-simple' //navbar-expand-lg ??
5414         };
5415         if (['light','white'].indexOf(this.weight) > -1) {
5416             cfg.cls += ['light','white'].indexOf(this.weight) > -1 ? ' navbar-light' : ' navbar-dark';
5417         }
5418         cfg.cls += ' bg-' + this.weight;
5419         
5420         if (this.inverse) {
5421             cfg.cls += ' navbar-inverse';
5422             
5423         }
5424         
5425         // i'm not actually sure these are really used - normally we add a navGroup to a navbar
5426         
5427         if (Roo.bootstrap.version == 4 && this.xtype == 'NavSimplebar') {
5428             return cfg;
5429         }
5430         
5431         
5432     
5433         
5434         cfg.cn = [
5435             {
5436                 cls: 'nav nav-' + this.xtype,
5437                 tag : 'ul'
5438             }
5439         ];
5440         
5441          
5442         this.type = this.type || 'nav';
5443         if (['tabs','pills'].indexOf(this.type) != -1) {
5444             cfg.cn[0].cls += ' nav-' + this.type
5445         
5446         
5447         } else {
5448             if (this.type!=='nav') {
5449                 Roo.log('nav type must be nav/tabs/pills')
5450             }
5451             cfg.cn[0].cls += ' navbar-nav'
5452         }
5453         
5454         
5455         
5456         
5457         if (['stacked','justified'].indexOf(this.arrangement) != -1) {
5458             cfg.cn[0].cls += ' nav-' + this.arrangement;
5459         }
5460         
5461         
5462         if (this.align === 'right') {
5463             cfg.cn[0].cls += ' navbar-right';
5464         }
5465         
5466         
5467         
5468         
5469         return cfg;
5470     
5471         
5472     }
5473     
5474     
5475     
5476 });
5477
5478
5479
5480  
5481
5482  
5483        /*
5484  * - LGPL
5485  *
5486  * navbar
5487  * navbar-fixed-top
5488  * navbar-expand-md  fixed-top 
5489  */
5490
5491 /**
5492  * @class Roo.bootstrap.NavHeaderbar
5493  * @extends Roo.bootstrap.NavSimplebar
5494  * Bootstrap Sidebar class
5495  *
5496  * @cfg {String} brand what is brand
5497  * @cfg {String} position (fixed-top|fixed-bottom|static-top) position
5498  * @cfg {String} brand_href href of the brand
5499  * @cfg {Boolean} srButton generate the (screen reader / mobile) sr-only button   default true
5500  * @cfg {Boolean} autohide a top nav bar header that hides on scroll.
5501  * @cfg {Boolean} desktopCenter should the header be centered on desktop using a container class
5502  * @cfg {Roo.bootstrap.Row} mobilerow - a row to display on mobile only..
5503  * 
5504  * @constructor
5505  * Create a new Sidebar
5506  * @param {Object} config The config object
5507  */
5508
5509
5510 Roo.bootstrap.NavHeaderbar = function(config){
5511     Roo.bootstrap.NavHeaderbar.superclass.constructor.call(this, config);
5512       
5513 };
5514
5515 Roo.extend(Roo.bootstrap.NavHeaderbar, Roo.bootstrap.NavSimplebar,  {
5516     
5517     position: '',
5518     brand: '',
5519     brand_href: false,
5520     srButton : true,
5521     autohide : false,
5522     desktopCenter : false,
5523    
5524     
5525     getAutoCreate : function(){
5526         
5527         var   cfg = {
5528             tag: this.nav || 'nav',
5529             cls: 'navbar navbar-expand-md',
5530             role: 'navigation',
5531             cn: []
5532         };
5533         
5534         var cn = cfg.cn;
5535         if (this.desktopCenter) {
5536             cn.push({cls : 'container', cn : []});
5537             cn = cn[0].cn;
5538         }
5539         
5540         if(this.srButton){
5541             var btn = {
5542                 tag: 'button',
5543                 type: 'button',
5544                 cls: 'navbar-toggle navbar-toggler',
5545                 'data-toggle': 'collapse',
5546                 cn: [
5547                     {
5548                         tag: 'span',
5549                         cls: 'sr-only',
5550                         html: 'Toggle navigation'
5551                     },
5552                     {
5553                         tag: 'span',
5554                         cls: 'icon-bar navbar-toggler-icon'
5555                     },
5556                     {
5557                         tag: 'span',
5558                         cls: 'icon-bar'
5559                     },
5560                     {
5561                         tag: 'span',
5562                         cls: 'icon-bar'
5563                     }
5564                 ]
5565             };
5566             
5567             cn.push( Roo.bootstrap.version == 4 ? btn : {
5568                 tag: 'div',
5569                 cls: 'navbar-header',
5570                 cn: [
5571                     btn
5572                 ]
5573             });
5574         }
5575         
5576         cn.push({
5577             tag: 'div',
5578             cls: Roo.bootstrap.version == 4  ? 'nav flex-row roo-navbar-collapse collapse navbar-collapse' : 'collapse navbar-collapse roo-navbar-collapse',
5579             cn : []
5580         });
5581         
5582         cfg.cls += this.inverse ? ' navbar-inverse navbar-dark bg-dark' : ' navbar-default';
5583         
5584         if (['light','white'].indexOf(this.weight) > -1) {
5585             cfg.cls += ['light','white'].indexOf(this.weight) > -1 ? ' navbar-light' : ' navbar-dark';
5586         }
5587         cfg.cls += ' bg-' + this.weight;
5588         
5589         
5590         if (['fixed-top','fixed-bottom','static-top'].indexOf(this.position)>-1) {
5591             cfg.cls += ' navbar-' + this.position + ' ' + this.position ;
5592             
5593             // tag can override this..
5594             
5595             cfg.tag = this.tag || (this.position  == 'fixed-bottom' ? 'footer' : 'header');
5596         }
5597         
5598         if (this.brand !== '') {
5599             var cp =  Roo.bootstrap.version == 4 ? cn : cn[0].cn;
5600             cp.unshift({ // changed from push ?? BS4 needs it at the start? - does this break or exsiting?
5601                 tag: 'a',
5602                 href: this.brand_href ? this.brand_href : '#',
5603                 cls: 'navbar-brand',
5604                 cn: [
5605                 this.brand
5606                 ]
5607             });
5608         }
5609         
5610         if(this.main){
5611             cfg.cls += ' main-nav';
5612         }
5613         
5614         
5615         return cfg;
5616
5617         
5618     },
5619     getHeaderChildContainer : function()
5620     {
5621         if (this.srButton && this.el.select('.navbar-header').getCount()) {
5622             return this.el.select('.navbar-header',true).first();
5623         }
5624         
5625         return this.getChildContainer();
5626     },
5627     
5628     getChildContainer : function()
5629     {
5630          
5631         return this.el.select('.roo-navbar-collapse',true).first();
5632          
5633         
5634     },
5635     
5636     initEvents : function()
5637     {
5638         Roo.bootstrap.NavHeaderbar.superclass.initEvents.call(this);
5639         
5640         if (this.autohide) {
5641             
5642             var prevScroll = 0;
5643             var ft = this.el;
5644             
5645             Roo.get(document).on('scroll',function(e) {
5646                 var ns = Roo.get(document).getScroll().top;
5647                 var os = prevScroll;
5648                 prevScroll = ns;
5649                 
5650                 if(ns > os){
5651                     ft.removeClass('slideDown');
5652                     ft.addClass('slideUp');
5653                     return;
5654                 }
5655                 ft.removeClass('slideUp');
5656                 ft.addClass('slideDown');
5657                  
5658               
5659           },this);
5660         }
5661     }    
5662     
5663 });
5664
5665
5666
5667  
5668
5669  /*
5670  * - LGPL
5671  *
5672  * navbar
5673  * 
5674  */
5675
5676 /**
5677  * @class Roo.bootstrap.NavSidebar
5678  * @extends Roo.bootstrap.Navbar
5679  * Bootstrap Sidebar class
5680  * 
5681  * @constructor
5682  * Create a new Sidebar
5683  * @param {Object} config The config object
5684  */
5685
5686
5687 Roo.bootstrap.NavSidebar = function(config){
5688     Roo.bootstrap.NavSidebar.superclass.constructor.call(this, config);
5689 };
5690
5691 Roo.extend(Roo.bootstrap.NavSidebar, Roo.bootstrap.Navbar,  {
5692     
5693     sidebar : true, // used by Navbar Item and NavbarGroup at present...
5694     
5695     getAutoCreate : function(){
5696         
5697         
5698         return  {
5699             tag: 'div',
5700             cls: 'sidebar sidebar-nav'
5701         };
5702     
5703         
5704     }
5705     
5706     
5707     
5708 });
5709
5710
5711
5712  
5713
5714  /*
5715  * - LGPL
5716  *
5717  * nav group
5718  * 
5719  */
5720
5721 /**
5722  * @class Roo.bootstrap.NavGroup
5723  * @extends Roo.bootstrap.Component
5724  * Bootstrap NavGroup class
5725  * @cfg {String} align (left|right)
5726  * @cfg {Boolean} inverse
5727  * @cfg {String} type (nav|pills|tab) default nav
5728  * @cfg {String} navId - reference Id for navbar.
5729
5730  * 
5731  * @constructor
5732  * Create a new nav group
5733  * @param {Object} config The config object
5734  */
5735
5736 Roo.bootstrap.NavGroup = function(config){
5737     Roo.bootstrap.NavGroup.superclass.constructor.call(this, config);
5738     this.navItems = [];
5739    
5740     Roo.bootstrap.NavGroup.register(this);
5741      this.addEvents({
5742         /**
5743              * @event changed
5744              * Fires when the active item changes
5745              * @param {Roo.bootstrap.NavGroup} this
5746              * @param {Roo.bootstrap.Navbar.Item} selected The item selected
5747              * @param {Roo.bootstrap.Navbar.Item} prev The previously selected item 
5748          */
5749         'changed': true
5750      });
5751     
5752 };
5753
5754 Roo.extend(Roo.bootstrap.NavGroup, Roo.bootstrap.Component,  {
5755     
5756     align: '',
5757     inverse: false,
5758     form: false,
5759     type: 'nav',
5760     navId : '',
5761     // private
5762     
5763     navItems : false, 
5764     
5765     getAutoCreate : function()
5766     {
5767         var cfg = Roo.apply({}, Roo.bootstrap.NavGroup.superclass.getAutoCreate.call(this));
5768         
5769         cfg = {
5770             tag : 'ul',
5771             cls: 'nav' 
5772         };
5773         if (Roo.bootstrap.version == 4) {
5774             if (['tabs','pills'].indexOf(this.type) != -1) {
5775                 cfg.cls += ' nav-' + this.type; 
5776             } else {
5777                 // trying to remove so header bar can right align top?
5778                 if (this.parent() && this.parent().xtype != 'NavHeaderbar') {
5779                     // do not use on header bar... 
5780                     cfg.cls += ' navbar-nav';
5781                 }
5782             }
5783             
5784         } else {
5785             if (['tabs','pills'].indexOf(this.type) != -1) {
5786                 cfg.cls += ' nav-' + this.type
5787             } else {
5788                 if (this.type !== 'nav') {
5789                     Roo.log('nav type must be nav/tabs/pills')
5790                 }
5791                 cfg.cls += ' navbar-nav'
5792             }
5793         }
5794         
5795         if (this.parent() && this.parent().sidebar) {
5796             cfg = {
5797                 tag: 'ul',
5798                 cls: 'dashboard-menu sidebar-menu'
5799             };
5800             
5801             return cfg;
5802         }
5803         
5804         if (this.form === true) {
5805             cfg = {
5806                 tag: 'form',
5807                 cls: 'navbar-form form-inline'
5808             };
5809             //nav navbar-right ml-md-auto
5810             if (this.align === 'right') {
5811                 cfg.cls += ' navbar-right ml-md-auto';
5812             } else {
5813                 cfg.cls += ' navbar-left';
5814             }
5815         }
5816         
5817         if (this.align === 'right') {
5818             cfg.cls += ' navbar-right ml-md-auto';
5819         } else {
5820             cfg.cls += ' mr-auto';
5821         }
5822         
5823         if (this.inverse) {
5824             cfg.cls += ' navbar-inverse';
5825             
5826         }
5827         
5828         
5829         return cfg;
5830     },
5831     /**
5832     * sets the active Navigation item
5833     * @param {Roo.bootstrap.NavItem} the new current navitem
5834     */
5835     setActiveItem : function(item)
5836     {
5837         var prev = false;
5838         Roo.each(this.navItems, function(v){
5839             if (v == item) {
5840                 return ;
5841             }
5842             if (v.isActive()) {
5843                 v.setActive(false, true);
5844                 prev = v;
5845                 
5846             }
5847             
5848         });
5849
5850         item.setActive(true, true);
5851         this.fireEvent('changed', this, item, prev);
5852         
5853         
5854     },
5855     /**
5856     * gets the active Navigation item
5857     * @return {Roo.bootstrap.NavItem} the current navitem
5858     */
5859     getActive : function()
5860     {
5861         
5862         var prev = false;
5863         Roo.each(this.navItems, function(v){
5864             
5865             if (v.isActive()) {
5866                 prev = v;
5867                 
5868             }
5869             
5870         });
5871         return prev;
5872     },
5873     
5874     indexOfNav : function()
5875     {
5876         
5877         var prev = false;
5878         Roo.each(this.navItems, function(v,i){
5879             
5880             if (v.isActive()) {
5881                 prev = i;
5882                 
5883             }
5884             
5885         });
5886         return prev;
5887     },
5888     /**
5889     * adds a Navigation item
5890     * @param {Roo.bootstrap.NavItem} the navitem to add
5891     */
5892     addItem : function(cfg)
5893     {
5894         if (this.form && Roo.bootstrap.version == 4) {
5895             cfg.tag = 'div';
5896         }
5897         var cn = new Roo.bootstrap.NavItem(cfg);
5898         this.register(cn);
5899         cn.parentId = this.id;
5900         cn.onRender(this.el, null);
5901         return cn;
5902     },
5903     /**
5904     * register a Navigation item
5905     * @param {Roo.bootstrap.NavItem} the navitem to add
5906     */
5907     register : function(item)
5908     {
5909         this.navItems.push( item);
5910         item.navId = this.navId;
5911     
5912     },
5913     
5914     /**
5915     * clear all the Navigation item
5916     */
5917    
5918     clearAll : function()
5919     {
5920         this.navItems = [];
5921         this.el.dom.innerHTML = '';
5922     },
5923     
5924     getNavItem: function(tabId)
5925     {
5926         var ret = false;
5927         Roo.each(this.navItems, function(e) {
5928             if (e.tabId == tabId) {
5929                ret =  e;
5930                return false;
5931             }
5932             return true;
5933             
5934         });
5935         return ret;
5936     },
5937     
5938     setActiveNext : function()
5939     {
5940         var i = this.indexOfNav(this.getActive());
5941         if (i > this.navItems.length) {
5942             return;
5943         }
5944         this.setActiveItem(this.navItems[i+1]);
5945     },
5946     setActivePrev : function()
5947     {
5948         var i = this.indexOfNav(this.getActive());
5949         if (i  < 1) {
5950             return;
5951         }
5952         this.setActiveItem(this.navItems[i-1]);
5953     },
5954     clearWasActive : function(except) {
5955         Roo.each(this.navItems, function(e) {
5956             if (e.tabId != except.tabId && e.was_active) {
5957                e.was_active = false;
5958                return false;
5959             }
5960             return true;
5961             
5962         });
5963     },
5964     getWasActive : function ()
5965     {
5966         var r = false;
5967         Roo.each(this.navItems, function(e) {
5968             if (e.was_active) {
5969                r = e;
5970                return false;
5971             }
5972             return true;
5973             
5974         });
5975         return r;
5976     }
5977     
5978     
5979 });
5980
5981  
5982 Roo.apply(Roo.bootstrap.NavGroup, {
5983     
5984     groups: {},
5985      /**
5986     * register a Navigation Group
5987     * @param {Roo.bootstrap.NavGroup} the navgroup to add
5988     */
5989     register : function(navgrp)
5990     {
5991         this.groups[navgrp.navId] = navgrp;
5992         
5993     },
5994     /**
5995     * fetch a Navigation Group based on the navigation ID
5996     * @param {string} the navgroup to add
5997     * @returns {Roo.bootstrap.NavGroup} the navgroup 
5998     */
5999     get: function(navId) {
6000         if (typeof(this.groups[navId]) == 'undefined') {
6001             return false;
6002             //this.register(new Roo.bootstrap.NavGroup({ navId : navId }));
6003         }
6004         return this.groups[navId] ;
6005     }
6006     
6007     
6008     
6009 });
6010
6011  /*
6012  * - LGPL
6013  *
6014  * row
6015  * 
6016  */
6017
6018 /**
6019  * @class Roo.bootstrap.NavItem
6020  * @extends Roo.bootstrap.Component
6021  * Bootstrap Navbar.NavItem class
6022  * @cfg {String} href  link to
6023  * @cfg {String} button_weight (default | primary | secondary | success | info | warning | danger | link ) default none
6024
6025  * @cfg {String} html content of button
6026  * @cfg {String} badge text inside badge
6027  * @cfg {String} badgecls (bg-green|bg-red|bg-yellow)the extra classes for the badge
6028  * @cfg {String} glyphicon DEPRICATED - use fa
6029  * @cfg {String} icon DEPRICATED - use fa
6030  * @cfg {String} fa - Fontawsome icon name (can add stuff to it like fa-2x)
6031  * @cfg {Boolean} active Is item active
6032  * @cfg {Boolean} disabled Is item disabled
6033  
6034  * @cfg {Boolean} preventDefault (true | false) default false
6035  * @cfg {String} tabId the tab that this item activates.
6036  * @cfg {String} tagtype (a|span) render as a href or span?
6037  * @cfg {Boolean} animateRef (true|false) link to element default false  
6038   
6039  * @constructor
6040  * Create a new Navbar Item
6041  * @param {Object} config The config object
6042  */
6043 Roo.bootstrap.NavItem = function(config){
6044     Roo.bootstrap.NavItem.superclass.constructor.call(this, config);
6045     this.addEvents({
6046         // raw events
6047         /**
6048          * @event click
6049          * The raw click event for the entire grid.
6050          * @param {Roo.EventObject} e
6051          */
6052         "click" : true,
6053          /**
6054             * @event changed
6055             * Fires when the active item active state changes
6056             * @param {Roo.bootstrap.NavItem} this
6057             * @param {boolean} state the new state
6058              
6059          */
6060         'changed': true,
6061         /**
6062             * @event scrollto
6063             * Fires when scroll to element
6064             * @param {Roo.bootstrap.NavItem} this
6065             * @param {Object} options
6066             * @param {Roo.EventObject} e
6067              
6068          */
6069         'scrollto': true
6070     });
6071    
6072 };
6073
6074 Roo.extend(Roo.bootstrap.NavItem, Roo.bootstrap.Component,  {
6075     
6076     href: false,
6077     html: '',
6078     badge: '',
6079     icon: false,
6080     fa : false,
6081     glyphicon: false,
6082     active: false,
6083     preventDefault : false,
6084     tabId : false,
6085     tagtype : 'a',
6086     tag: 'li',
6087     disabled : false,
6088     animateRef : false,
6089     was_active : false,
6090     button_weight : '',
6091     button_outline : false,
6092     
6093     navLink: false,
6094     
6095     getAutoCreate : function(){
6096          
6097         var cfg = {
6098             tag: this.tag,
6099             cls: 'nav-item'
6100         };
6101         
6102         if (this.active) {
6103             cfg.cls = typeof(cfg.cls) == 'undefined' ? 'active' : cfg.cls + ' active';
6104         }
6105         if (this.disabled) {
6106             cfg.cls += ' disabled';
6107         }
6108         
6109         // BS4 only?
6110         if (this.button_weight.length) {
6111             cfg.tag = this.href ? 'a' : 'button';
6112             cfg.html = this.html || '';
6113             cfg.cls += ' btn btn' + (this.button_outline ? '-outline' : '') + '-' + this.button_weight;
6114             if (this.href) {
6115                 cfg.href = this.href;
6116             }
6117             if (this.fa) {
6118                 cfg.html = '<i class="fa fas fa-'+this.fa+'"></i> <span>' + this.html + '</span>';
6119             }
6120             
6121             // menu .. should add dropdown-menu class - so no need for carat..
6122             
6123             if (this.badge !== '') {
6124                  
6125                 cfg.html += ' <span class="badge badge-secondary">' + this.badge + '</span>';
6126             }
6127             return cfg;
6128         }
6129         
6130         if (this.href || this.html || this.glyphicon || this.icon || this.fa) {
6131             cfg.cn = [
6132                 {
6133                     tag: this.tagtype,
6134                     href : this.href || "#",
6135                     html: this.html || ''
6136                 }
6137             ];
6138             if (this.tagtype == 'a') {
6139                 cfg.cn[0].cls = 'nav-link';
6140             }
6141             if (this.icon) {
6142                 cfg.cn[0].html = '<i class="'+this.icon+'"></i> <span>' + cfg.cn[0].html + '</span>';
6143             }
6144             if (this.fa) {
6145                 cfg.cn[0].html = '<i class="fa fas fa-'+this.fa+'"></i> <span>' + cfg.cn[0].html + '</span>';
6146             }
6147             if(this.glyphicon) {
6148                 cfg.cn[0].html = '<span class="glyphicon glyphicon-' + this.glyphicon + '"></span> '  + cfg.cn[0].html;
6149             }
6150             
6151             if (this.menu) {
6152                 
6153                 cfg.cn[0].html += " <span class='caret'></span>";
6154              
6155             }
6156             
6157             if (this.badge !== '') {
6158                  
6159                 cfg.cn[0].html += ' <span class="badge badge-secondary">' + this.badge + '</span>';
6160             }
6161         }
6162         
6163         
6164         
6165         return cfg;
6166     },
6167     onRender : function(ct, position)
6168     {
6169        // Roo.log("Call onRender: " + this.xtype);
6170         if (Roo.bootstrap.version == 4 && ct.dom.type != 'ul') {
6171             this.tag = 'div';
6172         }
6173         
6174         var ret = Roo.bootstrap.NavItem.superclass.onRender.call(this, ct, position);
6175         this.navLink = this.el.select('.nav-link',true).first();
6176         return ret;
6177     },
6178       
6179     
6180     initEvents: function() 
6181     {
6182         if (typeof (this.menu) != 'undefined') {
6183             this.menu.parentType = this.xtype;
6184             this.menu.triggerEl = this.el;
6185             this.menu = this.addxtype(Roo.apply({}, this.menu));
6186         }
6187         
6188         this.el.select('a',true).on('click', this.onClick, this);
6189         
6190         if(this.tagtype == 'span'){
6191             this.el.select('span',true).on('click', this.onClick, this);
6192         }
6193        
6194         // at this point parent should be available..
6195         this.parent().register(this);
6196     },
6197     
6198     onClick : function(e)
6199     {
6200         if (e.getTarget('.dropdown-menu-item')) {
6201             // did you click on a menu itemm.... - then don't trigger onclick..
6202             return;
6203         }
6204         
6205         if(
6206                 this.preventDefault || 
6207                 this.href == '#' 
6208         ){
6209             Roo.log("NavItem - prevent Default?");
6210             e.preventDefault();
6211         }
6212         
6213         if (this.disabled) {
6214             return;
6215         }
6216         
6217         var tg = Roo.bootstrap.TabGroup.get(this.navId);
6218         if (tg && tg.transition) {
6219             Roo.log("waiting for the transitionend");
6220             return;
6221         }
6222         
6223         
6224         
6225         //Roo.log("fire event clicked");
6226         if(this.fireEvent('click', this, e) === false){
6227             return;
6228         };
6229         
6230         if(this.tagtype == 'span'){
6231             return;
6232         }
6233         
6234         //Roo.log(this.href);
6235         var ael = this.el.select('a',true).first();
6236         //Roo.log(ael);
6237         
6238         if(ael && this.animateRef && this.href.indexOf('#') > -1){
6239             //Roo.log(["test:",ael.dom.href.split("#")[0], document.location.toString().split("#")[0]]);
6240             if (ael.dom.href.split("#")[0] != document.location.toString().split("#")[0]) {
6241                 return; // ignore... - it's a 'hash' to another page.
6242             }
6243             Roo.log("NavItem - prevent Default?");
6244             e.preventDefault();
6245             this.scrollToElement(e);
6246         }
6247         
6248         
6249         var p =  this.parent();
6250    
6251         if (['tabs','pills'].indexOf(p.type)!==-1) {
6252             if (typeof(p.setActiveItem) !== 'undefined') {
6253                 p.setActiveItem(this);
6254             }
6255         }
6256         
6257         // if parent is a navbarheader....- and link is probably a '#' page ref.. then remove the expanded menu.
6258         if (p.parentType == 'NavHeaderbar' && !this.menu) {
6259             // remove the collapsed menu expand...
6260             p.parent().el.select('.roo-navbar-collapse',true).removeClass('in');  
6261         }
6262     },
6263     
6264     isActive: function () {
6265         return this.active
6266     },
6267     setActive : function(state, fire, is_was_active)
6268     {
6269         if (this.active && !state && this.navId) {
6270             this.was_active = true;
6271             var nv = Roo.bootstrap.NavGroup.get(this.navId);
6272             if (nv) {
6273                 nv.clearWasActive(this);
6274             }
6275             
6276         }
6277         this.active = state;
6278         
6279         if (!state ) {
6280             this.el.removeClass('active');
6281             this.navLink ? this.navLink.removeClass('active') : false;
6282         } else if (!this.el.hasClass('active')) {
6283             
6284             this.el.addClass('active');
6285             if (Roo.bootstrap.version == 4 && this.navLink ) {
6286                 this.navLink.addClass('active');
6287             }
6288             
6289         }
6290         if (fire) {
6291             this.fireEvent('changed', this, state);
6292         }
6293         
6294         // show a panel if it's registered and related..
6295         
6296         if (!this.navId || !this.tabId || !state || is_was_active) {
6297             return;
6298         }
6299         
6300         var tg = Roo.bootstrap.TabGroup.get(this.navId);
6301         if (!tg) {
6302             return;
6303         }
6304         var pan = tg.getPanelByName(this.tabId);
6305         if (!pan) {
6306             return;
6307         }
6308         // if we can not flip to new panel - go back to old nav highlight..
6309         if (false == tg.showPanel(pan)) {
6310             var nv = Roo.bootstrap.NavGroup.get(this.navId);
6311             if (nv) {
6312                 var onav = nv.getWasActive();
6313                 if (onav) {
6314                     onav.setActive(true, false, true);
6315                 }
6316             }
6317             
6318         }
6319         
6320         
6321         
6322     },
6323      // this should not be here...
6324     setDisabled : function(state)
6325     {
6326         this.disabled = state;
6327         if (!state ) {
6328             this.el.removeClass('disabled');
6329         } else if (!this.el.hasClass('disabled')) {
6330             this.el.addClass('disabled');
6331         }
6332         
6333     },
6334     
6335     /**
6336      * Fetch the element to display the tooltip on.
6337      * @return {Roo.Element} defaults to this.el
6338      */
6339     tooltipEl : function()
6340     {
6341         return this.el.select('' + this.tagtype + '', true).first();
6342     },
6343     
6344     scrollToElement : function(e)
6345     {
6346         var c = document.body;
6347         
6348         /*
6349          * Firefox / IE places the overflow at the html level, unless specifically styled to behave differently.
6350          */
6351         if(Roo.isFirefox || Roo.isIE || Roo.isIE11){
6352             c = document.documentElement;
6353         }
6354         
6355         var target = Roo.get(c).select('a[name=' + this.href.split('#')[1] +']', true).first();
6356         
6357         if(!target){
6358             return;
6359         }
6360
6361         var o = target.calcOffsetsTo(c);
6362         
6363         var options = {
6364             target : target,
6365             value : o[1]
6366         };
6367         
6368         this.fireEvent('scrollto', this, options, e);
6369         
6370         Roo.get(c).scrollTo('top', options.value, true);
6371         
6372         return;
6373     }
6374 });
6375  
6376
6377  /*
6378  * - LGPL
6379  *
6380  * sidebar item
6381  *
6382  *  li
6383  *    <span> icon </span>
6384  *    <span> text </span>
6385  *    <span>badge </span>
6386  */
6387
6388 /**
6389  * @class Roo.bootstrap.NavSidebarItem
6390  * @extends Roo.bootstrap.NavItem
6391  * Bootstrap Navbar.NavSidebarItem class
6392  * {String} badgeWeight (default|primary|success|info|warning|danger)the extra classes for the badge
6393  * {Boolean} open is the menu open
6394  * {Boolean} buttonView use button as the tigger el rather that a (default false)
6395  * {String} buttonWeight (default|primary|success|info|warning|danger)the extra classes for the button
6396  * {String} buttonSize (sm|md|lg)the extra classes for the button
6397  * {Boolean} showArrow show arrow next to the text (default true)
6398  * @constructor
6399  * Create a new Navbar Button
6400  * @param {Object} config The config object
6401  */
6402 Roo.bootstrap.NavSidebarItem = function(config){
6403     Roo.bootstrap.NavSidebarItem.superclass.constructor.call(this, config);
6404     this.addEvents({
6405         // raw events
6406         /**
6407          * @event click
6408          * The raw click event for the entire grid.
6409          * @param {Roo.EventObject} e
6410          */
6411         "click" : true,
6412          /**
6413             * @event changed
6414             * Fires when the active item active state changes
6415             * @param {Roo.bootstrap.NavSidebarItem} this
6416             * @param {boolean} state the new state
6417              
6418          */
6419         'changed': true
6420     });
6421    
6422 };
6423
6424 Roo.extend(Roo.bootstrap.NavSidebarItem, Roo.bootstrap.NavItem,  {
6425     
6426     badgeWeight : 'default',
6427     
6428     open: false,
6429     
6430     buttonView : false,
6431     
6432     buttonWeight : 'default',
6433     
6434     buttonSize : 'md',
6435     
6436     showArrow : true,
6437     
6438     getAutoCreate : function(){
6439         
6440         
6441         var a = {
6442                 tag: 'a',
6443                 href : this.href || '#',
6444                 cls: '',
6445                 html : '',
6446                 cn : []
6447         };
6448         
6449         if(this.buttonView){
6450             a = {
6451                 tag: 'button',
6452                 href : this.href || '#',
6453                 cls: 'btn btn-' + this.buttonWeight + ' btn-' + this.buttonSize + 'roo-button-dropdown-toggle',
6454                 html : this.html,
6455                 cn : []
6456             };
6457         }
6458         
6459         var cfg = {
6460             tag: 'li',
6461             cls: '',
6462             cn: [ a ]
6463         };
6464         
6465         if (this.active) {
6466             cfg.cls += ' active';
6467         }
6468         
6469         if (this.disabled) {
6470             cfg.cls += ' disabled';
6471         }
6472         if (this.open) {
6473             cfg.cls += ' open x-open';
6474         }
6475         // left icon..
6476         if (this.glyphicon || this.icon) {
6477             var c = this.glyphicon  ? ('glyphicon glyphicon-'+this.glyphicon)  : this.icon;
6478             a.cn.push({ tag : 'i', cls : c }) ;
6479         }
6480         
6481         if(!this.buttonView){
6482             var span = {
6483                 tag: 'span',
6484                 html : this.html || ''
6485             };
6486
6487             a.cn.push(span);
6488             
6489         }
6490         
6491         if (this.badge !== '') {
6492             a.cn.push({ tag: 'span',  cls : 'badge pull-right badge-' + this.badgeWeight, html: this.badge }); 
6493         }
6494         
6495         if (this.menu) {
6496             
6497             if(this.showArrow){
6498                 a.cn.push({ tag : 'i', cls : 'glyphicon glyphicon-chevron-down pull-right'});
6499             }
6500             
6501             a.cls += ' dropdown-toggle treeview' ;
6502         }
6503         
6504         return cfg;
6505     },
6506     
6507     initEvents : function()
6508     { 
6509         if (typeof (this.menu) != 'undefined') {
6510             this.menu.parentType = this.xtype;
6511             this.menu.triggerEl = this.el;
6512             this.menu = this.addxtype(Roo.apply({}, this.menu));
6513         }
6514         
6515         this.el.on('click', this.onClick, this);
6516         
6517         if(this.badge !== ''){
6518             this.badgeEl = this.el.select('.badge', true).first().setVisibilityMode(Roo.Element.DISPLAY);
6519         }
6520         
6521     },
6522     
6523     onClick : function(e)
6524     {
6525         if(this.disabled){
6526             e.preventDefault();
6527             return;
6528         }
6529         
6530         if(this.preventDefault){
6531             e.preventDefault();
6532         }
6533         
6534         this.fireEvent('click', this, e);
6535     },
6536     
6537     disable : function()
6538     {
6539         this.setDisabled(true);
6540     },
6541     
6542     enable : function()
6543     {
6544         this.setDisabled(false);
6545     },
6546     
6547     setDisabled : function(state)
6548     {
6549         if(this.disabled == state){
6550             return;
6551         }
6552         
6553         this.disabled = state;
6554         
6555         if (state) {
6556             this.el.addClass('disabled');
6557             return;
6558         }
6559         
6560         this.el.removeClass('disabled');
6561         
6562         return;
6563     },
6564     
6565     setActive : function(state)
6566     {
6567         if(this.active == state){
6568             return;
6569         }
6570         
6571         this.active = state;
6572         
6573         if (state) {
6574             this.el.addClass('active');
6575             return;
6576         }
6577         
6578         this.el.removeClass('active');
6579         
6580         return;
6581     },
6582     
6583     isActive: function () 
6584     {
6585         return this.active;
6586     },
6587     
6588     setBadge : function(str)
6589     {
6590         if(!this.badgeEl){
6591             return;
6592         }
6593         
6594         this.badgeEl.dom.innerHTML = str;
6595     }
6596     
6597    
6598      
6599  
6600 });
6601  
6602
6603  /*
6604  * - LGPL
6605  *
6606  * row
6607  * 
6608  */
6609
6610 /**
6611  * @class Roo.bootstrap.Row
6612  * @extends Roo.bootstrap.Component
6613  * Bootstrap Row class (contains columns...)
6614  * 
6615  * @constructor
6616  * Create a new Row
6617  * @param {Object} config The config object
6618  */
6619
6620 Roo.bootstrap.Row = function(config){
6621     Roo.bootstrap.Row.superclass.constructor.call(this, config);
6622 };
6623
6624 Roo.extend(Roo.bootstrap.Row, Roo.bootstrap.Component,  {
6625     
6626     getAutoCreate : function(){
6627        return {
6628             cls: 'row clearfix'
6629        };
6630     }
6631     
6632     
6633 });
6634
6635  
6636
6637  /*
6638  * - LGPL
6639  *
6640  * pagination
6641  * 
6642  */
6643
6644 /**
6645  * @class Roo.bootstrap.Pagination
6646  * @extends Roo.bootstrap.Component
6647  * Bootstrap Pagination class
6648  * @cfg {String} size xs | sm | md | lg
6649  * @cfg {Boolean} inverse false | true
6650  * 
6651  * @constructor
6652  * Create a new Pagination
6653  * @param {Object} config The config object
6654  */
6655
6656 Roo.bootstrap.Pagination = function(config){
6657     Roo.bootstrap.Pagination.superclass.constructor.call(this, config);
6658 };
6659
6660 Roo.extend(Roo.bootstrap.Pagination, Roo.bootstrap.Component,  {
6661     
6662     cls: false,
6663     size: false,
6664     inverse: false,
6665     
6666     getAutoCreate : function(){
6667         var cfg = {
6668             tag: 'ul',
6669                 cls: 'pagination'
6670         };
6671         if (this.inverse) {
6672             cfg.cls += ' inverse';
6673         }
6674         if (this.html) {
6675             cfg.html=this.html;
6676         }
6677         if (this.cls) {
6678             cfg.cls += " " + this.cls;
6679         }
6680         return cfg;
6681     }
6682    
6683 });
6684
6685  
6686
6687  /*
6688  * - LGPL
6689  *
6690  * Pagination item
6691  * 
6692  */
6693
6694
6695 /**
6696  * @class Roo.bootstrap.PaginationItem
6697  * @extends Roo.bootstrap.Component
6698  * Bootstrap PaginationItem class
6699  * @cfg {String} html text
6700  * @cfg {String} href the link
6701  * @cfg {Boolean} preventDefault (true | false) default true
6702  * @cfg {Boolean} active (true | false) default false
6703  * @cfg {Boolean} disabled default false
6704  * 
6705  * 
6706  * @constructor
6707  * Create a new PaginationItem
6708  * @param {Object} config The config object
6709  */
6710
6711
6712 Roo.bootstrap.PaginationItem = function(config){
6713     Roo.bootstrap.PaginationItem.superclass.constructor.call(this, config);
6714     this.addEvents({
6715         // raw events
6716         /**
6717          * @event click
6718          * The raw click event for the entire grid.
6719          * @param {Roo.EventObject} e
6720          */
6721         "click" : true
6722     });
6723 };
6724
6725 Roo.extend(Roo.bootstrap.PaginationItem, Roo.bootstrap.Component,  {
6726     
6727     href : false,
6728     html : false,
6729     preventDefault: true,
6730     active : false,
6731     cls : false,
6732     disabled: false,
6733     
6734     getAutoCreate : function(){
6735         var cfg= {
6736             tag: 'li',
6737             cn: [
6738                 {
6739                     tag : 'a',
6740                     href : this.href ? this.href : '#',
6741                     html : this.html ? this.html : ''
6742                 }
6743             ]
6744         };
6745         
6746         if(this.cls){
6747             cfg.cls = this.cls;
6748         }
6749         
6750         if(this.disabled){
6751             cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' disabled' : 'disabled';
6752         }
6753         
6754         if(this.active){
6755             cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' active' : 'active';
6756         }
6757         
6758         return cfg;
6759     },
6760     
6761     initEvents: function() {
6762         
6763         this.el.on('click', this.onClick, this);
6764         
6765     },
6766     onClick : function(e)
6767     {
6768         Roo.log('PaginationItem on click ');
6769         if(this.preventDefault){
6770             e.preventDefault();
6771         }
6772         
6773         if(this.disabled){
6774             return;
6775         }
6776         
6777         this.fireEvent('click', this, e);
6778     }
6779    
6780 });
6781
6782  
6783
6784  /*
6785  * - LGPL
6786  *
6787  * slider
6788  * 
6789  */
6790
6791
6792 /**
6793  * @class Roo.bootstrap.Slider
6794  * @extends Roo.bootstrap.Component
6795  * Bootstrap Slider class
6796  *    
6797  * @constructor
6798  * Create a new Slider
6799  * @param {Object} config The config object
6800  */
6801
6802 Roo.bootstrap.Slider = function(config){
6803     Roo.bootstrap.Slider.superclass.constructor.call(this, config);
6804 };
6805
6806 Roo.extend(Roo.bootstrap.Slider, Roo.bootstrap.Component,  {
6807     
6808     getAutoCreate : function(){
6809         
6810         var cfg = {
6811             tag: 'div',
6812             cls: 'slider slider-sample1 vertical-handler ui-slider ui-slider-horizontal ui-widget ui-widget-content ui-corner-all',
6813             cn: [
6814                 {
6815                     tag: 'a',
6816                     cls: 'ui-slider-handle ui-state-default ui-corner-all'
6817                 }
6818             ]
6819         };
6820         
6821         return cfg;
6822     }
6823    
6824 });
6825
6826  /*
6827  * Based on:
6828  * Ext JS Library 1.1.1
6829  * Copyright(c) 2006-2007, Ext JS, LLC.
6830  *
6831  * Originally Released Under LGPL - original licence link has changed is not relivant.
6832  *
6833  * Fork - LGPL
6834  * <script type="text/javascript">
6835  */
6836  
6837
6838 /**
6839  * @class Roo.grid.ColumnModel
6840  * @extends Roo.util.Observable
6841  * This is the default implementation of a ColumnModel used by the Grid. It defines
6842  * the columns in the grid.
6843  * <br>Usage:<br>
6844  <pre><code>
6845  var colModel = new Roo.grid.ColumnModel([
6846         {header: "Ticker", width: 60, sortable: true, locked: true},
6847         {header: "Company Name", width: 150, sortable: true},
6848         {header: "Market Cap.", width: 100, sortable: true},
6849         {header: "$ Sales", width: 100, sortable: true, renderer: money},
6850         {header: "Employees", width: 100, sortable: true, resizable: false}
6851  ]);
6852  </code></pre>
6853  * <p>
6854  
6855  * The config options listed for this class are options which may appear in each
6856  * individual column definition.
6857  * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
6858  * @constructor
6859  * @param {Object} config An Array of column config objects. See this class's
6860  * config objects for details.
6861 */
6862 Roo.grid.ColumnModel = function(config){
6863         /**
6864      * The config passed into the constructor
6865      */
6866     this.config = config;
6867     this.lookup = {};
6868
6869     // if no id, create one
6870     // if the column does not have a dataIndex mapping,
6871     // map it to the order it is in the config
6872     for(var i = 0, len = config.length; i < len; i++){
6873         var c = config[i];
6874         if(typeof c.dataIndex == "undefined"){
6875             c.dataIndex = i;
6876         }
6877         if(typeof c.renderer == "string"){
6878             c.renderer = Roo.util.Format[c.renderer];
6879         }
6880         if(typeof c.id == "undefined"){
6881             c.id = Roo.id();
6882         }
6883         if(c.editor && c.editor.xtype){
6884             c.editor  = Roo.factory(c.editor, Roo.grid);
6885         }
6886         if(c.editor && c.editor.isFormField){
6887             c.editor = new Roo.grid.GridEditor(c.editor);
6888         }
6889         this.lookup[c.id] = c;
6890     }
6891
6892     /**
6893      * The width of columns which have no width specified (defaults to 100)
6894      * @type Number
6895      */
6896     this.defaultWidth = 100;
6897
6898     /**
6899      * Default sortable of columns which have no sortable specified (defaults to false)
6900      * @type Boolean
6901      */
6902     this.defaultSortable = false;
6903
6904     this.addEvents({
6905         /**
6906              * @event widthchange
6907              * Fires when the width of a column changes.
6908              * @param {ColumnModel} this
6909              * @param {Number} columnIndex The column index
6910              * @param {Number} newWidth The new width
6911              */
6912             "widthchange": true,
6913         /**
6914              * @event headerchange
6915              * Fires when the text of a header changes.
6916              * @param {ColumnModel} this
6917              * @param {Number} columnIndex The column index
6918              * @param {Number} newText The new header text
6919              */
6920             "headerchange": true,
6921         /**
6922              * @event hiddenchange
6923              * Fires when a column is hidden or "unhidden".
6924              * @param {ColumnModel} this
6925              * @param {Number} columnIndex The column index
6926              * @param {Boolean} hidden true if hidden, false otherwise
6927              */
6928             "hiddenchange": true,
6929             /**
6930          * @event columnmoved
6931          * Fires when a column is moved.
6932          * @param {ColumnModel} this
6933          * @param {Number} oldIndex
6934          * @param {Number} newIndex
6935          */
6936         "columnmoved" : true,
6937         /**
6938          * @event columlockchange
6939          * Fires when a column's locked state is changed
6940          * @param {ColumnModel} this
6941          * @param {Number} colIndex
6942          * @param {Boolean} locked true if locked
6943          */
6944         "columnlockchange" : true
6945     });
6946     Roo.grid.ColumnModel.superclass.constructor.call(this);
6947 };
6948 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
6949     /**
6950      * @cfg {String} header The header text to display in the Grid view.
6951      */
6952     /**
6953      * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
6954      * {@link Roo.data.Record} definition from which to draw the column's value. If not
6955      * specified, the column's index is used as an index into the Record's data Array.
6956      */
6957     /**
6958      * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
6959      * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
6960      */
6961     /**
6962      * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
6963      * Defaults to the value of the {@link #defaultSortable} property.
6964      * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
6965      */
6966     /**
6967      * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid.  Defaults to false.
6968      */
6969     /**
6970      * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed.  Defaults to false.
6971      */
6972     /**
6973      * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
6974      */
6975     /**
6976      * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
6977      */
6978     /**
6979      * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
6980      * given the cell's data value. See {@link #setRenderer}. If not specified, the
6981      * default renderer returns the escaped data value. If an object is returned (bootstrap only)
6982      * then it is treated as a Roo Component object instance, and it is rendered after the initial row is rendered
6983      */
6984        /**
6985      * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor 
6986      */
6987     /**
6988      * @cfg {String} align (Optional) Set the CSS text-align property of the column.  Defaults to undefined.
6989      */
6990     /**
6991      * @cfg {String} valign (Optional) Set the CSS vertical-align property of the column (eg. middle, top, bottom etc).  Defaults to undefined.
6992      */
6993     /**
6994      * @cfg {String} cursor (Optional)
6995      */
6996     /**
6997      * @cfg {String} tooltip (Optional)
6998      */
6999     /**
7000      * @cfg {Number} xs (Optional)
7001      */
7002     /**
7003      * @cfg {Number} sm (Optional)
7004      */
7005     /**
7006      * @cfg {Number} md (Optional)
7007      */
7008     /**
7009      * @cfg {Number} lg (Optional)
7010      */
7011     /**
7012      * Returns the id of the column at the specified index.
7013      * @param {Number} index The column index
7014      * @return {String} the id
7015      */
7016     getColumnId : function(index){
7017         return this.config[index].id;
7018     },
7019
7020     /**
7021      * Returns the column for a specified id.
7022      * @param {String} id The column id
7023      * @return {Object} the column
7024      */
7025     getColumnById : function(id){
7026         return this.lookup[id];
7027     },
7028
7029     
7030     /**
7031      * Returns the column for a specified dataIndex.
7032      * @param {String} dataIndex The column dataIndex
7033      * @return {Object|Boolean} the column or false if not found
7034      */
7035     getColumnByDataIndex: function(dataIndex){
7036         var index = this.findColumnIndex(dataIndex);
7037         return index > -1 ? this.config[index] : false;
7038     },
7039     
7040     /**
7041      * Returns the index for a specified column id.
7042      * @param {String} id The column id
7043      * @return {Number} the index, or -1 if not found
7044      */
7045     getIndexById : function(id){
7046         for(var i = 0, len = this.config.length; i < len; i++){
7047             if(this.config[i].id == id){
7048                 return i;
7049             }
7050         }
7051         return -1;
7052     },
7053     
7054     /**
7055      * Returns the index for a specified column dataIndex.
7056      * @param {String} dataIndex The column dataIndex
7057      * @return {Number} the index, or -1 if not found
7058      */
7059     
7060     findColumnIndex : function(dataIndex){
7061         for(var i = 0, len = this.config.length; i < len; i++){
7062             if(this.config[i].dataIndex == dataIndex){
7063                 return i;
7064             }
7065         }
7066         return -1;
7067     },
7068     
7069     
7070     moveColumn : function(oldIndex, newIndex){
7071         var c = this.config[oldIndex];
7072         this.config.splice(oldIndex, 1);
7073         this.config.splice(newIndex, 0, c);
7074         this.dataMap = null;
7075         this.fireEvent("columnmoved", this, oldIndex, newIndex);
7076     },
7077
7078     isLocked : function(colIndex){
7079         return this.config[colIndex].locked === true;
7080     },
7081
7082     setLocked : function(colIndex, value, suppressEvent){
7083         if(this.isLocked(colIndex) == value){
7084             return;
7085         }
7086         this.config[colIndex].locked = value;
7087         if(!suppressEvent){
7088             this.fireEvent("columnlockchange", this, colIndex, value);
7089         }
7090     },
7091
7092     getTotalLockedWidth : function(){
7093         var totalWidth = 0;
7094         for(var i = 0; i < this.config.length; i++){
7095             if(this.isLocked(i) && !this.isHidden(i)){
7096                 this.totalWidth += this.getColumnWidth(i);
7097             }
7098         }
7099         return totalWidth;
7100     },
7101
7102     getLockedCount : function(){
7103         for(var i = 0, len = this.config.length; i < len; i++){
7104             if(!this.isLocked(i)){
7105                 return i;
7106             }
7107         }
7108         
7109         return this.config.length;
7110     },
7111
7112     /**
7113      * Returns the number of columns.
7114      * @return {Number}
7115      */
7116     getColumnCount : function(visibleOnly){
7117         if(visibleOnly === true){
7118             var c = 0;
7119             for(var i = 0, len = this.config.length; i < len; i++){
7120                 if(!this.isHidden(i)){
7121                     c++;
7122                 }
7123             }
7124             return c;
7125         }
7126         return this.config.length;
7127     },
7128
7129     /**
7130      * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
7131      * @param {Function} fn
7132      * @param {Object} scope (optional)
7133      * @return {Array} result
7134      */
7135     getColumnsBy : function(fn, scope){
7136         var r = [];
7137         for(var i = 0, len = this.config.length; i < len; i++){
7138             var c = this.config[i];
7139             if(fn.call(scope||this, c, i) === true){
7140                 r[r.length] = c;
7141             }
7142         }
7143         return r;
7144     },
7145
7146     /**
7147      * Returns true if the specified column is sortable.
7148      * @param {Number} col The column index
7149      * @return {Boolean}
7150      */
7151     isSortable : function(col){
7152         if(typeof this.config[col].sortable == "undefined"){
7153             return this.defaultSortable;
7154         }
7155         return this.config[col].sortable;
7156     },
7157
7158     /**
7159      * Returns the rendering (formatting) function defined for the column.
7160      * @param {Number} col The column index.
7161      * @return {Function} The function used to render the cell. See {@link #setRenderer}.
7162      */
7163     getRenderer : function(col){
7164         if(!this.config[col].renderer){
7165             return Roo.grid.ColumnModel.defaultRenderer;
7166         }
7167         return this.config[col].renderer;
7168     },
7169
7170     /**
7171      * Sets the rendering (formatting) function for a column.
7172      * @param {Number} col The column index
7173      * @param {Function} fn The function to use to process the cell's raw data
7174      * to return HTML markup for the grid view. The render function is called with
7175      * the following parameters:<ul>
7176      * <li>Data value.</li>
7177      * <li>Cell metadata. An object in which you may set the following attributes:<ul>
7178      * <li>css A CSS style string to apply to the table cell.</li>
7179      * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
7180      * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
7181      * <li>Row index</li>
7182      * <li>Column index</li>
7183      * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
7184      */
7185     setRenderer : function(col, fn){
7186         this.config[col].renderer = fn;
7187     },
7188
7189     /**
7190      * Returns the width for the specified column.
7191      * @param {Number} col The column index
7192      * @return {Number}
7193      */
7194     getColumnWidth : function(col){
7195         return this.config[col].width * 1 || this.defaultWidth;
7196     },
7197
7198     /**
7199      * Sets the width for a column.
7200      * @param {Number} col The column index
7201      * @param {Number} width The new width
7202      */
7203     setColumnWidth : function(col, width, suppressEvent){
7204         this.config[col].width = width;
7205         this.totalWidth = null;
7206         if(!suppressEvent){
7207              this.fireEvent("widthchange", this, col, width);
7208         }
7209     },
7210
7211     /**
7212      * Returns the total width of all columns.
7213      * @param {Boolean} includeHidden True to include hidden column widths
7214      * @return {Number}
7215      */
7216     getTotalWidth : function(includeHidden){
7217         if(!this.totalWidth){
7218             this.totalWidth = 0;
7219             for(var i = 0, len = this.config.length; i < len; i++){
7220                 if(includeHidden || !this.isHidden(i)){
7221                     this.totalWidth += this.getColumnWidth(i);
7222                 }
7223             }
7224         }
7225         return this.totalWidth;
7226     },
7227
7228     /**
7229      * Returns the header for the specified column.
7230      * @param {Number} col The column index
7231      * @return {String}
7232      */
7233     getColumnHeader : function(col){
7234         return this.config[col].header;
7235     },
7236
7237     /**
7238      * Sets the header for a column.
7239      * @param {Number} col The column index
7240      * @param {String} header The new header
7241      */
7242     setColumnHeader : function(col, header){
7243         this.config[col].header = header;
7244         this.fireEvent("headerchange", this, col, header);
7245     },
7246
7247     /**
7248      * Returns the tooltip for the specified column.
7249      * @param {Number} col The column index
7250      * @return {String}
7251      */
7252     getColumnTooltip : function(col){
7253             return this.config[col].tooltip;
7254     },
7255     /**
7256      * Sets the tooltip for a column.
7257      * @param {Number} col The column index
7258      * @param {String} tooltip The new tooltip
7259      */
7260     setColumnTooltip : function(col, tooltip){
7261             this.config[col].tooltip = tooltip;
7262     },
7263
7264     /**
7265      * Returns the dataIndex for the specified column.
7266      * @param {Number} col The column index
7267      * @return {Number}
7268      */
7269     getDataIndex : function(col){
7270         return this.config[col].dataIndex;
7271     },
7272
7273     /**
7274      * Sets the dataIndex for a column.
7275      * @param {Number} col The column index
7276      * @param {Number} dataIndex The new dataIndex
7277      */
7278     setDataIndex : function(col, dataIndex){
7279         this.config[col].dataIndex = dataIndex;
7280     },
7281
7282     
7283     
7284     /**
7285      * Returns true if the cell is editable.
7286      * @param {Number} colIndex The column index
7287      * @param {Number} rowIndex The row index - this is nto actually used..?
7288      * @return {Boolean}
7289      */
7290     isCellEditable : function(colIndex, rowIndex){
7291         return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
7292     },
7293
7294     /**
7295      * Returns the editor defined for the cell/column.
7296      * return false or null to disable editing.
7297      * @param {Number} colIndex The column index
7298      * @param {Number} rowIndex The row index
7299      * @return {Object}
7300      */
7301     getCellEditor : function(colIndex, rowIndex){
7302         return this.config[colIndex].editor;
7303     },
7304
7305     /**
7306      * Sets if a column is editable.
7307      * @param {Number} col The column index
7308      * @param {Boolean} editable True if the column is editable
7309      */
7310     setEditable : function(col, editable){
7311         this.config[col].editable = editable;
7312     },
7313
7314
7315     /**
7316      * Returns true if the column is hidden.
7317      * @param {Number} colIndex The column index
7318      * @return {Boolean}
7319      */
7320     isHidden : function(colIndex){
7321         return this.config[colIndex].hidden;
7322     },
7323
7324
7325     /**
7326      * Returns true if the column width cannot be changed
7327      */
7328     isFixed : function(colIndex){
7329         return this.config[colIndex].fixed;
7330     },
7331
7332     /**
7333      * Returns true if the column can be resized
7334      * @return {Boolean}
7335      */
7336     isResizable : function(colIndex){
7337         return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
7338     },
7339     /**
7340      * Sets if a column is hidden.
7341      * @param {Number} colIndex The column index
7342      * @param {Boolean} hidden True if the column is hidden
7343      */
7344     setHidden : function(colIndex, hidden){
7345         this.config[colIndex].hidden = hidden;
7346         this.totalWidth = null;
7347         this.fireEvent("hiddenchange", this, colIndex, hidden);
7348     },
7349
7350     /**
7351      * Sets the editor for a column.
7352      * @param {Number} col The column index
7353      * @param {Object} editor The editor object
7354      */
7355     setEditor : function(col, editor){
7356         this.config[col].editor = editor;
7357     }
7358 });
7359
7360 Roo.grid.ColumnModel.defaultRenderer = function(value)
7361 {
7362     if(typeof value == "object") {
7363         return value;
7364     }
7365         if(typeof value == "string" && value.length < 1){
7366             return "&#160;";
7367         }
7368     
7369         return String.format("{0}", value);
7370 };
7371
7372 // Alias for backwards compatibility
7373 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
7374 /*
7375  * Based on:
7376  * Ext JS Library 1.1.1
7377  * Copyright(c) 2006-2007, Ext JS, LLC.
7378  *
7379  * Originally Released Under LGPL - original licence link has changed is not relivant.
7380  *
7381  * Fork - LGPL
7382  * <script type="text/javascript">
7383  */
7384  
7385 /**
7386  * @class Roo.LoadMask
7387  * A simple utility class for generically masking elements while loading data.  If the element being masked has
7388  * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
7389  * process and the mask element will be cached for reuse.  For all other elements, this mask will replace the
7390  * element's UpdateManager load indicator and will be destroyed after the initial load.
7391  * @constructor
7392  * Create a new LoadMask
7393  * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
7394  * @param {Object} config The config object
7395  */
7396 Roo.LoadMask = function(el, config){
7397     this.el = Roo.get(el);
7398     Roo.apply(this, config);
7399     if(this.store){
7400         this.store.on('beforeload', this.onBeforeLoad, this);
7401         this.store.on('load', this.onLoad, this);
7402         this.store.on('loadexception', this.onLoadException, this);
7403         this.removeMask = false;
7404     }else{
7405         var um = this.el.getUpdateManager();
7406         um.showLoadIndicator = false; // disable the default indicator
7407         um.on('beforeupdate', this.onBeforeLoad, this);
7408         um.on('update', this.onLoad, this);
7409         um.on('failure', this.onLoad, this);
7410         this.removeMask = true;
7411     }
7412 };
7413
7414 Roo.LoadMask.prototype = {
7415     /**
7416      * @cfg {Boolean} removeMask
7417      * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
7418      * False to persist the mask element reference for multiple uses (e.g., for paged data widgets).  Defaults to false.
7419      */
7420     /**
7421      * @cfg {String} msg
7422      * The text to display in a centered loading message box (defaults to 'Loading...')
7423      */
7424     msg : 'Loading...',
7425     /**
7426      * @cfg {String} msgCls
7427      * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
7428      */
7429     msgCls : 'x-mask-loading',
7430
7431     /**
7432      * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
7433      * @type Boolean
7434      */
7435     disabled: false,
7436
7437     /**
7438      * Disables the mask to prevent it from being displayed
7439      */
7440     disable : function(){
7441        this.disabled = true;
7442     },
7443
7444     /**
7445      * Enables the mask so that it can be displayed
7446      */
7447     enable : function(){
7448         this.disabled = false;
7449     },
7450     
7451     onLoadException : function()
7452     {
7453         Roo.log(arguments);
7454         
7455         if (typeof(arguments[3]) != 'undefined') {
7456             Roo.MessageBox.alert("Error loading",arguments[3]);
7457         } 
7458         /*
7459         try {
7460             if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
7461                 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
7462             }   
7463         } catch(e) {
7464             
7465         }
7466         */
7467     
7468         (function() { this.el.unmask(this.removeMask); }).defer(50, this);
7469     },
7470     // private
7471     onLoad : function()
7472     {
7473         (function() { this.el.unmask(this.removeMask); }).defer(50, this);
7474     },
7475
7476     // private
7477     onBeforeLoad : function(){
7478         if(!this.disabled){
7479             (function() { this.el.mask(this.msg, this.msgCls); }).defer(50, this);
7480         }
7481     },
7482
7483     // private
7484     destroy : function(){
7485         if(this.store){
7486             this.store.un('beforeload', this.onBeforeLoad, this);
7487             this.store.un('load', this.onLoad, this);
7488             this.store.un('loadexception', this.onLoadException, this);
7489         }else{
7490             var um = this.el.getUpdateManager();
7491             um.un('beforeupdate', this.onBeforeLoad, this);
7492             um.un('update', this.onLoad, this);
7493             um.un('failure', this.onLoad, this);
7494         }
7495     }
7496 };/*
7497  * - LGPL
7498  *
7499  * table
7500  * 
7501  */
7502
7503 /**
7504  * @class Roo.bootstrap.Table
7505  * @extends Roo.bootstrap.Component
7506  * Bootstrap Table class
7507  * @cfg {String} cls table class
7508  * @cfg {String} align (left|center|right) Specifies the alignment of a table according to surrounding text
7509  * @cfg {String} bgcolor Specifies the background color for a table
7510  * @cfg {Number} border Specifies whether the table cells should have borders or not
7511  * @cfg {Number} cellpadding Specifies the space between the cell wall and the cell content
7512  * @cfg {Number} cellspacing Specifies the space between cells
7513  * @cfg {String} frame Specifies which parts of the outside borders that should be visible
7514  * @cfg {String} rules Specifies which parts of the inside borders that should be visible
7515  * @cfg {String} sortable Specifies that the table should be sortable
7516  * @cfg {String} summary Specifies a summary of the content of a table
7517  * @cfg {Number} width Specifies the width of a table
7518  * @cfg {String} layout table layout (auto | fixed | initial | inherit)
7519  * 
7520  * @cfg {boolean} striped Should the rows be alternative striped
7521  * @cfg {boolean} bordered Add borders to the table
7522  * @cfg {boolean} hover Add hover highlighting
7523  * @cfg {boolean} condensed Format condensed
7524  * @cfg {boolean} responsive Format condensed
7525  * @cfg {Boolean} loadMask (true|false) default false
7526  * @cfg {Boolean} footerShow (true|false) generate tfoot, default true
7527  * @cfg {Boolean} headerShow (true|false) generate thead, default true
7528  * @cfg {Boolean} rowSelection (true|false) default false
7529  * @cfg {Boolean} cellSelection (true|false) default false
7530  * @cfg {Boolean} scrollBody (true|false) default false - body scrolled / fixed header
7531  * @cfg {Roo.bootstrap.PagingToolbar} footer  a paging toolbar
7532  * @cfg {Boolean} lazyLoad  auto load data while scrolling to the end (default false)
7533  * @cfg {Boolean} auto_hide_footer  auto hide footer if only one page (default false)
7534  
7535  * 
7536  * @constructor
7537  * Create a new Table
7538  * @param {Object} config The config object
7539  */
7540
7541 Roo.bootstrap.Table = function(config){
7542     Roo.bootstrap.Table.superclass.constructor.call(this, config);
7543     
7544   
7545     
7546     // BC...
7547     this.rowSelection = (typeof(config.rowSelection) != 'undefined') ? config.rowSelection : this.rowSelection;
7548     this.cellSelection = (typeof(config.cellSelection) != 'undefined') ? config.cellSelection : this.cellSelection;
7549     this.headerShow = (typeof(config.thead) != 'undefined') ? config.thead : this.headerShow;
7550     this.footerShow = (typeof(config.tfoot) != 'undefined') ? config.tfoot : this.footerShow;
7551     
7552     this.sm = this.sm || {xtype: 'RowSelectionModel'};
7553     if (this.sm) {
7554         this.sm.grid = this;
7555         this.selModel = Roo.factory(this.sm, Roo.bootstrap.Table);
7556         this.sm = this.selModel;
7557         this.sm.xmodule = this.xmodule || false;
7558     }
7559     
7560     if (this.cm && typeof(this.cm.config) == 'undefined') {
7561         this.colModel = new Roo.grid.ColumnModel(this.cm);
7562         this.cm = this.colModel;
7563         this.cm.xmodule = this.xmodule || false;
7564     }
7565     if (this.store) {
7566         this.store= Roo.factory(this.store, Roo.data);
7567         this.ds = this.store;
7568         this.ds.xmodule = this.xmodule || false;
7569          
7570     }
7571     if (this.footer && this.store) {
7572         this.footer.dataSource = this.ds;
7573         this.footer = Roo.factory(this.footer);
7574     }
7575     
7576     /** @private */
7577     this.addEvents({
7578         /**
7579          * @event cellclick
7580          * Fires when a cell is clicked
7581          * @param {Roo.bootstrap.Table} this
7582          * @param {Roo.Element} el
7583          * @param {Number} rowIndex
7584          * @param {Number} columnIndex
7585          * @param {Roo.EventObject} e
7586          */
7587         "cellclick" : true,
7588         /**
7589          * @event celldblclick
7590          * Fires when a cell is double clicked
7591          * @param {Roo.bootstrap.Table} this
7592          * @param {Roo.Element} el
7593          * @param {Number} rowIndex
7594          * @param {Number} columnIndex
7595          * @param {Roo.EventObject} e
7596          */
7597         "celldblclick" : true,
7598         /**
7599          * @event rowclick
7600          * Fires when a row is clicked
7601          * @param {Roo.bootstrap.Table} this
7602          * @param {Roo.Element} el
7603          * @param {Number} rowIndex
7604          * @param {Roo.EventObject} e
7605          */
7606         "rowclick" : true,
7607         /**
7608          * @event rowdblclick
7609          * Fires when a row is double clicked
7610          * @param {Roo.bootstrap.Table} this
7611          * @param {Roo.Element} el
7612          * @param {Number} rowIndex
7613          * @param {Roo.EventObject} e
7614          */
7615         "rowdblclick" : true,
7616         /**
7617          * @event mouseover
7618          * Fires when a mouseover occur
7619          * @param {Roo.bootstrap.Table} this
7620          * @param {Roo.Element} el
7621          * @param {Number} rowIndex
7622          * @param {Number} columnIndex
7623          * @param {Roo.EventObject} e
7624          */
7625         "mouseover" : true,
7626         /**
7627          * @event mouseout
7628          * Fires when a mouseout occur
7629          * @param {Roo.bootstrap.Table} this
7630          * @param {Roo.Element} el
7631          * @param {Number} rowIndex
7632          * @param {Number} columnIndex
7633          * @param {Roo.EventObject} e
7634          */
7635         "mouseout" : true,
7636         /**
7637          * @event rowclass
7638          * Fires when a row is rendered, so you can change add a style to it.
7639          * @param {Roo.bootstrap.Table} this
7640          * @param {Object} rowcfg   contains record  rowIndex colIndex and rowClass - set rowClass to add a style.
7641          */
7642         'rowclass' : true,
7643           /**
7644          * @event rowsrendered
7645          * Fires when all the  rows have been rendered
7646          * @param {Roo.bootstrap.Table} this
7647          */
7648         'rowsrendered' : true,
7649         /**
7650          * @event contextmenu
7651          * The raw contextmenu event for the entire grid.
7652          * @param {Roo.EventObject} e
7653          */
7654         "contextmenu" : true,
7655         /**
7656          * @event rowcontextmenu
7657          * Fires when a row is right clicked
7658          * @param {Roo.bootstrap.Table} this
7659          * @param {Number} rowIndex
7660          * @param {Roo.EventObject} e
7661          */
7662         "rowcontextmenu" : true,
7663         /**
7664          * @event cellcontextmenu
7665          * Fires when a cell is right clicked
7666          * @param {Roo.bootstrap.Table} this
7667          * @param {Number} rowIndex
7668          * @param {Number} cellIndex
7669          * @param {Roo.EventObject} e
7670          */
7671          "cellcontextmenu" : true,
7672          /**
7673          * @event headercontextmenu
7674          * Fires when a header is right clicked
7675          * @param {Roo.bootstrap.Table} this
7676          * @param {Number} columnIndex
7677          * @param {Roo.EventObject} e
7678          */
7679         "headercontextmenu" : true
7680     });
7681 };
7682
7683 Roo.extend(Roo.bootstrap.Table, Roo.bootstrap.Component,  {
7684     
7685     cls: false,
7686     align: false,
7687     bgcolor: false,
7688     border: false,
7689     cellpadding: false,
7690     cellspacing: false,
7691     frame: false,
7692     rules: false,
7693     sortable: false,
7694     summary: false,
7695     width: false,
7696     striped : false,
7697     scrollBody : false,
7698     bordered: false,
7699     hover:  false,
7700     condensed : false,
7701     responsive : false,
7702     sm : false,
7703     cm : false,
7704     store : false,
7705     loadMask : false,
7706     footerShow : true,
7707     headerShow : true,
7708   
7709     rowSelection : false,
7710     cellSelection : false,
7711     layout : false,
7712     
7713     // Roo.Element - the tbody
7714     mainBody: false,
7715     // Roo.Element - thead element
7716     mainHead: false,
7717     
7718     container: false, // used by gridpanel...
7719     
7720     lazyLoad : false,
7721     
7722     CSS : Roo.util.CSS,
7723     
7724     auto_hide_footer : false,
7725     
7726     getAutoCreate : function()
7727     {
7728         var cfg = Roo.apply({}, Roo.bootstrap.Table.superclass.getAutoCreate.call(this));
7729         
7730         cfg = {
7731             tag: 'table',
7732             cls : 'table',
7733             cn : []
7734         };
7735         if (this.scrollBody) {
7736             cfg.cls += ' table-body-fixed';
7737         }    
7738         if (this.striped) {
7739             cfg.cls += ' table-striped';
7740         }
7741         
7742         if (this.hover) {
7743             cfg.cls += ' table-hover';
7744         }
7745         if (this.bordered) {
7746             cfg.cls += ' table-bordered';
7747         }
7748         if (this.condensed) {
7749             cfg.cls += ' table-condensed';
7750         }
7751         if (this.responsive) {
7752             cfg.cls += ' table-responsive';
7753         }
7754         
7755         if (this.cls) {
7756             cfg.cls+=  ' ' +this.cls;
7757         }
7758         
7759         // this lot should be simplifed...
7760         var _t = this;
7761         var cp = [
7762             'align',
7763             'bgcolor',
7764             'border',
7765             'cellpadding',
7766             'cellspacing',
7767             'frame',
7768             'rules',
7769             'sortable',
7770             'summary',
7771             'width'
7772         ].forEach(function(k) {
7773             if (_t[k]) {
7774                 cfg[k] = _t[k];
7775             }
7776         });
7777         
7778         
7779         if (this.layout) {
7780             cfg.style = (typeof(cfg.style) == 'undefined') ? ('table-layout:' + this.layout + ';') : (cfg.style + ('table-layout:' + this.layout + ';'));
7781         }
7782         
7783         if(this.store || this.cm){
7784             if(this.headerShow){
7785                 cfg.cn.push(this.renderHeader());
7786             }
7787             
7788             cfg.cn.push(this.renderBody());
7789             
7790             if(this.footerShow){
7791                 cfg.cn.push(this.renderFooter());
7792             }
7793             // where does this come from?
7794             //cfg.cls+=  ' TableGrid';
7795         }
7796         
7797         return { cn : [ cfg ] };
7798     },
7799     
7800     initEvents : function()
7801     {   
7802         if(!this.store || !this.cm){
7803             return;
7804         }
7805         if (this.selModel) {
7806             this.selModel.initEvents();
7807         }
7808         
7809         
7810         //Roo.log('initEvents with ds!!!!');
7811         
7812         this.mainBody = this.el.select('tbody', true).first();
7813         this.mainHead = this.el.select('thead', true).first();
7814         this.mainFoot = this.el.select('tfoot', true).first();
7815         
7816         
7817         
7818         var _this = this;
7819         
7820         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
7821             e.on('click', _this.sort, _this);
7822         });
7823         
7824         this.mainBody.on("click", this.onClick, this);
7825         this.mainBody.on("dblclick", this.onDblClick, this);
7826         
7827         // why is this done????? = it breaks dialogs??
7828         //this.parent().el.setStyle('position', 'relative');
7829         
7830         
7831         if (this.footer) {
7832             this.footer.parentId = this.id;
7833             this.footer.onRender(this.el.select('tfoot tr td').first(), null);
7834             
7835             if(this.lazyLoad){
7836                 this.el.select('tfoot tr td').first().addClass('hide');
7837             }
7838         } 
7839         
7840         if(this.loadMask) {
7841             this.maskEl = new Roo.LoadMask(this.el, { store : this.ds, msgCls: 'roo-el-mask-msg' });
7842         }
7843         
7844         this.store.on('load', this.onLoad, this);
7845         this.store.on('beforeload', this.onBeforeLoad, this);
7846         this.store.on('update', this.onUpdate, this);
7847         this.store.on('add', this.onAdd, this);
7848         this.store.on("clear", this.clear, this);
7849         
7850         this.el.on("contextmenu", this.onContextMenu, this);
7851         
7852         this.mainBody.on('scroll', this.onBodyScroll, this);
7853         
7854         this.cm.on("headerchange", this.onHeaderChange, this);
7855         
7856         this.cm.on("hiddenchange", this.onHiddenChange, this, arguments);
7857         
7858     },
7859     
7860     onContextMenu : function(e, t)
7861     {
7862         this.processEvent("contextmenu", e);
7863     },
7864     
7865     processEvent : function(name, e)
7866     {
7867         if (name != 'touchstart' ) {
7868             this.fireEvent(name, e);    
7869         }
7870         
7871         var t = e.getTarget();
7872         
7873         var cell = Roo.get(t);
7874         
7875         if(!cell){
7876             return;
7877         }
7878         
7879         if(cell.findParent('tfoot', false, true)){
7880             return;
7881         }
7882         
7883         if(cell.findParent('thead', false, true)){
7884             
7885             if(e.getTarget().nodeName.toLowerCase() != 'th'){
7886                 cell = Roo.get(t).findParent('th', false, true);
7887                 if (!cell) {
7888                     Roo.log("failed to find th in thead?");
7889                     Roo.log(e.getTarget());
7890                     return;
7891                 }
7892             }
7893             
7894             var cellIndex = cell.dom.cellIndex;
7895             
7896             var ename = name == 'touchstart' ? 'click' : name;
7897             this.fireEvent("header" + ename, this, cellIndex, e);
7898             
7899             return;
7900         }
7901         
7902         if(e.getTarget().nodeName.toLowerCase() != 'td'){
7903             cell = Roo.get(t).findParent('td', false, true);
7904             if (!cell) {
7905                 Roo.log("failed to find th in tbody?");
7906                 Roo.log(e.getTarget());
7907                 return;
7908             }
7909         }
7910         
7911         var row = cell.findParent('tr', false, true);
7912         var cellIndex = cell.dom.cellIndex;
7913         var rowIndex = row.dom.rowIndex - 1;
7914         
7915         if(row !== false){
7916             
7917             this.fireEvent("row" + name, this, rowIndex, e);
7918             
7919             if(cell !== false){
7920             
7921                 this.fireEvent("cell" + name, this, rowIndex, cellIndex, e);
7922             }
7923         }
7924         
7925     },
7926     
7927     onMouseover : function(e, el)
7928     {
7929         var cell = Roo.get(el);
7930         
7931         if(!cell){
7932             return;
7933         }
7934         
7935         if(e.getTarget().nodeName.toLowerCase() != 'td'){
7936             cell = cell.findParent('td', false, true);
7937         }
7938         
7939         var row = cell.findParent('tr', false, true);
7940         var cellIndex = cell.dom.cellIndex;
7941         var rowIndex = row.dom.rowIndex - 1; // start from 0
7942         
7943         this.fireEvent('mouseover', this, cell, rowIndex, cellIndex, e);
7944         
7945     },
7946     
7947     onMouseout : function(e, el)
7948     {
7949         var cell = Roo.get(el);
7950         
7951         if(!cell){
7952             return;
7953         }
7954         
7955         if(e.getTarget().nodeName.toLowerCase() != 'td'){
7956             cell = cell.findParent('td', false, true);
7957         }
7958         
7959         var row = cell.findParent('tr', false, true);
7960         var cellIndex = cell.dom.cellIndex;
7961         var rowIndex = row.dom.rowIndex - 1; // start from 0
7962         
7963         this.fireEvent('mouseout', this, cell, rowIndex, cellIndex, e);
7964         
7965     },
7966     
7967     onClick : function(e, el)
7968     {
7969         var cell = Roo.get(el);
7970         
7971         if(!cell || (!this.cellSelection && !this.rowSelection)){
7972             return;
7973         }
7974         
7975         if(e.getTarget().nodeName.toLowerCase() != 'td'){
7976             cell = cell.findParent('td', false, true);
7977         }
7978         
7979         if(!cell || typeof(cell) == 'undefined'){
7980             return;
7981         }
7982         
7983         var row = cell.findParent('tr', false, true);
7984         
7985         if(!row || typeof(row) == 'undefined'){
7986             return;
7987         }
7988         
7989         var cellIndex = cell.dom.cellIndex;
7990         var rowIndex = this.getRowIndex(row);
7991         
7992         // why??? - should these not be based on SelectionModel?
7993         if(this.cellSelection){
7994             this.fireEvent('cellclick', this, cell, rowIndex, cellIndex, e);
7995         }
7996         
7997         if(this.rowSelection){
7998             this.fireEvent('rowclick', this, row, rowIndex, e);
7999         }
8000         
8001         
8002     },
8003         
8004     onDblClick : function(e,el)
8005     {
8006         var cell = Roo.get(el);
8007         
8008         if(!cell || (!this.cellSelection && !this.rowSelection)){
8009             return;
8010         }
8011         
8012         if(e.getTarget().nodeName.toLowerCase() != 'td'){
8013             cell = cell.findParent('td', false, true);
8014         }
8015         
8016         if(!cell || typeof(cell) == 'undefined'){
8017             return;
8018         }
8019         
8020         var row = cell.findParent('tr', false, true);
8021         
8022         if(!row || typeof(row) == 'undefined'){
8023             return;
8024         }
8025         
8026         var cellIndex = cell.dom.cellIndex;
8027         var rowIndex = this.getRowIndex(row);
8028         
8029         if(this.cellSelection){
8030             this.fireEvent('celldblclick', this, cell, rowIndex, cellIndex, e);
8031         }
8032         
8033         if(this.rowSelection){
8034             this.fireEvent('rowdblclick', this, row, rowIndex, e);
8035         }
8036     },
8037     
8038     sort : function(e,el)
8039     {
8040         var col = Roo.get(el);
8041         
8042         if(!col.hasClass('sortable')){
8043             return;
8044         }
8045         
8046         var sort = col.attr('sort');
8047         var dir = 'ASC';
8048         
8049         if(col.select('i', true).first().hasClass('glyphicon-arrow-up')){
8050             dir = 'DESC';
8051         }
8052         
8053         this.store.sortInfo = {field : sort, direction : dir};
8054         
8055         if (this.footer) {
8056             Roo.log("calling footer first");
8057             this.footer.onClick('first');
8058         } else {
8059         
8060             this.store.load({ params : { start : 0 } });
8061         }
8062     },
8063     
8064     renderHeader : function()
8065     {
8066         var header = {
8067             tag: 'thead',
8068             cn : []
8069         };
8070         
8071         var cm = this.cm;
8072         this.totalWidth = 0;
8073         
8074         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
8075             
8076             var config = cm.config[i];
8077             
8078             var c = {
8079                 tag: 'th',
8080                 cls : 'x-hcol-' + i,
8081                 style : '',
8082                 html: cm.getColumnHeader(i)
8083             };
8084             
8085             var hh = '';
8086             
8087             if(typeof(config.sortable) != 'undefined' && config.sortable){
8088                 c.cls = 'sortable';
8089                 c.html = '<i class="glyphicon"></i>' + c.html;
8090             }
8091             
8092             // could use BS4 hidden-..-down 
8093             
8094             if(typeof(config.lgHeader) != 'undefined'){
8095                 hh += '<span class="hidden-xs hidden-sm hidden-md ">' + config.lgHeader + '</span>';
8096             }
8097             
8098             if(typeof(config.mdHeader) != 'undefined'){
8099                 hh += '<span class="hidden-xs hidden-sm hidden-lg">' + config.mdHeader + '</span>';
8100             }
8101             
8102             if(typeof(config.smHeader) != 'undefined'){
8103                 hh += '<span class="hidden-xs hidden-md hidden-lg">' + config.smHeader + '</span>';
8104             }
8105             
8106             if(typeof(config.xsHeader) != 'undefined'){
8107                 hh += '<span class="hidden-sm hidden-md hidden-lg">' + config.xsHeader + '</span>';
8108             }
8109             
8110             if(hh.length){
8111                 c.html = hh;
8112             }
8113             
8114             if(typeof(config.tooltip) != 'undefined'){
8115                 c.tooltip = config.tooltip;
8116             }
8117             
8118             if(typeof(config.colspan) != 'undefined'){
8119                 c.colspan = config.colspan;
8120             }
8121             
8122             if(typeof(config.hidden) != 'undefined' && config.hidden){
8123                 c.style += ' display:none;';
8124             }
8125             
8126             if(typeof(config.dataIndex) != 'undefined'){
8127                 c.sort = config.dataIndex;
8128             }
8129             
8130            
8131             
8132             if(typeof(config.align) != 'undefined' && config.align.length){
8133                 c.style += ' text-align:' + config.align + ';';
8134             }
8135             
8136             if(typeof(config.width) != 'undefined'){
8137                 c.style += ' width:' + config.width + 'px;';
8138                 this.totalWidth += config.width;
8139             } else {
8140                 this.totalWidth += 100; // assume minimum of 100 per column?
8141             }
8142             
8143             if(typeof(config.cls) != 'undefined'){
8144                 c.cls = (typeof(c.cls) == 'undefined') ? config.cls : (c.cls + ' ' + config.cls);
8145             }
8146             
8147             ['xs','sm','md','lg'].map(function(size){
8148                 
8149                 if(typeof(config[size]) == 'undefined'){
8150                     return;
8151                 }
8152                  
8153                 if (!config[size]) { // 0 = hidden
8154                     // BS 4 '0' is treated as hide that column and below.
8155                     c.cls += ' hidden-' + size + ' hidden' + size + '-down';
8156                     return;
8157                 }
8158                 
8159                 c.cls += ' col-' + size + '-' + config[size] + (
8160                     size == 'xs' ? (' col-' + config[size] ) : '' // bs4 col-{num} replaces col-xs
8161                 );
8162                 
8163                 
8164             });
8165             
8166             header.cn.push(c)
8167         }
8168         
8169         return header;
8170     },
8171     
8172     renderBody : function()
8173     {
8174         var body = {
8175             tag: 'tbody',
8176             cn : [
8177                 {
8178                     tag: 'tr',
8179                     cn : [
8180                         {
8181                             tag : 'td',
8182                             colspan :  this.cm.getColumnCount()
8183                         }
8184                     ]
8185                 }
8186             ]
8187         };
8188         
8189         return body;
8190     },
8191     
8192     renderFooter : function()
8193     {
8194         var footer = {
8195             tag: 'tfoot',
8196             cn : [
8197                 {
8198                     tag: 'tr',
8199                     cn : [
8200                         {
8201                             tag : 'td',
8202                             colspan :  this.cm.getColumnCount()
8203                         }
8204                     ]
8205                 }
8206             ]
8207         };
8208         
8209         return footer;
8210     },
8211     
8212     
8213     
8214     onLoad : function()
8215     {
8216 //        Roo.log('ds onload');
8217         this.clear();
8218         
8219         var _this = this;
8220         var cm = this.cm;
8221         var ds = this.store;
8222         
8223         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
8224             e.select('i', true).removeClass(['glyphicon-arrow-up', 'glyphicon-arrow-down']);
8225             if (_this.store.sortInfo) {
8226                     
8227                 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'ASC'){
8228                     e.select('i', true).addClass(['glyphicon-arrow-up']);
8229                 }
8230                 
8231                 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'DESC'){
8232                     e.select('i', true).addClass(['glyphicon-arrow-down']);
8233                 }
8234             }
8235         });
8236         
8237         var tbody =  this.mainBody;
8238               
8239         if(ds.getCount() > 0){
8240             ds.data.each(function(d,rowIndex){
8241                 var row =  this.renderRow(cm, ds, rowIndex);
8242                 
8243                 tbody.createChild(row);
8244                 
8245                 var _this = this;
8246                 
8247                 if(row.cellObjects.length){
8248                     Roo.each(row.cellObjects, function(r){
8249                         _this.renderCellObject(r);
8250                     })
8251                 }
8252                 
8253             }, this);
8254         }
8255         
8256         var tfoot = this.el.select('tfoot', true).first();
8257         
8258         if(this.footerShow && this.auto_hide_footer && this.mainFoot){
8259             
8260             this.mainFoot.setVisibilityMode(Roo.Element.DISPLAY).hide();
8261             
8262             var total = this.ds.getTotalCount();
8263             
8264             if(this.footer.pageSize < total){
8265                 this.mainFoot.show();
8266             }
8267         }
8268         
8269         Roo.each(this.el.select('tbody td', true).elements, function(e){
8270             e.on('mouseover', _this.onMouseover, _this);
8271         });
8272         
8273         Roo.each(this.el.select('tbody td', true).elements, function(e){
8274             e.on('mouseout', _this.onMouseout, _this);
8275         });
8276         this.fireEvent('rowsrendered', this);
8277         
8278         this.autoSize();
8279     },
8280     
8281     
8282     onUpdate : function(ds,record)
8283     {
8284         this.refreshRow(record);
8285         this.autoSize();
8286     },
8287     
8288     onRemove : function(ds, record, index, isUpdate){
8289         if(isUpdate !== true){
8290             this.fireEvent("beforerowremoved", this, index, record);
8291         }
8292         var bt = this.mainBody.dom;
8293         
8294         var rows = this.el.select('tbody > tr', true).elements;
8295         
8296         if(typeof(rows[index]) != 'undefined'){
8297             bt.removeChild(rows[index].dom);
8298         }
8299         
8300 //        if(bt.rows[index]){
8301 //            bt.removeChild(bt.rows[index]);
8302 //        }
8303         
8304         if(isUpdate !== true){
8305             //this.stripeRows(index);
8306             //this.syncRowHeights(index, index);
8307             //this.layout();
8308             this.fireEvent("rowremoved", this, index, record);
8309         }
8310     },
8311     
8312     onAdd : function(ds, records, rowIndex)
8313     {
8314         //Roo.log('on Add called');
8315         // - note this does not handle multiple adding very well..
8316         var bt = this.mainBody.dom;
8317         for (var i =0 ; i < records.length;i++) {
8318             //Roo.log('call insert row Add called on ' + rowIndex + ':' + i);
8319             //Roo.log(records[i]);
8320             //Roo.log(this.store.getAt(rowIndex+i));
8321             this.insertRow(this.store, rowIndex + i, false);
8322             return;
8323         }
8324         
8325     },
8326     
8327     
8328     refreshRow : function(record){
8329         var ds = this.store, index;
8330         if(typeof record == 'number'){
8331             index = record;
8332             record = ds.getAt(index);
8333         }else{
8334             index = ds.indexOf(record);
8335             if (index < 0) {
8336                 return; // should not happen - but seems to 
8337             }
8338         }
8339         this.insertRow(ds, index, true);
8340         this.autoSize();
8341         this.onRemove(ds, record, index+1, true);
8342         this.autoSize();
8343         //this.syncRowHeights(index, index);
8344         //this.layout();
8345         this.fireEvent("rowupdated", this, index, record);
8346     },
8347     
8348     insertRow : function(dm, rowIndex, isUpdate){
8349         
8350         if(!isUpdate){
8351             this.fireEvent("beforerowsinserted", this, rowIndex);
8352         }
8353             //var s = this.getScrollState();
8354         var row = this.renderRow(this.cm, this.store, rowIndex);
8355         // insert before rowIndex..
8356         var e = this.mainBody.createChild(row,this.getRowDom(rowIndex));
8357         
8358         var _this = this;
8359                 
8360         if(row.cellObjects.length){
8361             Roo.each(row.cellObjects, function(r){
8362                 _this.renderCellObject(r);
8363             })
8364         }
8365             
8366         if(!isUpdate){
8367             this.fireEvent("rowsinserted", this, rowIndex);
8368             //this.syncRowHeights(firstRow, lastRow);
8369             //this.stripeRows(firstRow);
8370             //this.layout();
8371         }
8372         
8373     },
8374     
8375     
8376     getRowDom : function(rowIndex)
8377     {
8378         var rows = this.el.select('tbody > tr', true).elements;
8379         
8380         return (typeof(rows[rowIndex]) == 'undefined') ? false : rows[rowIndex];
8381         
8382     },
8383     // returns the object tree for a tr..
8384   
8385     
8386     renderRow : function(cm, ds, rowIndex) 
8387     {
8388         var d = ds.getAt(rowIndex);
8389         
8390         var row = {
8391             tag : 'tr',
8392             cls : 'x-row-' + rowIndex,
8393             cn : []
8394         };
8395             
8396         var cellObjects = [];
8397         
8398         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
8399             var config = cm.config[i];
8400             
8401             var renderer = cm.getRenderer(i);
8402             var value = '';
8403             var id = false;
8404             
8405             if(typeof(renderer) !== 'undefined'){
8406                 value = renderer(d.data[cm.getDataIndex(i)], false, d);
8407             }
8408             // if object are returned, then they are expected to be Roo.bootstrap.Component instances
8409             // and are rendered into the cells after the row is rendered - using the id for the element.
8410             
8411             if(typeof(value) === 'object'){
8412                 id = Roo.id();
8413                 cellObjects.push({
8414                     container : id,
8415                     cfg : value 
8416                 })
8417             }
8418             
8419             var rowcfg = {
8420                 record: d,
8421                 rowIndex : rowIndex,
8422                 colIndex : i,
8423                 rowClass : ''
8424             };
8425
8426             this.fireEvent('rowclass', this, rowcfg);
8427             
8428             var td = {
8429                 tag: 'td',
8430                 cls : rowcfg.rowClass + ' x-col-' + i,
8431                 style: '',
8432                 html: (typeof(value) === 'object') ? '' : value
8433             };
8434             
8435             if (id) {
8436                 td.id = id;
8437             }
8438             
8439             if(typeof(config.colspan) != 'undefined'){
8440                 td.colspan = config.colspan;
8441             }
8442             
8443             if(typeof(config.hidden) != 'undefined' && config.hidden){
8444                 td.style += ' display:none;';
8445             }
8446             
8447             if(typeof(config.align) != 'undefined' && config.align.length){
8448                 td.style += ' text-align:' + config.align + ';';
8449             }
8450             if(typeof(config.valign) != 'undefined' && config.valign.length){
8451                 td.style += ' vertical-align:' + config.valign + ';';
8452             }
8453             
8454             if(typeof(config.width) != 'undefined'){
8455                 td.style += ' width:' +  config.width + 'px;';
8456             }
8457             
8458             if(typeof(config.cursor) != 'undefined'){
8459                 td.style += ' cursor:' +  config.cursor + ';';
8460             }
8461             
8462             if(typeof(config.cls) != 'undefined'){
8463                 td.cls = (typeof(td.cls) == 'undefined') ? config.cls : (td.cls + ' ' + config.cls);
8464             }
8465             
8466             ['xs','sm','md','lg'].map(function(size){
8467                 
8468                 if(typeof(config[size]) == 'undefined'){
8469                     return;
8470                 }
8471                 
8472                 
8473                   
8474                 if (!config[size]) { // 0 = hidden
8475                     // BS 4 '0' is treated as hide that column and below.
8476                     td.cls += ' hidden-' + size + ' hidden' + size + '-down';
8477                     return;
8478                 }
8479                 
8480                 td.cls += ' col-' + size + '-' + config[size] + (
8481                     size == 'xs' ? (' col-' +   config[size] ) : '' // bs4 col-{num} replaces col-xs
8482                 );
8483                  
8484
8485             });
8486             
8487             row.cn.push(td);
8488            
8489         }
8490         
8491         row.cellObjects = cellObjects;
8492         
8493         return row;
8494           
8495     },
8496     
8497     
8498     
8499     onBeforeLoad : function()
8500     {
8501         
8502     },
8503      /**
8504      * Remove all rows
8505      */
8506     clear : function()
8507     {
8508         this.el.select('tbody', true).first().dom.innerHTML = '';
8509     },
8510     /**
8511      * Show or hide a row.
8512      * @param {Number} rowIndex to show or hide
8513      * @param {Boolean} state hide
8514      */
8515     setRowVisibility : function(rowIndex, state)
8516     {
8517         var bt = this.mainBody.dom;
8518         
8519         var rows = this.el.select('tbody > tr', true).elements;
8520         
8521         if(typeof(rows[rowIndex]) == 'undefined'){
8522             return;
8523         }
8524         rows[rowIndex].dom.style.display = state ? '' : 'none';
8525     },
8526     
8527     
8528     getSelectionModel : function(){
8529         if(!this.selModel){
8530             this.selModel = new Roo.bootstrap.Table.RowSelectionModel({grid: this});
8531         }
8532         return this.selModel;
8533     },
8534     /*
8535      * Render the Roo.bootstrap object from renderder
8536      */
8537     renderCellObject : function(r)
8538     {
8539         var _this = this;
8540         
8541         r.cfg.parentId = (typeof(r.container) == 'string') ? r.container : r.container.id;
8542         
8543         var t = r.cfg.render(r.container);
8544         
8545         if(r.cfg.cn){
8546             Roo.each(r.cfg.cn, function(c){
8547                 var child = {
8548                     container: t.getChildContainer(),
8549                     cfg: c
8550                 };
8551                 _this.renderCellObject(child);
8552             })
8553         }
8554     },
8555     
8556     getRowIndex : function(row)
8557     {
8558         var rowIndex = -1;
8559         
8560         Roo.each(this.el.select('tbody > tr', true).elements, function(el, index){
8561             if(el != row){
8562                 return;
8563             }
8564             
8565             rowIndex = index;
8566         });
8567         
8568         return rowIndex;
8569     },
8570      /**
8571      * Returns the grid's underlying element = used by panel.Grid
8572      * @return {Element} The element
8573      */
8574     getGridEl : function(){
8575         return this.el;
8576     },
8577      /**
8578      * Forces a resize - used by panel.Grid
8579      * @return {Element} The element
8580      */
8581     autoSize : function()
8582     {
8583         //var ctr = Roo.get(this.container.dom.parentElement);
8584         var ctr = Roo.get(this.el.dom);
8585         
8586         var thd = this.getGridEl().select('thead',true).first();
8587         var tbd = this.getGridEl().select('tbody', true).first();
8588         var tfd = this.getGridEl().select('tfoot', true).first();
8589         
8590         var cw = ctr.getWidth();
8591         
8592         if (tbd) {
8593             
8594             tbd.setWidth(ctr.getWidth());
8595             // if the body has a max height - and then scrolls - we should perhaps set up the height here
8596             // this needs fixing for various usage - currently only hydra job advers I think..
8597             //tdb.setHeight(
8598             //        ctr.getHeight() - ((thd ? thd.getHeight() : 0) + (tfd ? tfd.getHeight() : 0))
8599             //); 
8600             var barsize = (tbd.dom.offsetWidth - tbd.dom.clientWidth);
8601             cw -= barsize;
8602         }
8603         cw = Math.max(cw, this.totalWidth);
8604         this.getGridEl().select('tr',true).setWidth(cw);
8605         // resize 'expandable coloumn?
8606         
8607         return; // we doe not have a view in this design..
8608         
8609     },
8610     onBodyScroll: function()
8611     {
8612         //Roo.log("body scrolled');" + this.mainBody.dom.scrollLeft);
8613         if(this.mainHead){
8614             this.mainHead.setStyle({
8615                 'position' : 'relative',
8616                 'left': (-1* this.mainBody.dom.scrollLeft) + 'px'
8617             });
8618         }
8619         
8620         if(this.lazyLoad){
8621             
8622             var scrollHeight = this.mainBody.dom.scrollHeight;
8623             
8624             var scrollTop = Math.ceil(this.mainBody.getScroll().top);
8625             
8626             var height = this.mainBody.getHeight();
8627             
8628             if(scrollHeight - height == scrollTop) {
8629                 
8630                 var total = this.ds.getTotalCount();
8631                 
8632                 if(this.footer.cursor + this.footer.pageSize < total){
8633                     
8634                     this.footer.ds.load({
8635                         params : {
8636                             start : this.footer.cursor + this.footer.pageSize,
8637                             limit : this.footer.pageSize
8638                         },
8639                         add : true
8640                     });
8641                 }
8642             }
8643             
8644         }
8645     },
8646     
8647     onHeaderChange : function()
8648     {
8649         var header = this.renderHeader();
8650         var table = this.el.select('table', true).first();
8651         
8652         this.mainHead.remove();
8653         this.mainHead = table.createChild(header, this.mainBody, false);
8654     },
8655     
8656     onHiddenChange : function(colModel, colIndex, hidden)
8657     {
8658         var thSelector = '#' + this.id + ' .x-hcol-' + colIndex;
8659         var tdSelector = '#' + this.id + ' .x-col-' + colIndex;
8660         
8661         this.CSS.updateRule(thSelector, "display", "");
8662         this.CSS.updateRule(tdSelector, "display", "");
8663         
8664         if(hidden){
8665             this.CSS.updateRule(thSelector, "display", "none");
8666             this.CSS.updateRule(tdSelector, "display", "none");
8667         }
8668         
8669         this.onHeaderChange();
8670         this.onLoad();
8671     },
8672     
8673     setColumnWidth: function(col_index, width)
8674     {
8675         // width = "md-2 xs-2..."
8676         if(!this.colModel.config[col_index]) {
8677             return;
8678         }
8679         
8680         var w = width.split(" ");
8681         
8682         var rows = this.el.dom.getElementsByClassName("x-col-"+col_index);
8683         
8684         var h_row = this.el.dom.getElementsByClassName("x-hcol-"+col_index);
8685         
8686         
8687         for(var j = 0; j < w.length; j++) {
8688             
8689             if(!w[j]) {
8690                 continue;
8691             }
8692             
8693             var size_cls = w[j].split("-");
8694             
8695             if(!Number.isInteger(size_cls[1] * 1)) {
8696                 continue;
8697             }
8698             
8699             if(!this.colModel.config[col_index][size_cls[0]]) {
8700                 continue;
8701             }
8702             
8703             if(!h_row[0].classList.contains("col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]])) {
8704                 continue;
8705             }
8706             
8707             h_row[0].classList.replace(
8708                 "col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]],
8709                 "col-"+size_cls[0]+"-"+size_cls[1]
8710             );
8711             
8712             for(var i = 0; i < rows.length; i++) {
8713                 
8714                 var size_cls = w[j].split("-");
8715                 
8716                 if(!Number.isInteger(size_cls[1] * 1)) {
8717                     continue;
8718                 }
8719                 
8720                 if(!this.colModel.config[col_index][size_cls[0]]) {
8721                     continue;
8722                 }
8723                 
8724                 if(!rows[i].classList.contains("col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]])) {
8725                     continue;
8726                 }
8727                 
8728                 rows[i].classList.replace(
8729                     "col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]],
8730                     "col-"+size_cls[0]+"-"+size_cls[1]
8731                 );
8732             }
8733             
8734             this.colModel.config[col_index][size_cls[0]] = size_cls[1];
8735         }
8736     }
8737 });
8738
8739  
8740
8741  /*
8742  * - LGPL
8743  *
8744  * table cell
8745  * 
8746  */
8747
8748 /**
8749  * @class Roo.bootstrap.TableCell
8750  * @extends Roo.bootstrap.Component
8751  * Bootstrap TableCell class
8752  * @cfg {String} html cell contain text
8753  * @cfg {String} cls cell class
8754  * @cfg {String} tag cell tag (td|th) default td
8755  * @cfg {String} abbr Specifies an abbreviated version of the content in a cell
8756  * @cfg {String} align Aligns the content in a cell
8757  * @cfg {String} axis Categorizes cells
8758  * @cfg {String} bgcolor Specifies the background color of a cell
8759  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
8760  * @cfg {Number} colspan Specifies the number of columns a cell should span
8761  * @cfg {String} headers Specifies one or more header cells a cell is related to
8762  * @cfg {Number} height Sets the height of a cell
8763  * @cfg {String} nowrap Specifies that the content inside a cell should not wrap
8764  * @cfg {Number} rowspan Sets the number of rows a cell should span
8765  * @cfg {String} scope Defines a way to associate header cells and data cells in a table
8766  * @cfg {String} valign Vertical aligns the content in a cell
8767  * @cfg {Number} width Specifies the width of a cell
8768  * 
8769  * @constructor
8770  * Create a new TableCell
8771  * @param {Object} config The config object
8772  */
8773
8774 Roo.bootstrap.TableCell = function(config){
8775     Roo.bootstrap.TableCell.superclass.constructor.call(this, config);
8776 };
8777
8778 Roo.extend(Roo.bootstrap.TableCell, Roo.bootstrap.Component,  {
8779     
8780     html: false,
8781     cls: false,
8782     tag: false,
8783     abbr: false,
8784     align: false,
8785     axis: false,
8786     bgcolor: false,
8787     charoff: false,
8788     colspan: false,
8789     headers: false,
8790     height: false,
8791     nowrap: false,
8792     rowspan: false,
8793     scope: false,
8794     valign: false,
8795     width: false,
8796     
8797     
8798     getAutoCreate : function(){
8799         var cfg = Roo.apply({}, Roo.bootstrap.TableCell.superclass.getAutoCreate.call(this));
8800         
8801         cfg = {
8802             tag: 'td'
8803         };
8804         
8805         if(this.tag){
8806             cfg.tag = this.tag;
8807         }
8808         
8809         if (this.html) {
8810             cfg.html=this.html
8811         }
8812         if (this.cls) {
8813             cfg.cls=this.cls
8814         }
8815         if (this.abbr) {
8816             cfg.abbr=this.abbr
8817         }
8818         if (this.align) {
8819             cfg.align=this.align
8820         }
8821         if (this.axis) {
8822             cfg.axis=this.axis
8823         }
8824         if (this.bgcolor) {
8825             cfg.bgcolor=this.bgcolor
8826         }
8827         if (this.charoff) {
8828             cfg.charoff=this.charoff
8829         }
8830         if (this.colspan) {
8831             cfg.colspan=this.colspan
8832         }
8833         if (this.headers) {
8834             cfg.headers=this.headers
8835         }
8836         if (this.height) {
8837             cfg.height=this.height
8838         }
8839         if (this.nowrap) {
8840             cfg.nowrap=this.nowrap
8841         }
8842         if (this.rowspan) {
8843             cfg.rowspan=this.rowspan
8844         }
8845         if (this.scope) {
8846             cfg.scope=this.scope
8847         }
8848         if (this.valign) {
8849             cfg.valign=this.valign
8850         }
8851         if (this.width) {
8852             cfg.width=this.width
8853         }
8854         
8855         
8856         return cfg;
8857     }
8858    
8859 });
8860
8861  
8862
8863  /*
8864  * - LGPL
8865  *
8866  * table row
8867  * 
8868  */
8869
8870 /**
8871  * @class Roo.bootstrap.TableRow
8872  * @extends Roo.bootstrap.Component
8873  * Bootstrap TableRow class
8874  * @cfg {String} cls row class
8875  * @cfg {String} align Aligns the content in a table row
8876  * @cfg {String} bgcolor Specifies a background color for a table row
8877  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
8878  * @cfg {String} valign Vertical aligns the content in a table row
8879  * 
8880  * @constructor
8881  * Create a new TableRow
8882  * @param {Object} config The config object
8883  */
8884
8885 Roo.bootstrap.TableRow = function(config){
8886     Roo.bootstrap.TableRow.superclass.constructor.call(this, config);
8887 };
8888
8889 Roo.extend(Roo.bootstrap.TableRow, Roo.bootstrap.Component,  {
8890     
8891     cls: false,
8892     align: false,
8893     bgcolor: false,
8894     charoff: false,
8895     valign: false,
8896     
8897     getAutoCreate : function(){
8898         var cfg = Roo.apply({}, Roo.bootstrap.TableRow.superclass.getAutoCreate.call(this));
8899         
8900         cfg = {
8901             tag: 'tr'
8902         };
8903             
8904         if(this.cls){
8905             cfg.cls = this.cls;
8906         }
8907         if(this.align){
8908             cfg.align = this.align;
8909         }
8910         if(this.bgcolor){
8911             cfg.bgcolor = this.bgcolor;
8912         }
8913         if(this.charoff){
8914             cfg.charoff = this.charoff;
8915         }
8916         if(this.valign){
8917             cfg.valign = this.valign;
8918         }
8919         
8920         return cfg;
8921     }
8922    
8923 });
8924
8925  
8926
8927  /*
8928  * - LGPL
8929  *
8930  * table body
8931  * 
8932  */
8933
8934 /**
8935  * @class Roo.bootstrap.TableBody
8936  * @extends Roo.bootstrap.Component
8937  * Bootstrap TableBody class
8938  * @cfg {String} cls element class
8939  * @cfg {String} tag element tag (thead|tbody|tfoot) default tbody
8940  * @cfg {String} align Aligns the content inside the element
8941  * @cfg {Number} charoff Sets the number of characters the content inside the element will be aligned from the character specified by the char attribute
8942  * @cfg {String} valign Vertical aligns the content inside the <tbody> element
8943  * 
8944  * @constructor
8945  * Create a new TableBody
8946  * @param {Object} config The config object
8947  */
8948
8949 Roo.bootstrap.TableBody = function(config){
8950     Roo.bootstrap.TableBody.superclass.constructor.call(this, config);
8951 };
8952
8953 Roo.extend(Roo.bootstrap.TableBody, Roo.bootstrap.Component,  {
8954     
8955     cls: false,
8956     tag: false,
8957     align: false,
8958     charoff: false,
8959     valign: false,
8960     
8961     getAutoCreate : function(){
8962         var cfg = Roo.apply({}, Roo.bootstrap.TableBody.superclass.getAutoCreate.call(this));
8963         
8964         cfg = {
8965             tag: 'tbody'
8966         };
8967             
8968         if (this.cls) {
8969             cfg.cls=this.cls
8970         }
8971         if(this.tag){
8972             cfg.tag = this.tag;
8973         }
8974         
8975         if(this.align){
8976             cfg.align = this.align;
8977         }
8978         if(this.charoff){
8979             cfg.charoff = this.charoff;
8980         }
8981         if(this.valign){
8982             cfg.valign = this.valign;
8983         }
8984         
8985         return cfg;
8986     }
8987     
8988     
8989 //    initEvents : function()
8990 //    {
8991 //        
8992 //        if(!this.store){
8993 //            return;
8994 //        }
8995 //        
8996 //        this.store = Roo.factory(this.store, Roo.data);
8997 //        this.store.on('load', this.onLoad, this);
8998 //        
8999 //        this.store.load();
9000 //        
9001 //    },
9002 //    
9003 //    onLoad: function () 
9004 //    {   
9005 //        this.fireEvent('load', this);
9006 //    }
9007 //    
9008 //   
9009 });
9010
9011  
9012
9013  /*
9014  * Based on:
9015  * Ext JS Library 1.1.1
9016  * Copyright(c) 2006-2007, Ext JS, LLC.
9017  *
9018  * Originally Released Under LGPL - original licence link has changed is not relivant.
9019  *
9020  * Fork - LGPL
9021  * <script type="text/javascript">
9022  */
9023
9024 // as we use this in bootstrap.
9025 Roo.namespace('Roo.form');
9026  /**
9027  * @class Roo.form.Action
9028  * Internal Class used to handle form actions
9029  * @constructor
9030  * @param {Roo.form.BasicForm} el The form element or its id
9031  * @param {Object} config Configuration options
9032  */
9033
9034  
9035  
9036 // define the action interface
9037 Roo.form.Action = function(form, options){
9038     this.form = form;
9039     this.options = options || {};
9040 };
9041 /**
9042  * Client Validation Failed
9043  * @const 
9044  */
9045 Roo.form.Action.CLIENT_INVALID = 'client';
9046 /**
9047  * Server Validation Failed
9048  * @const 
9049  */
9050 Roo.form.Action.SERVER_INVALID = 'server';
9051  /**
9052  * Connect to Server Failed
9053  * @const 
9054  */
9055 Roo.form.Action.CONNECT_FAILURE = 'connect';
9056 /**
9057  * Reading Data from Server Failed
9058  * @const 
9059  */
9060 Roo.form.Action.LOAD_FAILURE = 'load';
9061
9062 Roo.form.Action.prototype = {
9063     type : 'default',
9064     failureType : undefined,
9065     response : undefined,
9066     result : undefined,
9067
9068     // interface method
9069     run : function(options){
9070
9071     },
9072
9073     // interface method
9074     success : function(response){
9075
9076     },
9077
9078     // interface method
9079     handleResponse : function(response){
9080
9081     },
9082
9083     // default connection failure
9084     failure : function(response){
9085         
9086         this.response = response;
9087         this.failureType = Roo.form.Action.CONNECT_FAILURE;
9088         this.form.afterAction(this, false);
9089     },
9090
9091     processResponse : function(response){
9092         this.response = response;
9093         if(!response.responseText){
9094             return true;
9095         }
9096         this.result = this.handleResponse(response);
9097         return this.result;
9098     },
9099
9100     // utility functions used internally
9101     getUrl : function(appendParams){
9102         var url = this.options.url || this.form.url || this.form.el.dom.action;
9103         if(appendParams){
9104             var p = this.getParams();
9105             if(p){
9106                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
9107             }
9108         }
9109         return url;
9110     },
9111
9112     getMethod : function(){
9113         return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
9114     },
9115
9116     getParams : function(){
9117         var bp = this.form.baseParams;
9118         var p = this.options.params;
9119         if(p){
9120             if(typeof p == "object"){
9121                 p = Roo.urlEncode(Roo.applyIf(p, bp));
9122             }else if(typeof p == 'string' && bp){
9123                 p += '&' + Roo.urlEncode(bp);
9124             }
9125         }else if(bp){
9126             p = Roo.urlEncode(bp);
9127         }
9128         return p;
9129     },
9130
9131     createCallback : function(){
9132         return {
9133             success: this.success,
9134             failure: this.failure,
9135             scope: this,
9136             timeout: (this.form.timeout*1000),
9137             upload: this.form.fileUpload ? this.success : undefined
9138         };
9139     }
9140 };
9141
9142 Roo.form.Action.Submit = function(form, options){
9143     Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
9144 };
9145
9146 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
9147     type : 'submit',
9148
9149     haveProgress : false,
9150     uploadComplete : false,
9151     
9152     // uploadProgress indicator.
9153     uploadProgress : function()
9154     {
9155         if (!this.form.progressUrl) {
9156             return;
9157         }
9158         
9159         if (!this.haveProgress) {
9160             Roo.MessageBox.progress("Uploading", "Uploading");
9161         }
9162         if (this.uploadComplete) {
9163            Roo.MessageBox.hide();
9164            return;
9165         }
9166         
9167         this.haveProgress = true;
9168    
9169         var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
9170         
9171         var c = new Roo.data.Connection();
9172         c.request({
9173             url : this.form.progressUrl,
9174             params: {
9175                 id : uid
9176             },
9177             method: 'GET',
9178             success : function(req){
9179                //console.log(data);
9180                 var rdata = false;
9181                 var edata;
9182                 try  {
9183                    rdata = Roo.decode(req.responseText)
9184                 } catch (e) {
9185                     Roo.log("Invalid data from server..");
9186                     Roo.log(edata);
9187                     return;
9188                 }
9189                 if (!rdata || !rdata.success) {
9190                     Roo.log(rdata);
9191                     Roo.MessageBox.alert(Roo.encode(rdata));
9192                     return;
9193                 }
9194                 var data = rdata.data;
9195                 
9196                 if (this.uploadComplete) {
9197                    Roo.MessageBox.hide();
9198                    return;
9199                 }
9200                    
9201                 if (data){
9202                     Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
9203                        Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
9204                     );
9205                 }
9206                 this.uploadProgress.defer(2000,this);
9207             },
9208        
9209             failure: function(data) {
9210                 Roo.log('progress url failed ');
9211                 Roo.log(data);
9212             },
9213             scope : this
9214         });
9215            
9216     },
9217     
9218     
9219     run : function()
9220     {
9221         // run get Values on the form, so it syncs any secondary forms.
9222         this.form.getValues();
9223         
9224         var o = this.options;
9225         var method = this.getMethod();
9226         var isPost = method == 'POST';
9227         if(o.clientValidation === false || this.form.isValid()){
9228             
9229             if (this.form.progressUrl) {
9230                 this.form.findField('UPLOAD_IDENTIFIER').setValue(
9231                     (new Date() * 1) + '' + Math.random());
9232                     
9233             } 
9234             
9235             
9236             Roo.Ajax.request(Roo.apply(this.createCallback(), {
9237                 form:this.form.el.dom,
9238                 url:this.getUrl(!isPost),
9239                 method: method,
9240                 params:isPost ? this.getParams() : null,
9241                 isUpload: this.form.fileUpload,
9242                 formData : this.form.formData
9243             }));
9244             
9245             this.uploadProgress();
9246
9247         }else if (o.clientValidation !== false){ // client validation failed
9248             this.failureType = Roo.form.Action.CLIENT_INVALID;
9249             this.form.afterAction(this, false);
9250         }
9251     },
9252
9253     success : function(response)
9254     {
9255         this.uploadComplete= true;
9256         if (this.haveProgress) {
9257             Roo.MessageBox.hide();
9258         }
9259         
9260         
9261         var result = this.processResponse(response);
9262         if(result === true || result.success){
9263             this.form.afterAction(this, true);
9264             return;
9265         }
9266         if(result.errors){
9267             this.form.markInvalid(result.errors);
9268             this.failureType = Roo.form.Action.SERVER_INVALID;
9269         }
9270         this.form.afterAction(this, false);
9271     },
9272     failure : function(response)
9273     {
9274         this.uploadComplete= true;
9275         if (this.haveProgress) {
9276             Roo.MessageBox.hide();
9277         }
9278         
9279         this.response = response;
9280         this.failureType = Roo.form.Action.CONNECT_FAILURE;
9281         this.form.afterAction(this, false);
9282     },
9283     
9284     handleResponse : function(response){
9285         if(this.form.errorReader){
9286             var rs = this.form.errorReader.read(response);
9287             var errors = [];
9288             if(rs.records){
9289                 for(var i = 0, len = rs.records.length; i < len; i++) {
9290                     var r = rs.records[i];
9291                     errors[i] = r.data;
9292                 }
9293             }
9294             if(errors.length < 1){
9295                 errors = null;
9296             }
9297             return {
9298                 success : rs.success,
9299                 errors : errors
9300             };
9301         }
9302         var ret = false;
9303         try {
9304             ret = Roo.decode(response.responseText);
9305         } catch (e) {
9306             ret = {
9307                 success: false,
9308                 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
9309                 errors : []
9310             };
9311         }
9312         return ret;
9313         
9314     }
9315 });
9316
9317
9318 Roo.form.Action.Load = function(form, options){
9319     Roo.form.Action.Load.superclass.constructor.call(this, form, options);
9320     this.reader = this.form.reader;
9321 };
9322
9323 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
9324     type : 'load',
9325
9326     run : function(){
9327         
9328         Roo.Ajax.request(Roo.apply(
9329                 this.createCallback(), {
9330                     method:this.getMethod(),
9331                     url:this.getUrl(false),
9332                     params:this.getParams()
9333         }));
9334     },
9335
9336     success : function(response){
9337         
9338         var result = this.processResponse(response);
9339         if(result === true || !result.success || !result.data){
9340             this.failureType = Roo.form.Action.LOAD_FAILURE;
9341             this.form.afterAction(this, false);
9342             return;
9343         }
9344         this.form.clearInvalid();
9345         this.form.setValues(result.data);
9346         this.form.afterAction(this, true);
9347     },
9348
9349     handleResponse : function(response){
9350         if(this.form.reader){
9351             var rs = this.form.reader.read(response);
9352             var data = rs.records && rs.records[0] ? rs.records[0].data : null;
9353             return {
9354                 success : rs.success,
9355                 data : data
9356             };
9357         }
9358         return Roo.decode(response.responseText);
9359     }
9360 });
9361
9362 Roo.form.Action.ACTION_TYPES = {
9363     'load' : Roo.form.Action.Load,
9364     'submit' : Roo.form.Action.Submit
9365 };/*
9366  * - LGPL
9367  *
9368  * form
9369  *
9370  */
9371
9372 /**
9373  * @class Roo.bootstrap.Form
9374  * @extends Roo.bootstrap.Component
9375  * Bootstrap Form class
9376  * @cfg {String} method  GET | POST (default POST)
9377  * @cfg {String} labelAlign top | left (default top)
9378  * @cfg {String} align left  | right - for navbars
9379  * @cfg {Boolean} loadMask load mask when submit (default true)
9380
9381  *
9382  * @constructor
9383  * Create a new Form
9384  * @param {Object} config The config object
9385  */
9386
9387
9388 Roo.bootstrap.Form = function(config){
9389     
9390     Roo.bootstrap.Form.superclass.constructor.call(this, config);
9391     
9392     Roo.bootstrap.Form.popover.apply();
9393     
9394     this.addEvents({
9395         /**
9396          * @event clientvalidation
9397          * If the monitorValid config option is true, this event fires repetitively to notify of valid state
9398          * @param {Form} this
9399          * @param {Boolean} valid true if the form has passed client-side validation
9400          */
9401         clientvalidation: true,
9402         /**
9403          * @event beforeaction
9404          * Fires before any action is performed. Return false to cancel the action.
9405          * @param {Form} this
9406          * @param {Action} action The action to be performed
9407          */
9408         beforeaction: true,
9409         /**
9410          * @event actionfailed
9411          * Fires when an action fails.
9412          * @param {Form} this
9413          * @param {Action} action The action that failed
9414          */
9415         actionfailed : true,
9416         /**
9417          * @event actioncomplete
9418          * Fires when an action is completed.
9419          * @param {Form} this
9420          * @param {Action} action The action that completed
9421          */
9422         actioncomplete : true
9423     });
9424 };
9425
9426 Roo.extend(Roo.bootstrap.Form, Roo.bootstrap.Component,  {
9427
9428      /**
9429      * @cfg {String} method
9430      * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
9431      */
9432     method : 'POST',
9433     /**
9434      * @cfg {String} url
9435      * The URL to use for form actions if one isn't supplied in the action options.
9436      */
9437     /**
9438      * @cfg {Boolean} fileUpload
9439      * Set to true if this form is a file upload.
9440      */
9441
9442     /**
9443      * @cfg {Object} baseParams
9444      * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
9445      */
9446
9447     /**
9448      * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
9449      */
9450     timeout: 30,
9451     /**
9452      * @cfg {Sting} align (left|right) for navbar forms
9453      */
9454     align : 'left',
9455
9456     // private
9457     activeAction : null,
9458
9459     /**
9460      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
9461      * element by passing it or its id or mask the form itself by passing in true.
9462      * @type Mixed
9463      */
9464     waitMsgTarget : false,
9465
9466     loadMask : true,
9467     
9468     /**
9469      * @cfg {Boolean} errorMask (true|false) default false
9470      */
9471     errorMask : false,
9472     
9473     /**
9474      * @cfg {Number} maskOffset Default 100
9475      */
9476     maskOffset : 100,
9477     
9478     /**
9479      * @cfg {Boolean} maskBody
9480      */
9481     maskBody : false,
9482
9483     getAutoCreate : function(){
9484
9485         var cfg = {
9486             tag: 'form',
9487             method : this.method || 'POST',
9488             id : this.id || Roo.id(),
9489             cls : ''
9490         };
9491         if (this.parent().xtype.match(/^Nav/)) {
9492             cfg.cls = 'navbar-form form-inline navbar-' + this.align;
9493
9494         }
9495
9496         if (this.labelAlign == 'left' ) {
9497             cfg.cls += ' form-horizontal';
9498         }
9499
9500
9501         return cfg;
9502     },
9503     initEvents : function()
9504     {
9505         this.el.on('submit', this.onSubmit, this);
9506         // this was added as random key presses on the form where triggering form submit.
9507         this.el.on('keypress', function(e) {
9508             if (e.getCharCode() != 13) {
9509                 return true;
9510             }
9511             // we might need to allow it for textareas.. and some other items.
9512             // check e.getTarget().
9513
9514             if(e.getTarget().nodeName.toLowerCase() === 'textarea'){
9515                 return true;
9516             }
9517
9518             Roo.log("keypress blocked");
9519
9520             e.preventDefault();
9521             return false;
9522         });
9523         
9524     },
9525     // private
9526     onSubmit : function(e){
9527         e.stopEvent();
9528     },
9529
9530      /**
9531      * Returns true if client-side validation on the form is successful.
9532      * @return Boolean
9533      */
9534     isValid : function(){
9535         var items = this.getItems();
9536         var valid = true;
9537         var target = false;
9538         
9539         items.each(function(f){
9540             
9541             if(f.validate()){
9542                 return;
9543             }
9544             
9545             Roo.log('invalid field: ' + f.name);
9546             
9547             valid = false;
9548
9549             if(!target && f.el.isVisible(true)){
9550                 target = f;
9551             }
9552            
9553         });
9554         
9555         if(this.errorMask && !valid){
9556             Roo.bootstrap.Form.popover.mask(this, target);
9557         }
9558         
9559         return valid;
9560     },
9561     
9562     /**
9563      * Returns true if any fields in this form have changed since their original load.
9564      * @return Boolean
9565      */
9566     isDirty : function(){
9567         var dirty = false;
9568         var items = this.getItems();
9569         items.each(function(f){
9570            if(f.isDirty()){
9571                dirty = true;
9572                return false;
9573            }
9574            return true;
9575         });
9576         return dirty;
9577     },
9578      /**
9579      * Performs a predefined action (submit or load) or custom actions you define on this form.
9580      * @param {String} actionName The name of the action type
9581      * @param {Object} options (optional) The options to pass to the action.  All of the config options listed
9582      * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
9583      * accept other config options):
9584      * <pre>
9585 Property          Type             Description
9586 ----------------  ---------------  ----------------------------------------------------------------------------------
9587 url               String           The url for the action (defaults to the form's url)
9588 method            String           The form method to use (defaults to the form's method, or POST if not defined)
9589 params            String/Object    The params to pass (defaults to the form's baseParams, or none if not defined)
9590 clientValidation  Boolean          Applies to submit only.  Pass true to call form.isValid() prior to posting to
9591                                    validate the form on the client (defaults to false)
9592      * </pre>
9593      * @return {BasicForm} this
9594      */
9595     doAction : function(action, options){
9596         if(typeof action == 'string'){
9597             action = new Roo.form.Action.ACTION_TYPES[action](this, options);
9598         }
9599         if(this.fireEvent('beforeaction', this, action) !== false){
9600             this.beforeAction(action);
9601             action.run.defer(100, action);
9602         }
9603         return this;
9604     },
9605
9606     // private
9607     beforeAction : function(action){
9608         var o = action.options;
9609         
9610         if(this.loadMask){
9611             
9612             if(this.maskBody){
9613                 Roo.get(document.body).mask(o.waitMsg || "Sending", 'x-mask-loading')
9614             } else {
9615                 this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
9616             }
9617         }
9618         // not really supported yet.. ??
9619
9620         //if(this.waitMsgTarget === true){
9621         //  this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
9622         //}else if(this.waitMsgTarget){
9623         //    this.waitMsgTarget = Roo.get(this.waitMsgTarget);
9624         //    this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
9625         //}else {
9626         //    Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
9627        // }
9628
9629     },
9630
9631     // private
9632     afterAction : function(action, success){
9633         this.activeAction = null;
9634         var o = action.options;
9635
9636         if(this.loadMask){
9637             
9638             if(this.maskBody){
9639                 Roo.get(document.body).unmask();
9640             } else {
9641                 this.el.unmask();
9642             }
9643         }
9644         
9645         //if(this.waitMsgTarget === true){
9646 //            this.el.unmask();
9647         //}else if(this.waitMsgTarget){
9648         //    this.waitMsgTarget.unmask();
9649         //}else{
9650         //    Roo.MessageBox.updateProgress(1);
9651         //    Roo.MessageBox.hide();
9652        // }
9653         //
9654         if(success){
9655             if(o.reset){
9656                 this.reset();
9657             }
9658             Roo.callback(o.success, o.scope, [this, action]);
9659             this.fireEvent('actioncomplete', this, action);
9660
9661         }else{
9662
9663             // failure condition..
9664             // we have a scenario where updates need confirming.
9665             // eg. if a locking scenario exists..
9666             // we look for { errors : { needs_confirm : true }} in the response.
9667             if (
9668                 (typeof(action.result) != 'undefined')  &&
9669                 (typeof(action.result.errors) != 'undefined')  &&
9670                 (typeof(action.result.errors.needs_confirm) != 'undefined')
9671            ){
9672                 var _t = this;
9673                 Roo.log("not supported yet");
9674                  /*
9675
9676                 Roo.MessageBox.confirm(
9677                     "Change requires confirmation",
9678                     action.result.errorMsg,
9679                     function(r) {
9680                         if (r != 'yes') {
9681                             return;
9682                         }
9683                         _t.doAction('submit', { params :  { _submit_confirmed : 1 } }  );
9684                     }
9685
9686                 );
9687                 */
9688
9689
9690                 return;
9691             }
9692
9693             Roo.callback(o.failure, o.scope, [this, action]);
9694             // show an error message if no failed handler is set..
9695             if (!this.hasListener('actionfailed')) {
9696                 Roo.log("need to add dialog support");
9697                 /*
9698                 Roo.MessageBox.alert("Error",
9699                     (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
9700                         action.result.errorMsg :
9701                         "Saving Failed, please check your entries or try again"
9702                 );
9703                 */
9704             }
9705
9706             this.fireEvent('actionfailed', this, action);
9707         }
9708
9709     },
9710     /**
9711      * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
9712      * @param {String} id The value to search for
9713      * @return Field
9714      */
9715     findField : function(id){
9716         var items = this.getItems();
9717         var field = items.get(id);
9718         if(!field){
9719              items.each(function(f){
9720                 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
9721                     field = f;
9722                     return false;
9723                 }
9724                 return true;
9725             });
9726         }
9727         return field || null;
9728     },
9729      /**
9730      * Mark fields in this form invalid in bulk.
9731      * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
9732      * @return {BasicForm} this
9733      */
9734     markInvalid : function(errors){
9735         if(errors instanceof Array){
9736             for(var i = 0, len = errors.length; i < len; i++){
9737                 var fieldError = errors[i];
9738                 var f = this.findField(fieldError.id);
9739                 if(f){
9740                     f.markInvalid(fieldError.msg);
9741                 }
9742             }
9743         }else{
9744             var field, id;
9745             for(id in errors){
9746                 if(typeof errors[id] != 'function' && (field = this.findField(id))){
9747                     field.markInvalid(errors[id]);
9748                 }
9749             }
9750         }
9751         //Roo.each(this.childForms || [], function (f) {
9752         //    f.markInvalid(errors);
9753         //});
9754
9755         return this;
9756     },
9757
9758     /**
9759      * Set values for fields in this form in bulk.
9760      * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
9761      * @return {BasicForm} this
9762      */
9763     setValues : function(values){
9764         if(values instanceof Array){ // array of objects
9765             for(var i = 0, len = values.length; i < len; i++){
9766                 var v = values[i];
9767                 var f = this.findField(v.id);
9768                 if(f){
9769                     f.setValue(v.value);
9770                     if(this.trackResetOnLoad){
9771                         f.originalValue = f.getValue();
9772                     }
9773                 }
9774             }
9775         }else{ // object hash
9776             var field, id;
9777             for(id in values){
9778                 if(typeof values[id] != 'function' && (field = this.findField(id))){
9779
9780                     if (field.setFromData &&
9781                         field.valueField &&
9782                         field.displayField &&
9783                         // combos' with local stores can
9784                         // be queried via setValue()
9785                         // to set their value..
9786                         (field.store && !field.store.isLocal)
9787                         ) {
9788                         // it's a combo
9789                         var sd = { };
9790                         sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
9791                         sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
9792                         field.setFromData(sd);
9793
9794                     } else if(field.setFromData && (field.store && !field.store.isLocal)) {
9795                         
9796                         field.setFromData(values);
9797                         
9798                     } else {
9799                         field.setValue(values[id]);
9800                     }
9801
9802
9803                     if(this.trackResetOnLoad){
9804                         field.originalValue = field.getValue();
9805                     }
9806                 }
9807             }
9808         }
9809
9810         //Roo.each(this.childForms || [], function (f) {
9811         //    f.setValues(values);
9812         //});
9813
9814         return this;
9815     },
9816
9817     /**
9818      * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
9819      * they are returned as an array.
9820      * @param {Boolean} asString
9821      * @return {Object}
9822      */
9823     getValues : function(asString){
9824         //if (this.childForms) {
9825             // copy values from the child forms
9826         //    Roo.each(this.childForms, function (f) {
9827         //        this.setValues(f.getValues());
9828         //    }, this);
9829         //}
9830
9831
9832
9833         var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
9834         if(asString === true){
9835             return fs;
9836         }
9837         return Roo.urlDecode(fs);
9838     },
9839
9840     /**
9841      * Returns the fields in this form as an object with key/value pairs.
9842      * This differs from getValues as it calls getValue on each child item, rather than using dom data.
9843      * @return {Object}
9844      */
9845     getFieldValues : function(with_hidden)
9846     {
9847         var items = this.getItems();
9848         var ret = {};
9849         items.each(function(f){
9850             
9851             if (!f.getName()) {
9852                 return;
9853             }
9854             
9855             var v = f.getValue();
9856             
9857             if (f.inputType =='radio') {
9858                 if (typeof(ret[f.getName()]) == 'undefined') {
9859                     ret[f.getName()] = ''; // empty..
9860                 }
9861
9862                 if (!f.el.dom.checked) {
9863                     return;
9864
9865                 }
9866                 v = f.el.dom.value;
9867
9868             }
9869             
9870             if(f.xtype == 'MoneyField'){
9871                 ret[f.currencyName] = f.getCurrency();
9872             }
9873
9874             // not sure if this supported any more..
9875             if ((typeof(v) == 'object') && f.getRawValue) {
9876                 v = f.getRawValue() ; // dates..
9877             }
9878             // combo boxes where name != hiddenName...
9879             if (f.name !== false && f.name != '' && f.name != f.getName()) {
9880                 ret[f.name] = f.getRawValue();
9881             }
9882             ret[f.getName()] = v;
9883         });
9884
9885         return ret;
9886     },
9887
9888     /**
9889      * Clears all invalid messages in this form.
9890      * @return {BasicForm} this
9891      */
9892     clearInvalid : function(){
9893         var items = this.getItems();
9894
9895         items.each(function(f){
9896            f.clearInvalid();
9897         });
9898
9899         return this;
9900     },
9901
9902     /**
9903      * Resets this form.
9904      * @return {BasicForm} this
9905      */
9906     reset : function(){
9907         var items = this.getItems();
9908         items.each(function(f){
9909             f.reset();
9910         });
9911
9912         Roo.each(this.childForms || [], function (f) {
9913             f.reset();
9914         });
9915
9916
9917         return this;
9918     },
9919     
9920     getItems : function()
9921     {
9922         var r=new Roo.util.MixedCollection(false, function(o){
9923             return o.id || (o.id = Roo.id());
9924         });
9925         var iter = function(el) {
9926             if (el.inputEl) {
9927                 r.add(el);
9928             }
9929             if (!el.items) {
9930                 return;
9931             }
9932             Roo.each(el.items,function(e) {
9933                 iter(e);
9934             });
9935         };
9936
9937         iter(this);
9938         return r;
9939     },
9940     
9941     hideFields : function(items)
9942     {
9943         Roo.each(items, function(i){
9944             
9945             var f = this.findField(i);
9946             
9947             if(!f){
9948                 return;
9949             }
9950             
9951             f.hide();
9952             
9953         }, this);
9954     },
9955     
9956     showFields : function(items)
9957     {
9958         Roo.each(items, function(i){
9959             
9960             var f = this.findField(i);
9961             
9962             if(!f){
9963                 return;
9964             }
9965             
9966             f.show();
9967             
9968         }, this);
9969     }
9970
9971 });
9972
9973 Roo.apply(Roo.bootstrap.Form, {
9974     
9975     popover : {
9976         
9977         padding : 5,
9978         
9979         isApplied : false,
9980         
9981         isMasked : false,
9982         
9983         form : false,
9984         
9985         target : false,
9986         
9987         toolTip : false,
9988         
9989         intervalID : false,
9990         
9991         maskEl : false,
9992         
9993         apply : function()
9994         {
9995             if(this.isApplied){
9996                 return;
9997             }
9998             
9999             this.maskEl = {
10000                 top : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-top-mask" }, true),
10001                 left : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-left-mask" }, true),
10002                 bottom : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-bottom-mask" }, true),
10003                 right : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-right-mask" }, true)
10004             };
10005             
10006             this.maskEl.top.enableDisplayMode("block");
10007             this.maskEl.left.enableDisplayMode("block");
10008             this.maskEl.bottom.enableDisplayMode("block");
10009             this.maskEl.right.enableDisplayMode("block");
10010             
10011             this.toolTip = new Roo.bootstrap.Tooltip({
10012                 cls : 'roo-form-error-popover',
10013                 alignment : {
10014                     'left' : ['r-l', [-2,0], 'right'],
10015                     'right' : ['l-r', [2,0], 'left'],
10016                     'bottom' : ['tl-bl', [0,2], 'top'],
10017                     'top' : [ 'bl-tl', [0,-2], 'bottom']
10018                 }
10019             });
10020             
10021             this.toolTip.render(Roo.get(document.body));
10022
10023             this.toolTip.el.enableDisplayMode("block");
10024             
10025             Roo.get(document.body).on('click', function(){
10026                 this.unmask();
10027             }, this);
10028             
10029             Roo.get(document.body).on('touchstart', function(){
10030                 this.unmask();
10031             }, this);
10032             
10033             this.isApplied = true
10034         },
10035         
10036         mask : function(form, target)
10037         {
10038             this.form = form;
10039             
10040             this.target = target;
10041             
10042             if(!this.form.errorMask || !target.el){
10043                 return;
10044             }
10045             
10046             var scrollable = this.target.el.findScrollableParent() || this.target.el.findParent('div.modal', 100, true) || Roo.get(document.body);
10047             
10048             Roo.log(scrollable);
10049             
10050             var ot = this.target.el.calcOffsetsTo(scrollable);
10051             
10052             var scrollTo = ot[1] - this.form.maskOffset;
10053             
10054             scrollTo = Math.min(scrollTo, scrollable.dom.scrollHeight);
10055             
10056             scrollable.scrollTo('top', scrollTo);
10057             
10058             var box = this.target.el.getBox();
10059             Roo.log(box);
10060             var zIndex = Roo.bootstrap.Modal.zIndex++;
10061
10062             
10063             this.maskEl.top.setStyle('position', 'absolute');
10064             this.maskEl.top.setStyle('z-index', zIndex);
10065             this.maskEl.top.setSize(Roo.lib.Dom.getDocumentWidth(), box.y - this.padding);
10066             this.maskEl.top.setLeft(0);
10067             this.maskEl.top.setTop(0);
10068             this.maskEl.top.show();
10069             
10070             this.maskEl.left.setStyle('position', 'absolute');
10071             this.maskEl.left.setStyle('z-index', zIndex);
10072             this.maskEl.left.setSize(box.x - this.padding, box.height + this.padding * 2);
10073             this.maskEl.left.setLeft(0);
10074             this.maskEl.left.setTop(box.y - this.padding);
10075             this.maskEl.left.show();
10076
10077             this.maskEl.bottom.setStyle('position', 'absolute');
10078             this.maskEl.bottom.setStyle('z-index', zIndex);
10079             this.maskEl.bottom.setSize(Roo.lib.Dom.getDocumentWidth(), Roo.lib.Dom.getDocumentHeight() - box.bottom - this.padding);
10080             this.maskEl.bottom.setLeft(0);
10081             this.maskEl.bottom.setTop(box.bottom + this.padding);
10082             this.maskEl.bottom.show();
10083
10084             this.maskEl.right.setStyle('position', 'absolute');
10085             this.maskEl.right.setStyle('z-index', zIndex);
10086             this.maskEl.right.setSize(Roo.lib.Dom.getDocumentWidth() - box.right - this.padding, box.height + this.padding * 2);
10087             this.maskEl.right.setLeft(box.right + this.padding);
10088             this.maskEl.right.setTop(box.y - this.padding);
10089             this.maskEl.right.show();
10090
10091             this.toolTip.bindEl = this.target.el;
10092
10093             this.toolTip.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
10094
10095             var tip = this.target.blankText;
10096
10097             if(this.target.getValue() !== '' ) {
10098                 
10099                 if (this.target.invalidText.length) {
10100                     tip = this.target.invalidText;
10101                 } else if (this.target.regexText.length){
10102                     tip = this.target.regexText;
10103                 }
10104             }
10105
10106             this.toolTip.show(tip);
10107
10108             this.intervalID = window.setInterval(function() {
10109                 Roo.bootstrap.Form.popover.unmask();
10110             }, 10000);
10111
10112             window.onwheel = function(){ return false;};
10113             
10114             (function(){ this.isMasked = true; }).defer(500, this);
10115             
10116         },
10117         
10118         unmask : function()
10119         {
10120             if(!this.isApplied || !this.isMasked || !this.form || !this.target || !this.form.errorMask){
10121                 return;
10122             }
10123             
10124             this.maskEl.top.setStyle('position', 'absolute');
10125             this.maskEl.top.setSize(0, 0).setXY([0, 0]);
10126             this.maskEl.top.hide();
10127
10128             this.maskEl.left.setStyle('position', 'absolute');
10129             this.maskEl.left.setSize(0, 0).setXY([0, 0]);
10130             this.maskEl.left.hide();
10131
10132             this.maskEl.bottom.setStyle('position', 'absolute');
10133             this.maskEl.bottom.setSize(0, 0).setXY([0, 0]);
10134             this.maskEl.bottom.hide();
10135
10136             this.maskEl.right.setStyle('position', 'absolute');
10137             this.maskEl.right.setSize(0, 0).setXY([0, 0]);
10138             this.maskEl.right.hide();
10139             
10140             this.toolTip.hide();
10141             
10142             this.toolTip.el.hide();
10143             
10144             window.onwheel = function(){ return true;};
10145             
10146             if(this.intervalID){
10147                 window.clearInterval(this.intervalID);
10148                 this.intervalID = false;
10149             }
10150             
10151             this.isMasked = false;
10152             
10153         }
10154         
10155     }
10156     
10157 });
10158
10159 /*
10160  * Based on:
10161  * Ext JS Library 1.1.1
10162  * Copyright(c) 2006-2007, Ext JS, LLC.
10163  *
10164  * Originally Released Under LGPL - original licence link has changed is not relivant.
10165  *
10166  * Fork - LGPL
10167  * <script type="text/javascript">
10168  */
10169 /**
10170  * @class Roo.form.VTypes
10171  * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
10172  * @singleton
10173  */
10174 Roo.form.VTypes = function(){
10175     // closure these in so they are only created once.
10176     var alpha = /^[a-zA-Z_]+$/;
10177     var alphanum = /^[a-zA-Z0-9_]+$/;
10178     var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,24}$/;
10179     var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
10180
10181     // All these messages and functions are configurable
10182     return {
10183         /**
10184          * The function used to validate email addresses
10185          * @param {String} value The email address
10186          */
10187         'email' : function(v){
10188             return email.test(v);
10189         },
10190         /**
10191          * The error text to display when the email validation function returns false
10192          * @type String
10193          */
10194         'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
10195         /**
10196          * The keystroke filter mask to be applied on email input
10197          * @type RegExp
10198          */
10199         'emailMask' : /[a-z0-9_\.\-@]/i,
10200
10201         /**
10202          * The function used to validate URLs
10203          * @param {String} value The URL
10204          */
10205         'url' : function(v){
10206             return url.test(v);
10207         },
10208         /**
10209          * The error text to display when the url validation function returns false
10210          * @type String
10211          */
10212         'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
10213         
10214         /**
10215          * The function used to validate alpha values
10216          * @param {String} value The value
10217          */
10218         'alpha' : function(v){
10219             return alpha.test(v);
10220         },
10221         /**
10222          * The error text to display when the alpha validation function returns false
10223          * @type String
10224          */
10225         'alphaText' : 'This field should only contain letters and _',
10226         /**
10227          * The keystroke filter mask to be applied on alpha input
10228          * @type RegExp
10229          */
10230         'alphaMask' : /[a-z_]/i,
10231
10232         /**
10233          * The function used to validate alphanumeric values
10234          * @param {String} value The value
10235          */
10236         'alphanum' : function(v){
10237             return alphanum.test(v);
10238         },
10239         /**
10240          * The error text to display when the alphanumeric validation function returns false
10241          * @type String
10242          */
10243         'alphanumText' : 'This field should only contain letters, numbers and _',
10244         /**
10245          * The keystroke filter mask to be applied on alphanumeric input
10246          * @type RegExp
10247          */
10248         'alphanumMask' : /[a-z0-9_]/i
10249     };
10250 }();/*
10251  * - LGPL
10252  *
10253  * Input
10254  * 
10255  */
10256
10257 /**
10258  * @class Roo.bootstrap.Input
10259  * @extends Roo.bootstrap.Component
10260  * Bootstrap Input class
10261  * @cfg {Boolean} disabled is it disabled
10262  * @cfg {String} (button|checkbox|email|file|hidden|image|number|password|radio|range|reset|search|submit|text) inputType 
10263  * @cfg {String} name name of the input
10264  * @cfg {string} fieldLabel - the label associated
10265  * @cfg {string} placeholder - placeholder to put in text.
10266  * @cfg {string}  before - input group add on before
10267  * @cfg {string} after - input group add on after
10268  * @cfg {string} size - (lg|sm) or leave empty..
10269  * @cfg {Number} xs colspan out of 12 for mobile-sized screens
10270  * @cfg {Number} sm colspan out of 12 for tablet-sized screens
10271  * @cfg {Number} md colspan out of 12 for computer-sized screens
10272  * @cfg {Number} lg colspan out of 12 for large computer-sized screens
10273  * @cfg {string} value default value of the input
10274  * @cfg {Number} labelWidth set the width of label 
10275  * @cfg {Number} labellg set the width of label (1-12)
10276  * @cfg {Number} labelmd set the width of label (1-12)
10277  * @cfg {Number} labelsm set the width of label (1-12)
10278  * @cfg {Number} labelxs set the width of label (1-12)
10279  * @cfg {String} labelAlign (top|left)
10280  * @cfg {Boolean} readOnly Specifies that the field should be read-only
10281  * @cfg {String} autocomplete - default is new-password see: https://developers.google.com/web/fundamentals/input/form/label-and-name-inputs?hl=en
10282  * @cfg {String} indicatorpos (left|right) default left
10283  * @cfg {String} capture (user|camera) use for file input only. (default empty)
10284  * @cfg {String} accept (image|video|audio) use for file input only. (default empty)
10285  * @cfg {Boolean} preventMark Do not show tick or cross if error/success
10286
10287  * @cfg {String} align (left|center|right) Default left
10288  * @cfg {Boolean} forceFeedback (true|false) Default false
10289  * 
10290  * @constructor
10291  * Create a new Input
10292  * @param {Object} config The config object
10293  */
10294
10295 Roo.bootstrap.Input = function(config){
10296     
10297     Roo.bootstrap.Input.superclass.constructor.call(this, config);
10298     
10299     this.addEvents({
10300         /**
10301          * @event focus
10302          * Fires when this field receives input focus.
10303          * @param {Roo.form.Field} this
10304          */
10305         focus : true,
10306         /**
10307          * @event blur
10308          * Fires when this field loses input focus.
10309          * @param {Roo.form.Field} this
10310          */
10311         blur : true,
10312         /**
10313          * @event specialkey
10314          * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
10315          * {@link Roo.EventObject#getKey} to determine which key was pressed.
10316          * @param {Roo.form.Field} this
10317          * @param {Roo.EventObject} e The event object
10318          */
10319         specialkey : true,
10320         /**
10321          * @event change
10322          * Fires just before the field blurs if the field value has changed.
10323          * @param {Roo.form.Field} this
10324          * @param {Mixed} newValue The new value
10325          * @param {Mixed} oldValue The original value
10326          */
10327         change : true,
10328         /**
10329          * @event invalid
10330          * Fires after the field has been marked as invalid.
10331          * @param {Roo.form.Field} this
10332          * @param {String} msg The validation message
10333          */
10334         invalid : true,
10335         /**
10336          * @event valid
10337          * Fires after the field has been validated with no errors.
10338          * @param {Roo.form.Field} this
10339          */
10340         valid : true,
10341          /**
10342          * @event keyup
10343          * Fires after the key up
10344          * @param {Roo.form.Field} this
10345          * @param {Roo.EventObject}  e The event Object
10346          */
10347         keyup : true
10348     });
10349 };
10350
10351 Roo.extend(Roo.bootstrap.Input, Roo.bootstrap.Component,  {
10352      /**
10353      * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
10354       automatic validation (defaults to "keyup").
10355      */
10356     validationEvent : "keyup",
10357      /**
10358      * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
10359      */
10360     validateOnBlur : true,
10361     /**
10362      * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
10363      */
10364     validationDelay : 250,
10365      /**
10366      * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
10367      */
10368     focusClass : "x-form-focus",  // not needed???
10369     
10370        
10371     /**
10372      * @cfg {String} invalidClass DEPRICATED - code uses BS4 - is-valid / is-invalid
10373      */
10374     invalidClass : "has-warning",
10375     
10376     /**
10377      * @cfg {String} validClass DEPRICATED - code uses BS4 - is-valid / is-invalid
10378      */
10379     validClass : "has-success",
10380     
10381     /**
10382      * @cfg {Boolean} hasFeedback (true|false) default true
10383      */
10384     hasFeedback : true,
10385     
10386     /**
10387      * @cfg {String} invalidFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
10388      */
10389     invalidFeedbackClass : "glyphicon-warning-sign",
10390     
10391     /**
10392      * @cfg {String} validFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
10393      */
10394     validFeedbackClass : "glyphicon-ok",
10395     
10396     /**
10397      * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
10398      */
10399     selectOnFocus : false,
10400     
10401      /**
10402      * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
10403      */
10404     maskRe : null,
10405        /**
10406      * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
10407      */
10408     vtype : null,
10409     
10410       /**
10411      * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
10412      */
10413     disableKeyFilter : false,
10414     
10415        /**
10416      * @cfg {Boolean} disabled True to disable the field (defaults to false).
10417      */
10418     disabled : false,
10419      /**
10420      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
10421      */
10422     allowBlank : true,
10423     /**
10424      * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
10425      */
10426     blankText : "Please complete this mandatory field",
10427     
10428      /**
10429      * @cfg {Number} minLength Minimum input field length required (defaults to 0)
10430      */
10431     minLength : 0,
10432     /**
10433      * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
10434      */
10435     maxLength : Number.MAX_VALUE,
10436     /**
10437      * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
10438      */
10439     minLengthText : "The minimum length for this field is {0}",
10440     /**
10441      * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
10442      */
10443     maxLengthText : "The maximum length for this field is {0}",
10444   
10445     
10446     /**
10447      * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
10448      * If available, this function will be called only after the basic validators all return true, and will be passed the
10449      * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
10450      */
10451     validator : null,
10452     /**
10453      * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
10454      * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
10455      * current field value.  If the test fails, the field will be marked invalid using {@link #regexText}.
10456      */
10457     regex : null,
10458     /**
10459      * @cfg {String} regexText -- Depricated - use Invalid Text
10460      */
10461     regexText : "",
10462     
10463     /**
10464      * @cfg {String} invalidText The error text to display if {@link #validator} test fails during validation (defaults to "")
10465      */
10466     invalidText : "",
10467     
10468     
10469     
10470     autocomplete: false,
10471     
10472     
10473     fieldLabel : '',
10474     inputType : 'text',
10475     
10476     name : false,
10477     placeholder: false,
10478     before : false,
10479     after : false,
10480     size : false,
10481     hasFocus : false,
10482     preventMark: false,
10483     isFormField : true,
10484     value : '',
10485     labelWidth : 2,
10486     labelAlign : false,
10487     readOnly : false,
10488     align : false,
10489     formatedValue : false,
10490     forceFeedback : false,
10491     
10492     indicatorpos : 'left',
10493     
10494     labellg : 0,
10495     labelmd : 0,
10496     labelsm : 0,
10497     labelxs : 0,
10498     
10499     capture : '',
10500     accept : '',
10501     
10502     parentLabelAlign : function()
10503     {
10504         var parent = this;
10505         while (parent.parent()) {
10506             parent = parent.parent();
10507             if (typeof(parent.labelAlign) !='undefined') {
10508                 return parent.labelAlign;
10509             }
10510         }
10511         return 'left';
10512         
10513     },
10514     
10515     getAutoCreate : function()
10516     {
10517         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
10518         
10519         var id = Roo.id();
10520         
10521         var cfg = {};
10522         
10523         if(this.inputType != 'hidden'){
10524             cfg.cls = 'form-group' //input-group
10525         }
10526         
10527         var input =  {
10528             tag: 'input',
10529             id : id,
10530             type : this.inputType,
10531             value : this.value,
10532             cls : 'form-control',
10533             placeholder : this.placeholder || '',
10534             autocomplete : this.autocomplete || 'new-password'
10535         };
10536         if (this.inputType == 'file') {
10537             input.style = 'overflow:hidden'; // why not in CSS?
10538         }
10539         
10540         if(this.capture.length){
10541             input.capture = this.capture;
10542         }
10543         
10544         if(this.accept.length){
10545             input.accept = this.accept + "/*";
10546         }
10547         
10548         if(this.align){
10549             input.style = (typeof(input.style) == 'undefined') ? ('text-align:' + this.align) : (input.style + 'text-align:' + this.align);
10550         }
10551         
10552         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
10553             input.maxLength = this.maxLength;
10554         }
10555         
10556         if (this.disabled) {
10557             input.disabled=true;
10558         }
10559         
10560         if (this.readOnly) {
10561             input.readonly=true;
10562         }
10563         
10564         if (this.name) {
10565             input.name = this.name;
10566         }
10567         
10568         if (this.size) {
10569             input.cls += ' input-' + this.size;
10570         }
10571         
10572         var settings=this;
10573         ['xs','sm','md','lg'].map(function(size){
10574             if (settings[size]) {
10575                 cfg.cls += ' col-' + size + '-' + settings[size];
10576             }
10577         });
10578         
10579         var inputblock = input;
10580         
10581         var feedback = {
10582             tag: 'span',
10583             cls: 'glyphicon form-control-feedback'
10584         };
10585             
10586         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
10587             
10588             inputblock = {
10589                 cls : 'has-feedback',
10590                 cn :  [
10591                     input,
10592                     feedback
10593                 ] 
10594             };  
10595         }
10596         
10597         if (this.before || this.after) {
10598             
10599             inputblock = {
10600                 cls : 'input-group',
10601                 cn :  [] 
10602             };
10603             
10604             if (this.before && typeof(this.before) == 'string') {
10605                 
10606                 inputblock.cn.push({
10607                     tag :'span',
10608                     cls : 'roo-input-before input-group-addon input-group-prepend input-group-text',
10609                     html : this.before
10610                 });
10611             }
10612             if (this.before && typeof(this.before) == 'object') {
10613                 this.before = Roo.factory(this.before);
10614                 
10615                 inputblock.cn.push({
10616                     tag :'span',
10617                     cls : 'roo-input-before input-group-prepend   input-group-' +
10618                         (this.before.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
10619                 });
10620             }
10621             
10622             inputblock.cn.push(input);
10623             
10624             if (this.after && typeof(this.after) == 'string') {
10625                 inputblock.cn.push({
10626                     tag :'span',
10627                     cls : 'roo-input-after input-group-append input-group-text input-group-addon',
10628                     html : this.after
10629                 });
10630             }
10631             if (this.after && typeof(this.after) == 'object') {
10632                 this.after = Roo.factory(this.after);
10633                 
10634                 inputblock.cn.push({
10635                     tag :'span',
10636                     cls : 'roo-input-after input-group-append  input-group-' +
10637                         (this.after.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
10638                 });
10639             }
10640             
10641             if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
10642                 inputblock.cls += ' has-feedback';
10643                 inputblock.cn.push(feedback);
10644             }
10645         };
10646         var indicator = {
10647             tag : 'i',
10648             cls : 'roo-required-indicator ' + (this.indicatorpos == 'right'  ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
10649             tooltip : 'This field is required'
10650         };
10651         if (this.allowBlank ) {
10652             indicator.style = this.allowBlank ? ' display:none' : '';
10653         }
10654         if (align ==='left' && this.fieldLabel.length) {
10655             
10656             cfg.cls += ' roo-form-group-label-left'  + (Roo.bootstrap.version == 4 ? ' row' : '');
10657             
10658             cfg.cn = [
10659                 indicator,
10660                 {
10661                     tag: 'label',
10662                     'for' :  id,
10663                     cls : 'control-label col-form-label',
10664                     html : this.fieldLabel
10665
10666                 },
10667                 {
10668                     cls : "", 
10669                     cn: [
10670                         inputblock
10671                     ]
10672                 }
10673             ];
10674             
10675             var labelCfg = cfg.cn[1];
10676             var contentCfg = cfg.cn[2];
10677             
10678             if(this.indicatorpos == 'right'){
10679                 cfg.cn = [
10680                     {
10681                         tag: 'label',
10682                         'for' :  id,
10683                         cls : 'control-label col-form-label',
10684                         cn : [
10685                             {
10686                                 tag : 'span',
10687                                 html : this.fieldLabel
10688                             },
10689                             indicator
10690                         ]
10691                     },
10692                     {
10693                         cls : "",
10694                         cn: [
10695                             inputblock
10696                         ]
10697                     }
10698
10699                 ];
10700                 
10701                 labelCfg = cfg.cn[0];
10702                 contentCfg = cfg.cn[1];
10703             
10704             }
10705             
10706             if(this.labelWidth > 12){
10707                 labelCfg.style = "width: " + this.labelWidth + 'px';
10708             }
10709             
10710             if(this.labelWidth < 13 && this.labelmd == 0){
10711                 this.labelmd = this.labelWidth;
10712             }
10713             
10714             if(this.labellg > 0){
10715                 labelCfg.cls += ' col-lg-' + this.labellg;
10716                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
10717             }
10718             
10719             if(this.labelmd > 0){
10720                 labelCfg.cls += ' col-md-' + this.labelmd;
10721                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
10722             }
10723             
10724             if(this.labelsm > 0){
10725                 labelCfg.cls += ' col-sm-' + this.labelsm;
10726                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
10727             }
10728             
10729             if(this.labelxs > 0){
10730                 labelCfg.cls += ' col-xs-' + this.labelxs;
10731                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
10732             }
10733             
10734             
10735         } else if ( this.fieldLabel.length) {
10736                 
10737             
10738             
10739             cfg.cn = [
10740                 {
10741                     tag : 'i',
10742                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
10743                     tooltip : 'This field is required',
10744                     style : this.allowBlank ? ' display:none' : '' 
10745                 },
10746                 {
10747                     tag: 'label',
10748                    //cls : 'input-group-addon',
10749                     html : this.fieldLabel
10750
10751                 },
10752
10753                inputblock
10754
10755            ];
10756            
10757            if(this.indicatorpos == 'right'){
10758        
10759                 cfg.cn = [
10760                     {
10761                         tag: 'label',
10762                        //cls : 'input-group-addon',
10763                         html : this.fieldLabel
10764
10765                     },
10766                     {
10767                         tag : 'i',
10768                         cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
10769                         tooltip : 'This field is required',
10770                         style : this.allowBlank ? ' display:none' : '' 
10771                     },
10772
10773                    inputblock
10774
10775                ];
10776
10777             }
10778
10779         } else {
10780             
10781             cfg.cn = [
10782
10783                     inputblock
10784
10785             ];
10786                 
10787                 
10788         };
10789         
10790         if (this.parentType === 'Navbar' &&  this.parent().bar) {
10791            cfg.cls += ' navbar-form';
10792         }
10793         
10794         if (this.parentType === 'NavGroup' && !(Roo.bootstrap.version == 4 && this.parent().form)) {
10795             // on BS4 we do this only if not form 
10796             cfg.cls += ' navbar-form';
10797             cfg.tag = 'li';
10798         }
10799         
10800         return cfg;
10801         
10802     },
10803     /**
10804      * return the real input element.
10805      */
10806     inputEl: function ()
10807     {
10808         return this.el.select('input.form-control',true).first();
10809     },
10810     
10811     tooltipEl : function()
10812     {
10813         return this.inputEl();
10814     },
10815     
10816     indicatorEl : function()
10817     {
10818         if (Roo.bootstrap.version == 4) {
10819             return false; // not enabled in v4 yet.
10820         }
10821         
10822         var indicator = this.el.select('i.roo-required-indicator',true).first();
10823         
10824         if(!indicator){
10825             return false;
10826         }
10827         
10828         return indicator;
10829         
10830     },
10831     
10832     setDisabled : function(v)
10833     {
10834         var i  = this.inputEl().dom;
10835         if (!v) {
10836             i.removeAttribute('disabled');
10837             return;
10838             
10839         }
10840         i.setAttribute('disabled','true');
10841     },
10842     initEvents : function()
10843     {
10844           
10845         this.inputEl().on("keydown" , this.fireKey,  this);
10846         this.inputEl().on("focus", this.onFocus,  this);
10847         this.inputEl().on("blur", this.onBlur,  this);
10848         
10849         this.inputEl().relayEvent('keyup', this);
10850         
10851         this.indicator = this.indicatorEl();
10852         
10853         if(this.indicator){
10854             this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible'); // changed from invisible??? - 
10855         }
10856  
10857         // reference to original value for reset
10858         this.originalValue = this.getValue();
10859         //Roo.form.TextField.superclass.initEvents.call(this);
10860         if(this.validationEvent == 'keyup'){
10861             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
10862             this.inputEl().on('keyup', this.filterValidation, this);
10863         }
10864         else if(this.validationEvent !== false){
10865             this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
10866         }
10867         
10868         if(this.selectOnFocus){
10869             this.on("focus", this.preFocus, this);
10870             
10871         }
10872         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
10873             this.inputEl().on("keypress", this.filterKeys, this);
10874         } else {
10875             this.inputEl().relayEvent('keypress', this);
10876         }
10877        /* if(this.grow){
10878             this.el.on("keyup", this.onKeyUp,  this, {buffer:50});
10879             this.el.on("click", this.autoSize,  this);
10880         }
10881         */
10882         if(this.inputEl().is('input[type=password]') && Roo.isSafari){
10883             this.inputEl().on('keydown', this.SafariOnKeyDown, this);
10884         }
10885         
10886         if (typeof(this.before) == 'object') {
10887             this.before.render(this.el.select('.roo-input-before',true).first());
10888         }
10889         if (typeof(this.after) == 'object') {
10890             this.after.render(this.el.select('.roo-input-after',true).first());
10891         }
10892         
10893         this.inputEl().on('change', this.onChange, this);
10894         
10895     },
10896     filterValidation : function(e){
10897         if(!e.isNavKeyPress()){
10898             this.validationTask.delay(this.validationDelay);
10899         }
10900     },
10901      /**
10902      * Validates the field value
10903      * @return {Boolean} True if the value is valid, else false
10904      */
10905     validate : function(){
10906         //if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
10907         if(this.disabled || this.validateValue(this.getRawValue())){
10908             this.markValid();
10909             return true;
10910         }
10911         
10912         this.markInvalid();
10913         return false;
10914     },
10915     
10916     
10917     /**
10918      * Validates a value according to the field's validation rules and marks the field as invalid
10919      * if the validation fails
10920      * @param {Mixed} value The value to validate
10921      * @return {Boolean} True if the value is valid, else false
10922      */
10923     validateValue : function(value)
10924     {
10925         if(this.getVisibilityEl().hasClass('hidden')){
10926             return true;
10927         }
10928         
10929         if(value.length < 1)  { // if it's blank
10930             if(this.allowBlank){
10931                 return true;
10932             }
10933             return false;
10934         }
10935         
10936         if(value.length < this.minLength){
10937             return false;
10938         }
10939         if(value.length > this.maxLength){
10940             return false;
10941         }
10942         if(this.vtype){
10943             var vt = Roo.form.VTypes;
10944             if(!vt[this.vtype](value, this)){
10945                 return false;
10946             }
10947         }
10948         if(typeof this.validator == "function"){
10949             var msg = this.validator(value);
10950             if(msg !== true){
10951                 return false;
10952             }
10953             if (typeof(msg) == 'string') {
10954                 this.invalidText = msg;
10955             }
10956         }
10957         
10958         if(this.regex && !this.regex.test(value)){
10959             return false;
10960         }
10961         
10962         return true;
10963     },
10964     
10965      // private
10966     fireKey : function(e){
10967         //Roo.log('field ' + e.getKey());
10968         if(e.isNavKeyPress()){
10969             this.fireEvent("specialkey", this, e);
10970         }
10971     },
10972     focus : function (selectText){
10973         if(this.rendered){
10974             this.inputEl().focus();
10975             if(selectText === true){
10976                 this.inputEl().dom.select();
10977             }
10978         }
10979         return this;
10980     } ,
10981     
10982     onFocus : function(){
10983         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
10984            // this.el.addClass(this.focusClass);
10985         }
10986         if(!this.hasFocus){
10987             this.hasFocus = true;
10988             this.startValue = this.getValue();
10989             this.fireEvent("focus", this);
10990         }
10991     },
10992     
10993     beforeBlur : Roo.emptyFn,
10994
10995     
10996     // private
10997     onBlur : function(){
10998         this.beforeBlur();
10999         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
11000             //this.el.removeClass(this.focusClass);
11001         }
11002         this.hasFocus = false;
11003         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
11004             this.validate();
11005         }
11006         var v = this.getValue();
11007         if(String(v) !== String(this.startValue)){
11008             this.fireEvent('change', this, v, this.startValue);
11009         }
11010         this.fireEvent("blur", this);
11011     },
11012     
11013     onChange : function(e)
11014     {
11015         var v = this.getValue();
11016         if(String(v) !== String(this.startValue)){
11017             this.fireEvent('change', this, v, this.startValue);
11018         }
11019         
11020     },
11021     
11022     /**
11023      * Resets the current field value to the originally loaded value and clears any validation messages
11024      */
11025     reset : function(){
11026         this.setValue(this.originalValue);
11027         this.validate();
11028     },
11029      /**
11030      * Returns the name of the field
11031      * @return {Mixed} name The name field
11032      */
11033     getName: function(){
11034         return this.name;
11035     },
11036      /**
11037      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
11038      * @return {Mixed} value The field value
11039      */
11040     getValue : function(){
11041         
11042         var v = this.inputEl().getValue();
11043         
11044         return v;
11045     },
11046     /**
11047      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
11048      * @return {Mixed} value The field value
11049      */
11050     getRawValue : function(){
11051         var v = this.inputEl().getValue();
11052         
11053         return v;
11054     },
11055     
11056     /**
11057      * Sets the underlying DOM field's value directly, bypassing validation.  To set the value with validation see {@link #setValue}.
11058      * @param {Mixed} value The value to set
11059      */
11060     setRawValue : function(v){
11061         return this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
11062     },
11063     
11064     selectText : function(start, end){
11065         var v = this.getRawValue();
11066         if(v.length > 0){
11067             start = start === undefined ? 0 : start;
11068             end = end === undefined ? v.length : end;
11069             var d = this.inputEl().dom;
11070             if(d.setSelectionRange){
11071                 d.setSelectionRange(start, end);
11072             }else if(d.createTextRange){
11073                 var range = d.createTextRange();
11074                 range.moveStart("character", start);
11075                 range.moveEnd("character", v.length-end);
11076                 range.select();
11077             }
11078         }
11079     },
11080     
11081     /**
11082      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
11083      * @param {Mixed} value The value to set
11084      */
11085     setValue : function(v){
11086         this.value = v;
11087         if(this.rendered){
11088             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
11089             this.validate();
11090         }
11091     },
11092     
11093     /*
11094     processValue : function(value){
11095         if(this.stripCharsRe){
11096             var newValue = value.replace(this.stripCharsRe, '');
11097             if(newValue !== value){
11098                 this.setRawValue(newValue);
11099                 return newValue;
11100             }
11101         }
11102         return value;
11103     },
11104   */
11105     preFocus : function(){
11106         
11107         if(this.selectOnFocus){
11108             this.inputEl().dom.select();
11109         }
11110     },
11111     filterKeys : function(e){
11112         var k = e.getKey();
11113         if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
11114             return;
11115         }
11116         var c = e.getCharCode(), cc = String.fromCharCode(c);
11117         if(Roo.isIE && (e.isSpecialKey() || !cc)){
11118             return;
11119         }
11120         if(!this.maskRe.test(cc)){
11121             e.stopEvent();
11122         }
11123     },
11124      /**
11125      * Clear any invalid styles/messages for this field
11126      */
11127     clearInvalid : function(){
11128         
11129         if(!this.el || this.preventMark){ // not rendered
11130             return;
11131         }
11132         
11133         
11134         this.el.removeClass([this.invalidClass, 'is-invalid']);
11135         
11136         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
11137             
11138             var feedback = this.el.select('.form-control-feedback', true).first();
11139             
11140             if(feedback){
11141                 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
11142             }
11143             
11144         }
11145         
11146         if(this.indicator){
11147             this.indicator.removeClass('visible');
11148             this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
11149         }
11150         
11151         this.fireEvent('valid', this);
11152     },
11153     
11154      /**
11155      * Mark this field as valid
11156      */
11157     markValid : function()
11158     {
11159         if(!this.el  || this.preventMark){ // not rendered...
11160             return;
11161         }
11162         
11163         this.el.removeClass([this.invalidClass, this.validClass]);
11164         this.inputEl().removeClass(['is-valid', 'is-invalid']);
11165
11166         var feedback = this.el.select('.form-control-feedback', true).first();
11167             
11168         if(feedback){
11169             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
11170         }
11171         
11172         if(this.indicator){
11173             this.indicator.removeClass('visible');
11174             this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
11175         }
11176         
11177         if(this.disabled){
11178             return;
11179         }
11180         
11181            
11182         if(this.allowBlank && !this.getRawValue().length){
11183             return;
11184         }
11185         if (Roo.bootstrap.version == 3) {
11186             this.el.addClass(this.validClass);
11187         } else {
11188             this.inputEl().addClass('is-valid');
11189         }
11190
11191         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
11192             
11193             var feedback = this.el.select('.form-control-feedback', true).first();
11194             
11195             if(feedback){
11196                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
11197                 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
11198             }
11199             
11200         }
11201         
11202         this.fireEvent('valid', this);
11203     },
11204     
11205      /**
11206      * Mark this field as invalid
11207      * @param {String} msg The validation message
11208      */
11209     markInvalid : function(msg)
11210     {
11211         if(!this.el  || this.preventMark){ // not rendered
11212             return;
11213         }
11214         
11215         this.el.removeClass([this.invalidClass, this.validClass]);
11216         this.inputEl().removeClass(['is-valid', 'is-invalid']);
11217         
11218         var feedback = this.el.select('.form-control-feedback', true).first();
11219             
11220         if(feedback){
11221             this.el.select('.form-control-feedback', true).first().removeClass(
11222                     [this.invalidFeedbackClass, this.validFeedbackClass]);
11223         }
11224
11225         if(this.disabled){
11226             return;
11227         }
11228         
11229         if(this.allowBlank && !this.getRawValue().length){
11230             return;
11231         }
11232         
11233         if(this.indicator){
11234             this.indicator.removeClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
11235             this.indicator.addClass('visible');
11236         }
11237         if (Roo.bootstrap.version == 3) {
11238             this.el.addClass(this.invalidClass);
11239         } else {
11240             this.inputEl().addClass('is-invalid');
11241         }
11242         
11243         
11244         
11245         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
11246             
11247             var feedback = this.el.select('.form-control-feedback', true).first();
11248             
11249             if(feedback){
11250                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
11251                 
11252                 if(this.getValue().length || this.forceFeedback){
11253                     this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
11254                 }
11255                 
11256             }
11257             
11258         }
11259         
11260         this.fireEvent('invalid', this, msg);
11261     },
11262     // private
11263     SafariOnKeyDown : function(event)
11264     {
11265         // this is a workaround for a password hang bug on chrome/ webkit.
11266         if (this.inputEl().dom.type != 'password') {
11267             return;
11268         }
11269         
11270         var isSelectAll = false;
11271         
11272         if(this.inputEl().dom.selectionEnd > 0){
11273             isSelectAll = (this.inputEl().dom.selectionEnd - this.inputEl().dom.selectionStart - this.getValue().length == 0) ? true : false;
11274         }
11275         if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
11276             event.preventDefault();
11277             this.setValue('');
11278             return;
11279         }
11280         
11281         if(isSelectAll  && event.getCharCode() > 31 && !event.ctrlKey) { // not backspace and delete key (or ctrl-v)
11282             
11283             event.preventDefault();
11284             // this is very hacky as keydown always get's upper case.
11285             //
11286             var cc = String.fromCharCode(event.getCharCode());
11287             this.setValue( event.shiftKey ?  cc : cc.toLowerCase());
11288             
11289         }
11290     },
11291     adjustWidth : function(tag, w){
11292         tag = tag.toLowerCase();
11293         if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
11294             if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
11295                 if(tag == 'input'){
11296                     return w + 2;
11297                 }
11298                 if(tag == 'textarea'){
11299                     return w-2;
11300                 }
11301             }else if(Roo.isOpera){
11302                 if(tag == 'input'){
11303                     return w + 2;
11304                 }
11305                 if(tag == 'textarea'){
11306                     return w-2;
11307                 }
11308             }
11309         }
11310         return w;
11311     },
11312     
11313     setFieldLabel : function(v)
11314     {
11315         if(!this.rendered){
11316             return;
11317         }
11318         
11319         if(this.indicatorEl()){
11320             var ar = this.el.select('label > span',true);
11321             
11322             if (ar.elements.length) {
11323                 this.el.select('label > span',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
11324                 this.fieldLabel = v;
11325                 return;
11326             }
11327             
11328             var br = this.el.select('label',true);
11329             
11330             if(br.elements.length) {
11331                 this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
11332                 this.fieldLabel = v;
11333                 return;
11334             }
11335             
11336             Roo.log('Cannot Found any of label > span || label in input');
11337             return;
11338         }
11339         
11340         this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
11341         this.fieldLabel = v;
11342         
11343         
11344     }
11345 });
11346
11347  
11348 /*
11349  * - LGPL
11350  *
11351  * Input
11352  * 
11353  */
11354
11355 /**
11356  * @class Roo.bootstrap.TextArea
11357  * @extends Roo.bootstrap.Input
11358  * Bootstrap TextArea class
11359  * @cfg {Number} cols Specifies the visible width of a text area
11360  * @cfg {Number} rows Specifies the visible number of lines in a text area
11361  * @cfg {string} wrap (soft|hard)Specifies how the text in a text area is to be wrapped when submitted in a form
11362  * @cfg {string} resize (none|both|horizontal|vertical|inherit|initial)
11363  * @cfg {string} html text
11364  * 
11365  * @constructor
11366  * Create a new TextArea
11367  * @param {Object} config The config object
11368  */
11369
11370 Roo.bootstrap.TextArea = function(config){
11371     Roo.bootstrap.TextArea.superclass.constructor.call(this, config);
11372    
11373 };
11374
11375 Roo.extend(Roo.bootstrap.TextArea, Roo.bootstrap.Input,  {
11376      
11377     cols : false,
11378     rows : 5,
11379     readOnly : false,
11380     warp : 'soft',
11381     resize : false,
11382     value: false,
11383     html: false,
11384     
11385     getAutoCreate : function(){
11386         
11387         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
11388         
11389         var id = Roo.id();
11390         
11391         var cfg = {};
11392         
11393         if(this.inputType != 'hidden'){
11394             cfg.cls = 'form-group' //input-group
11395         }
11396         
11397         var input =  {
11398             tag: 'textarea',
11399             id : id,
11400             warp : this.warp,
11401             rows : this.rows,
11402             value : this.value || '',
11403             html: this.html || '',
11404             cls : 'form-control',
11405             placeholder : this.placeholder || '' 
11406             
11407         };
11408         
11409         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
11410             input.maxLength = this.maxLength;
11411         }
11412         
11413         if(this.resize){
11414             input.style = (typeof(input.style) == 'undefined') ? 'resize:' + this.resize : input.style + 'resize:' + this.resize;
11415         }
11416         
11417         if(this.cols){
11418             input.cols = this.cols;
11419         }
11420         
11421         if (this.readOnly) {
11422             input.readonly = true;
11423         }
11424         
11425         if (this.name) {
11426             input.name = this.name;
11427         }
11428         
11429         if (this.size) {
11430             input.cls = (typeof(input.cls) == 'undefined') ? 'input-' + this.size : input.cls + ' input-' + this.size;
11431         }
11432         
11433         var settings=this;
11434         ['xs','sm','md','lg'].map(function(size){
11435             if (settings[size]) {
11436                 cfg.cls += ' col-' + size + '-' + settings[size];
11437             }
11438         });
11439         
11440         var inputblock = input;
11441         
11442         if(this.hasFeedback && !this.allowBlank){
11443             
11444             var feedback = {
11445                 tag: 'span',
11446                 cls: 'glyphicon form-control-feedback'
11447             };
11448
11449             inputblock = {
11450                 cls : 'has-feedback',
11451                 cn :  [
11452                     input,
11453                     feedback
11454                 ] 
11455             };  
11456         }
11457         
11458         
11459         if (this.before || this.after) {
11460             
11461             inputblock = {
11462                 cls : 'input-group',
11463                 cn :  [] 
11464             };
11465             if (this.before) {
11466                 inputblock.cn.push({
11467                     tag :'span',
11468                     cls : 'input-group-addon',
11469                     html : this.before
11470                 });
11471             }
11472             
11473             inputblock.cn.push(input);
11474             
11475             if(this.hasFeedback && !this.allowBlank){
11476                 inputblock.cls += ' has-feedback';
11477                 inputblock.cn.push(feedback);
11478             }
11479             
11480             if (this.after) {
11481                 inputblock.cn.push({
11482                     tag :'span',
11483                     cls : 'input-group-addon',
11484                     html : this.after
11485                 });
11486             }
11487             
11488         }
11489         
11490         if (align ==='left' && this.fieldLabel.length) {
11491             cfg.cn = [
11492                 {
11493                     tag: 'label',
11494                     'for' :  id,
11495                     cls : 'control-label',
11496                     html : this.fieldLabel
11497                 },
11498                 {
11499                     cls : "",
11500                     cn: [
11501                         inputblock
11502                     ]
11503                 }
11504
11505             ];
11506             
11507             if(this.labelWidth > 12){
11508                 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
11509             }
11510
11511             if(this.labelWidth < 13 && this.labelmd == 0){
11512                 this.labelmd = this.labelWidth;
11513             }
11514
11515             if(this.labellg > 0){
11516                 cfg.cn[0].cls += ' col-lg-' + this.labellg;
11517                 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
11518             }
11519
11520             if(this.labelmd > 0){
11521                 cfg.cn[0].cls += ' col-md-' + this.labelmd;
11522                 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
11523             }
11524
11525             if(this.labelsm > 0){
11526                 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
11527                 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
11528             }
11529
11530             if(this.labelxs > 0){
11531                 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
11532                 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
11533             }
11534             
11535         } else if ( this.fieldLabel.length) {
11536             cfg.cn = [
11537
11538                {
11539                    tag: 'label',
11540                    //cls : 'input-group-addon',
11541                    html : this.fieldLabel
11542
11543                },
11544
11545                inputblock
11546
11547            ];
11548
11549         } else {
11550
11551             cfg.cn = [
11552
11553                 inputblock
11554
11555             ];
11556                 
11557         }
11558         
11559         if (this.disabled) {
11560             input.disabled=true;
11561         }
11562         
11563         return cfg;
11564         
11565     },
11566     /**
11567      * return the real textarea element.
11568      */
11569     inputEl: function ()
11570     {
11571         return this.el.select('textarea.form-control',true).first();
11572     },
11573     
11574     /**
11575      * Clear any invalid styles/messages for this field
11576      */
11577     clearInvalid : function()
11578     {
11579         
11580         if(!this.el || this.preventMark){ // not rendered
11581             return;
11582         }
11583         
11584         var label = this.el.select('label', true).first();
11585         var icon = this.el.select('i.fa-star', true).first();
11586         
11587         if(label && icon){
11588             icon.remove();
11589         }
11590         this.el.removeClass( this.validClass);
11591         this.inputEl().removeClass('is-invalid');
11592          
11593         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
11594             
11595             var feedback = this.el.select('.form-control-feedback', true).first();
11596             
11597             if(feedback){
11598                 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
11599             }
11600             
11601         }
11602         
11603         this.fireEvent('valid', this);
11604     },
11605     
11606      /**
11607      * Mark this field as valid
11608      */
11609     markValid : function()
11610     {
11611         if(!this.el  || this.preventMark){ // not rendered
11612             return;
11613         }
11614         
11615         this.el.removeClass([this.invalidClass, this.validClass]);
11616         this.inputEl().removeClass(['is-valid', 'is-invalid']);
11617         
11618         var feedback = this.el.select('.form-control-feedback', true).first();
11619             
11620         if(feedback){
11621             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
11622         }
11623
11624         if(this.disabled || this.allowBlank){
11625             return;
11626         }
11627         
11628         var label = this.el.select('label', true).first();
11629         var icon = this.el.select('i.fa-star', true).first();
11630         
11631         if(label && icon){
11632             icon.remove();
11633         }
11634         if (Roo.bootstrap.version == 3) {
11635             this.el.addClass(this.validClass);
11636         } else {
11637             this.inputEl().addClass('is-valid');
11638         }
11639         
11640         
11641         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
11642             
11643             var feedback = this.el.select('.form-control-feedback', true).first();
11644             
11645             if(feedback){
11646                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
11647                 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
11648             }
11649             
11650         }
11651         
11652         this.fireEvent('valid', this);
11653     },
11654     
11655      /**
11656      * Mark this field as invalid
11657      * @param {String} msg The validation message
11658      */
11659     markInvalid : function(msg)
11660     {
11661         if(!this.el  || this.preventMark){ // not rendered
11662             return;
11663         }
11664         
11665         this.el.removeClass([this.invalidClass, this.validClass]);
11666         this.inputEl().removeClass(['is-valid', 'is-invalid']);
11667         
11668         var feedback = this.el.select('.form-control-feedback', true).first();
11669             
11670         if(feedback){
11671             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
11672         }
11673
11674         if(this.disabled || this.allowBlank){
11675             return;
11676         }
11677         
11678         var label = this.el.select('label', true).first();
11679         var icon = this.el.select('i.fa-star', true).first();
11680         
11681         if(!this.getValue().length && label && !icon){
11682             this.el.createChild({
11683                 tag : 'i',
11684                 cls : 'text-danger fa fa-lg fa-star',
11685                 tooltip : 'This field is required',
11686                 style : 'margin-right:5px;'
11687             }, label, true);
11688         }
11689         
11690         if (Roo.bootstrap.version == 3) {
11691             this.el.addClass(this.invalidClass);
11692         } else {
11693             this.inputEl().addClass('is-invalid');
11694         }
11695         
11696         // fixme ... this may be depricated need to test..
11697         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
11698             
11699             var feedback = this.el.select('.form-control-feedback', true).first();
11700             
11701             if(feedback){
11702                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
11703                 
11704                 if(this.getValue().length || this.forceFeedback){
11705                     this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
11706                 }
11707                 
11708             }
11709             
11710         }
11711         
11712         this.fireEvent('invalid', this, msg);
11713     }
11714 });
11715
11716  
11717 /*
11718  * - LGPL
11719  *
11720  * trigger field - base class for combo..
11721  * 
11722  */
11723  
11724 /**
11725  * @class Roo.bootstrap.TriggerField
11726  * @extends Roo.bootstrap.Input
11727  * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
11728  * The trigger has no default action, so you must assign a function to implement the trigger click handler by
11729  * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
11730  * for which you can provide a custom implementation.  For example:
11731  * <pre><code>
11732 var trigger = new Roo.bootstrap.TriggerField();
11733 trigger.onTriggerClick = myTriggerFn;
11734 trigger.applyTo('my-field');
11735 </code></pre>
11736  *
11737  * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
11738  * {@link Roo.bootstrap.DateField} and {@link Roo.bootstrap.ComboBox} are perfect examples of this.
11739  * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
11740  * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
11741  * @cfg {String} caret (search|calendar) BS3 only - carat fa name
11742
11743  * @constructor
11744  * Create a new TriggerField.
11745  * @param {Object} config Configuration options (valid {@Roo.bootstrap.Input} config options will also be applied
11746  * to the base TextField)
11747  */
11748 Roo.bootstrap.TriggerField = function(config){
11749     this.mimicing = false;
11750     Roo.bootstrap.TriggerField.superclass.constructor.call(this, config);
11751 };
11752
11753 Roo.extend(Roo.bootstrap.TriggerField, Roo.bootstrap.Input,  {
11754     /**
11755      * @cfg {String} triggerClass A CSS class to apply to the trigger
11756      */
11757      /**
11758      * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
11759      */
11760     hideTrigger:false,
11761
11762     /**
11763      * @cfg {Boolean} removable (true|false) special filter default false
11764      */
11765     removable : false,
11766     
11767     /** @cfg {Boolean} grow @hide */
11768     /** @cfg {Number} growMin @hide */
11769     /** @cfg {Number} growMax @hide */
11770
11771     /**
11772      * @hide 
11773      * @method
11774      */
11775     autoSize: Roo.emptyFn,
11776     // private
11777     monitorTab : true,
11778     // private
11779     deferHeight : true,
11780
11781     
11782     actionMode : 'wrap',
11783     
11784     caret : false,
11785     
11786     
11787     getAutoCreate : function(){
11788        
11789         var align = this.labelAlign || this.parentLabelAlign();
11790         
11791         var id = Roo.id();
11792         
11793         var cfg = {
11794             cls: 'form-group' //input-group
11795         };
11796         
11797         
11798         var input =  {
11799             tag: 'input',
11800             id : id,
11801             type : this.inputType,
11802             cls : 'form-control',
11803             autocomplete: 'new-password',
11804             placeholder : this.placeholder || '' 
11805             
11806         };
11807         if (this.name) {
11808             input.name = this.name;
11809         }
11810         if (this.size) {
11811             input.cls += ' input-' + this.size;
11812         }
11813         
11814         if (this.disabled) {
11815             input.disabled=true;
11816         }
11817         
11818         var inputblock = input;
11819         
11820         if(this.hasFeedback && !this.allowBlank){
11821             
11822             var feedback = {
11823                 tag: 'span',
11824                 cls: 'glyphicon form-control-feedback'
11825             };
11826             
11827             if(this.removable && !this.editable  ){
11828                 inputblock = {
11829                     cls : 'has-feedback',
11830                     cn :  [
11831                         inputblock,
11832                         {
11833                             tag: 'button',
11834                             html : 'x',
11835                             cls : 'roo-combo-removable-btn close'
11836                         },
11837                         feedback
11838                     ] 
11839                 };
11840             } else {
11841                 inputblock = {
11842                     cls : 'has-feedback',
11843                     cn :  [
11844                         inputblock,
11845                         feedback
11846                     ] 
11847                 };
11848             }
11849
11850         } else {
11851             if(this.removable && !this.editable ){
11852                 inputblock = {
11853                     cls : 'roo-removable',
11854                     cn :  [
11855                         inputblock,
11856                         {
11857                             tag: 'button',
11858                             html : 'x',
11859                             cls : 'roo-combo-removable-btn close'
11860                         }
11861                     ] 
11862                 };
11863             }
11864         }
11865         
11866         if (this.before || this.after) {
11867             
11868             inputblock = {
11869                 cls : 'input-group',
11870                 cn :  [] 
11871             };
11872             if (this.before) {
11873                 inputblock.cn.push({
11874                     tag :'span',
11875                     cls : 'input-group-addon input-group-prepend input-group-text',
11876                     html : this.before
11877                 });
11878             }
11879             
11880             inputblock.cn.push(input);
11881             
11882             if(this.hasFeedback && !this.allowBlank){
11883                 inputblock.cls += ' has-feedback';
11884                 inputblock.cn.push(feedback);
11885             }
11886             
11887             if (this.after) {
11888                 inputblock.cn.push({
11889                     tag :'span',
11890                     cls : 'input-group-addon input-group-append input-group-text',
11891                     html : this.after
11892                 });
11893             }
11894             
11895         };
11896         
11897       
11898         
11899         var ibwrap = inputblock;
11900         
11901         if(this.multiple){
11902             ibwrap = {
11903                 tag: 'ul',
11904                 cls: 'roo-select2-choices',
11905                 cn:[
11906                     {
11907                         tag: 'li',
11908                         cls: 'roo-select2-search-field',
11909                         cn: [
11910
11911                             inputblock
11912                         ]
11913                     }
11914                 ]
11915             };
11916                 
11917         }
11918         
11919         var combobox = {
11920             cls: 'roo-select2-container input-group',
11921             cn: [
11922                  {
11923                     tag: 'input',
11924                     type : 'hidden',
11925                     cls: 'form-hidden-field'
11926                 },
11927                 ibwrap
11928             ]
11929         };
11930         
11931         if(!this.multiple && this.showToggleBtn){
11932             
11933             var caret = {
11934                         tag: 'span',
11935                         cls: 'caret'
11936              };
11937             if (this.caret != false) {
11938                 caret = {
11939                      tag: 'i',
11940                      cls: 'fa fa-' + this.caret
11941                 };
11942                 
11943             }
11944             
11945             combobox.cn.push({
11946                 tag :'span',
11947                 cls : 'input-group-addon input-group-append input-group-text btn dropdown-toggle',
11948                 cn : [
11949                     Roo.bootstrap.version == 3 ? caret : '',
11950                     {
11951                         tag: 'span',
11952                         cls: 'combobox-clear',
11953                         cn  : [
11954                             {
11955                                 tag : 'i',
11956                                 cls: 'icon-remove'
11957                             }
11958                         ]
11959                     }
11960                 ]
11961
11962             })
11963         }
11964         
11965         if(this.multiple){
11966             combobox.cls += ' roo-select2-container-multi';
11967         }
11968          var indicator = {
11969             tag : 'i',
11970             cls : 'roo-required-indicator ' + (this.indicatorpos == 'right'  ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
11971             tooltip : 'This field is required'
11972         };
11973         if (Roo.bootstrap.version == 4) {
11974             indicator = {
11975                 tag : 'i',
11976                 style : 'display:none'
11977             };
11978         }
11979         
11980         
11981         if (align ==='left' && this.fieldLabel.length) {
11982             
11983             cfg.cls += ' roo-form-group-label-left'  + (Roo.bootstrap.version == 4 ? ' row' : '');
11984
11985             cfg.cn = [
11986                 indicator,
11987                 {
11988                     tag: 'label',
11989                     'for' :  id,
11990                     cls : 'control-label',
11991                     html : this.fieldLabel
11992
11993                 },
11994                 {
11995                     cls : "", 
11996                     cn: [
11997                         combobox
11998                     ]
11999                 }
12000
12001             ];
12002             
12003             var labelCfg = cfg.cn[1];
12004             var contentCfg = cfg.cn[2];
12005             
12006             if(this.indicatorpos == 'right'){
12007                 cfg.cn = [
12008                     {
12009                         tag: 'label',
12010                         'for' :  id,
12011                         cls : 'control-label',
12012                         cn : [
12013                             {
12014                                 tag : 'span',
12015                                 html : this.fieldLabel
12016                             },
12017                             indicator
12018                         ]
12019                     },
12020                     {
12021                         cls : "", 
12022                         cn: [
12023                             combobox
12024                         ]
12025                     }
12026
12027                 ];
12028                 
12029                 labelCfg = cfg.cn[0];
12030                 contentCfg = cfg.cn[1];
12031             }
12032             
12033             if(this.labelWidth > 12){
12034                 labelCfg.style = "width: " + this.labelWidth + 'px';
12035             }
12036             
12037             if(this.labelWidth < 13 && this.labelmd == 0){
12038                 this.labelmd = this.labelWidth;
12039             }
12040             
12041             if(this.labellg > 0){
12042                 labelCfg.cls += ' col-lg-' + this.labellg;
12043                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
12044             }
12045             
12046             if(this.labelmd > 0){
12047                 labelCfg.cls += ' col-md-' + this.labelmd;
12048                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
12049             }
12050             
12051             if(this.labelsm > 0){
12052                 labelCfg.cls += ' col-sm-' + this.labelsm;
12053                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
12054             }
12055             
12056             if(this.labelxs > 0){
12057                 labelCfg.cls += ' col-xs-' + this.labelxs;
12058                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
12059             }
12060             
12061         } else if ( this.fieldLabel.length) {
12062 //                Roo.log(" label");
12063             cfg.cn = [
12064                 indicator,
12065                {
12066                    tag: 'label',
12067                    //cls : 'input-group-addon',
12068                    html : this.fieldLabel
12069
12070                },
12071
12072                combobox
12073
12074             ];
12075             
12076             if(this.indicatorpos == 'right'){
12077                 
12078                 cfg.cn = [
12079                     {
12080                        tag: 'label',
12081                        cn : [
12082                            {
12083                                tag : 'span',
12084                                html : this.fieldLabel
12085                            },
12086                            indicator
12087                        ]
12088
12089                     },
12090                     combobox
12091
12092                 ];
12093
12094             }
12095
12096         } else {
12097             
12098 //                Roo.log(" no label && no align");
12099                 cfg = combobox
12100                      
12101                 
12102         }
12103         
12104         var settings=this;
12105         ['xs','sm','md','lg'].map(function(size){
12106             if (settings[size]) {
12107                 cfg.cls += ' col-' + size + '-' + settings[size];
12108             }
12109         });
12110         
12111         return cfg;
12112         
12113     },
12114     
12115     
12116     
12117     // private
12118     onResize : function(w, h){
12119 //        Roo.bootstrap.TriggerField.superclass.onResize.apply(this, arguments);
12120 //        if(typeof w == 'number'){
12121 //            var x = w - this.trigger.getWidth();
12122 //            this.inputEl().setWidth(this.adjustWidth('input', x));
12123 //            this.trigger.setStyle('left', x+'px');
12124 //        }
12125     },
12126
12127     // private
12128     adjustSize : Roo.BoxComponent.prototype.adjustSize,
12129
12130     // private
12131     getResizeEl : function(){
12132         return this.inputEl();
12133     },
12134
12135     // private
12136     getPositionEl : function(){
12137         return this.inputEl();
12138     },
12139
12140     // private
12141     alignErrorIcon : function(){
12142         this.errorIcon.alignTo(this.inputEl(), 'tl-tr', [2, 0]);
12143     },
12144
12145     // private
12146     initEvents : function(){
12147         
12148         this.createList();
12149         
12150         Roo.bootstrap.TriggerField.superclass.initEvents.call(this);
12151         //this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
12152         if(!this.multiple && this.showToggleBtn){
12153             this.trigger = this.el.select('span.dropdown-toggle',true).first();
12154             if(this.hideTrigger){
12155                 this.trigger.setDisplayed(false);
12156             }
12157             this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
12158         }
12159         
12160         if(this.multiple){
12161             this.inputEl().on("click", this.onTriggerClick, this, {preventDefault:true});
12162         }
12163         
12164         if(this.removable && !this.editable && !this.tickable){
12165             var close = this.closeTriggerEl();
12166             
12167             if(close){
12168                 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
12169                 close.on('click', this.removeBtnClick, this, close);
12170             }
12171         }
12172         
12173         //this.trigger.addClassOnOver('x-form-trigger-over');
12174         //this.trigger.addClassOnClick('x-form-trigger-click');
12175         
12176         //if(!this.width){
12177         //    this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
12178         //}
12179     },
12180     
12181     closeTriggerEl : function()
12182     {
12183         var close = this.el.select('.roo-combo-removable-btn', true).first();
12184         return close ? close : false;
12185     },
12186     
12187     removeBtnClick : function(e, h, el)
12188     {
12189         e.preventDefault();
12190         
12191         if(this.fireEvent("remove", this) !== false){
12192             this.reset();
12193             this.fireEvent("afterremove", this)
12194         }
12195     },
12196     
12197     createList : function()
12198     {
12199         this.list = Roo.get(document.body).createChild({
12200             tag: Roo.bootstrap.version == 4 ? 'div' : 'ul',
12201             cls: 'typeahead typeahead-long dropdown-menu',
12202             style: 'display:none'
12203         });
12204         
12205         this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';;
12206         
12207     },
12208
12209     // private
12210     initTrigger : function(){
12211        
12212     },
12213
12214     // private
12215     onDestroy : function(){
12216         if(this.trigger){
12217             this.trigger.removeAllListeners();
12218           //  this.trigger.remove();
12219         }
12220         //if(this.wrap){
12221         //    this.wrap.remove();
12222         //}
12223         Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
12224     },
12225
12226     // private
12227     onFocus : function(){
12228         Roo.bootstrap.TriggerField.superclass.onFocus.call(this);
12229         /*
12230         if(!this.mimicing){
12231             this.wrap.addClass('x-trigger-wrap-focus');
12232             this.mimicing = true;
12233             Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
12234             if(this.monitorTab){
12235                 this.el.on("keydown", this.checkTab, this);
12236             }
12237         }
12238         */
12239     },
12240
12241     // private
12242     checkTab : function(e){
12243         if(e.getKey() == e.TAB){
12244             this.triggerBlur();
12245         }
12246     },
12247
12248     // private
12249     onBlur : function(){
12250         // do nothing
12251     },
12252
12253     // private
12254     mimicBlur : function(e, t){
12255         /*
12256         if(!this.wrap.contains(t) && this.validateBlur()){
12257             this.triggerBlur();
12258         }
12259         */
12260     },
12261
12262     // private
12263     triggerBlur : function(){
12264         this.mimicing = false;
12265         Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
12266         if(this.monitorTab){
12267             this.el.un("keydown", this.checkTab, this);
12268         }
12269         //this.wrap.removeClass('x-trigger-wrap-focus');
12270         Roo.bootstrap.TriggerField.superclass.onBlur.call(this);
12271     },
12272
12273     // private
12274     // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
12275     validateBlur : function(e, t){
12276         return true;
12277     },
12278
12279     // private
12280     onDisable : function(){
12281         this.inputEl().dom.disabled = true;
12282         //Roo.bootstrap.TriggerField.superclass.onDisable.call(this);
12283         //if(this.wrap){
12284         //    this.wrap.addClass('x-item-disabled');
12285         //}
12286     },
12287
12288     // private
12289     onEnable : function(){
12290         this.inputEl().dom.disabled = false;
12291         //Roo.bootstrap.TriggerField.superclass.onEnable.call(this);
12292         //if(this.wrap){
12293         //    this.el.removeClass('x-item-disabled');
12294         //}
12295     },
12296
12297     // private
12298     onShow : function(){
12299         var ae = this.getActionEl();
12300         
12301         if(ae){
12302             ae.dom.style.display = '';
12303             ae.dom.style.visibility = 'visible';
12304         }
12305     },
12306
12307     // private
12308     
12309     onHide : function(){
12310         var ae = this.getActionEl();
12311         ae.dom.style.display = 'none';
12312     },
12313
12314     /**
12315      * The function that should handle the trigger's click event.  This method does nothing by default until overridden
12316      * by an implementing function.
12317      * @method
12318      * @param {EventObject} e
12319      */
12320     onTriggerClick : Roo.emptyFn
12321 });
12322  
12323 /*
12324 * Licence: LGPL
12325 */
12326
12327 /**
12328  * @class Roo.bootstrap.CardUploader
12329  * @extends Roo.bootstrap.Button
12330  * Bootstrap Card Uploader class - it's a button which when you add files to it, adds cards below with preview and the name...
12331  * @cfg {Number} errorTimeout default 3000
12332  * @cfg {Array}  images  an array of ?? Img objects ??? when loading existing files..
12333  * @cfg {Array}  html The button text.
12334
12335  *
12336  * @constructor
12337  * Create a new CardUploader
12338  * @param {Object} config The config object
12339  */
12340
12341 Roo.bootstrap.CardUploader = function(config){
12342     
12343  
12344     
12345     Roo.bootstrap.CardUploader.superclass.constructor.call(this, config);
12346     
12347     
12348     this.fileCollection   = new Roo.util.MixedCollection(false,function(r) {
12349         return r.data.id
12350         });
12351     
12352     
12353 };
12354
12355 Roo.extend(Roo.bootstrap.CardUploader, Roo.bootstrap.Input,  {
12356     
12357      
12358     errorTimeout : 3000,
12359      
12360     images : false,
12361    
12362     fileCollection : false,
12363     allowBlank : true,
12364     
12365     getAutoCreate : function()
12366     {
12367         
12368         var cfg =  {
12369             cls :'form-group' ,
12370             cn : [
12371                
12372                 {
12373                     tag: 'label',
12374                    //cls : 'input-group-addon',
12375                     html : this.fieldLabel
12376
12377                 },
12378
12379                 {
12380                     tag: 'input',
12381                     type : 'hidden',
12382                     value : this.value,
12383                     cls : 'd-none  form-control'
12384                 },
12385                 
12386                 {
12387                     tag: 'input',
12388                     multiple : 'multiple',
12389                     type : 'file',
12390                     cls : 'd-none  roo-card-upload-selector'
12391                 },
12392                 
12393                 {
12394                     cls : 'roo-card-uploader-button-container w-100 mb-2'
12395                 },
12396                 {
12397                     cls : 'card-columns roo-card-uploader-container'
12398                 }
12399
12400             ]
12401         };
12402            
12403          
12404         return cfg;
12405     },
12406     
12407     getChildContainer : function() /// what children are added to.
12408     {
12409         return this.containerEl;
12410     },
12411    
12412     getButtonContainer : function() /// what children are added to.
12413     {
12414         return this.el.select(".roo-card-uploader-button-container").first();
12415     },
12416    
12417     initEvents : function()
12418     {
12419         
12420         Roo.bootstrap.Input.prototype.initEvents.call(this);
12421         
12422         var t = this;
12423         this.addxtype({
12424             xns: Roo.bootstrap,
12425
12426             xtype : 'Button',
12427             container_method : 'getButtonContainer' ,            
12428             html :  this.html, // fix changable?
12429             cls : 'w-100 ',
12430             listeners : {
12431                 'click' : function(btn, e) {
12432                     t.onClick(e);
12433                 }
12434             }
12435         });
12436         
12437         
12438         
12439         
12440         this.urlAPI = (window.createObjectURL && window) || 
12441                                 (window.URL && URL.revokeObjectURL && URL) || 
12442                                 (window.webkitURL && webkitURL);
12443                         
12444          
12445          
12446          
12447         this.selectorEl = this.el.select('.roo-card-upload-selector', true).first();
12448         
12449         this.selectorEl.on('change', this.onFileSelected, this);
12450         if (this.images) {
12451             var t = this;
12452             this.images.forEach(function(img) {
12453                 t.addCard(img)
12454             });
12455             this.images = false;
12456         }
12457         this.containerEl = this.el.select('.roo-card-uploader-container', true).first();
12458          
12459        
12460     },
12461     
12462    
12463     onClick : function(e)
12464     {
12465         e.preventDefault();
12466          
12467         this.selectorEl.dom.click();
12468          
12469     },
12470     
12471     onFileSelected : function(e)
12472     {
12473         e.preventDefault();
12474         
12475         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
12476             return;
12477         }
12478         
12479         Roo.each(this.selectorEl.dom.files, function(file){    
12480             this.addFile(file);
12481         }, this);
12482          
12483     },
12484     
12485       
12486     
12487       
12488     
12489     addFile : function(file)
12490     {
12491            
12492         if(typeof(file) === 'string'){
12493             throw "Add file by name?"; // should not happen
12494             return;
12495         }
12496         
12497         if(!file || !this.urlAPI){
12498             return;
12499         }
12500         
12501         // file;
12502         // file.type;
12503         
12504         var _this = this;
12505         
12506         
12507         var url = _this.urlAPI.createObjectURL( file);
12508            
12509         this.addCard({
12510             id : Roo.bootstrap.CardUploader.ID--,
12511             is_uploaded : false,
12512             src : url,
12513             title : file.name,
12514             mimetype : file.type,
12515             preview : false,
12516             is_deleted : 0
12517         })
12518         
12519     },
12520     
12521     addCard : function (data)
12522     {
12523         // hidden input element?
12524         // if the file is not an image...
12525         //then we need to use something other that and header_image
12526         var t = this;
12527         //   remove.....
12528         var footer = [
12529             {
12530                 xns : Roo.bootstrap,
12531                 xtype : 'CardFooter',
12532                 items: [
12533                     {
12534                         xns : Roo.bootstrap,
12535                         xtype : 'Element',
12536                         cls : 'd-flex',
12537                         items : [
12538                             
12539                             {
12540                                 xns : Roo.bootstrap,
12541                                 xtype : 'Button',
12542                                 html : String.format("<small>{0}</small>", data.title),
12543                                 cls : 'col-11 text-left',
12544                                 size: 'sm',
12545                                 weight: 'link',
12546                                 fa : 'download',
12547                                 listeners : {
12548                                     click : function() {
12549                                         this.downloadCard(data.id)
12550                                     }
12551                                 }
12552                             },
12553                           
12554                             {
12555                                 xns : Roo.bootstrap,
12556                                 xtype : 'Button',
12557                                 
12558                                 size : 'sm',
12559                                 weight: 'danger',
12560                                 cls : 'col-1',
12561                                 fa : 'times',
12562                                 listeners : {
12563                                     click : function() {
12564                                         t.removeCard(data.id)
12565                                     }
12566                                 }
12567                             }
12568                         ]
12569                     }
12570                     
12571                 ] 
12572             }
12573             
12574         ];
12575
12576         var cn = this.addxtype(
12577             {
12578                  
12579                 xns : Roo.bootstrap,
12580                 xtype : 'Card',
12581                 closeable : true,
12582                 header : !data.mimetype.match(/image/) && !data.preview ? "Document": false,
12583                 header_image : data.mimetype.match(/image/) ? data.src  : data.preview,
12584                 header_image_fit_square: true, // fixme  - we probably need to use the 'Img' element to do stuff like this.
12585                 data : data,
12586                 html : false,
12587                  
12588                 items : footer,
12589                 initEvents : function() {
12590                     Roo.bootstrap.Card.prototype.initEvents.call(this);
12591                     this.imgEl = this.el.select('.card-img-top').first();
12592                     if (this.imgEl) {
12593                         this.imgEl.on('click', function() { t.previewCard( data.id); }, this);
12594                         this.imgEl.set({ 'pointer' : 'cursor' });
12595                                   
12596                     }
12597                     
12598                   
12599                 }
12600                 
12601             }
12602         );
12603         // dont' really need ot update items.
12604         // this.items.push(cn);
12605         this.fileCollection.add(cn);
12606         this.updateInput();
12607         
12608     },
12609     removeCard : function(id)
12610     {
12611         
12612         var card  = this.fileCollection.get(id);
12613         card.data.is_deleted = 1;
12614         card.data.src = ''; /// delete the source - so it reduces size of not uploaded images etc.
12615         this.fileCollection.remove(card);
12616         //this.items = this.items.filter(function(e) { return e != card });
12617         // dont' really need ot update items.
12618         card.el.dom.parentNode.removeChild(card.el.dom);
12619         
12620     },
12621     reset: function()
12622     {
12623         this.fileCollection.each(function(card) {
12624             card.el.dom.parentNode.removeChild(card.el.dom);    
12625         });
12626         this.fileCollection.clear();
12627         this.updateInput();
12628     },
12629     
12630     updateInput : function()
12631     {
12632         var data = [];
12633         this.fileCollection.each(function(e) {
12634             data.push(e.data);
12635         });
12636         
12637         this.inputEl().dom.value = JSON.stringify(data);
12638     }
12639     
12640     
12641 });
12642
12643
12644 Roo.bootstrap.CardUploader.ID = -1;/*
12645  * Based on:
12646  * Ext JS Library 1.1.1
12647  * Copyright(c) 2006-2007, Ext JS, LLC.
12648  *
12649  * Originally Released Under LGPL - original licence link has changed is not relivant.
12650  *
12651  * Fork - LGPL
12652  * <script type="text/javascript">
12653  */
12654
12655
12656 /**
12657  * @class Roo.data.SortTypes
12658  * @singleton
12659  * Defines the default sorting (casting?) comparison functions used when sorting data.
12660  */
12661 Roo.data.SortTypes = {
12662     /**
12663      * Default sort that does nothing
12664      * @param {Mixed} s The value being converted
12665      * @return {Mixed} The comparison value
12666      */
12667     none : function(s){
12668         return s;
12669     },
12670     
12671     /**
12672      * The regular expression used to strip tags
12673      * @type {RegExp}
12674      * @property
12675      */
12676     stripTagsRE : /<\/?[^>]+>/gi,
12677     
12678     /**
12679      * Strips all HTML tags to sort on text only
12680      * @param {Mixed} s The value being converted
12681      * @return {String} The comparison value
12682      */
12683     asText : function(s){
12684         return String(s).replace(this.stripTagsRE, "");
12685     },
12686     
12687     /**
12688      * Strips all HTML tags to sort on text only - Case insensitive
12689      * @param {Mixed} s The value being converted
12690      * @return {String} The comparison value
12691      */
12692     asUCText : function(s){
12693         return String(s).toUpperCase().replace(this.stripTagsRE, "");
12694     },
12695     
12696     /**
12697      * Case insensitive string
12698      * @param {Mixed} s The value being converted
12699      * @return {String} The comparison value
12700      */
12701     asUCString : function(s) {
12702         return String(s).toUpperCase();
12703     },
12704     
12705     /**
12706      * Date sorting
12707      * @param {Mixed} s The value being converted
12708      * @return {Number} The comparison value
12709      */
12710     asDate : function(s) {
12711         if(!s){
12712             return 0;
12713         }
12714         if(s instanceof Date){
12715             return s.getTime();
12716         }
12717         return Date.parse(String(s));
12718     },
12719     
12720     /**
12721      * Float sorting
12722      * @param {Mixed} s The value being converted
12723      * @return {Float} The comparison value
12724      */
12725     asFloat : function(s) {
12726         var val = parseFloat(String(s).replace(/,/g, ""));
12727         if(isNaN(val)) {
12728             val = 0;
12729         }
12730         return val;
12731     },
12732     
12733     /**
12734      * Integer sorting
12735      * @param {Mixed} s The value being converted
12736      * @return {Number} The comparison value
12737      */
12738     asInt : function(s) {
12739         var val = parseInt(String(s).replace(/,/g, ""));
12740         if(isNaN(val)) {
12741             val = 0;
12742         }
12743         return val;
12744     }
12745 };/*
12746  * Based on:
12747  * Ext JS Library 1.1.1
12748  * Copyright(c) 2006-2007, Ext JS, LLC.
12749  *
12750  * Originally Released Under LGPL - original licence link has changed is not relivant.
12751  *
12752  * Fork - LGPL
12753  * <script type="text/javascript">
12754  */
12755
12756 /**
12757 * @class Roo.data.Record
12758  * Instances of this class encapsulate both record <em>definition</em> information, and record
12759  * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
12760  * to access Records cached in an {@link Roo.data.Store} object.<br>
12761  * <p>
12762  * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
12763  * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
12764  * objects.<br>
12765  * <p>
12766  * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
12767  * @constructor
12768  * This constructor should not be used to create Record objects. Instead, use the constructor generated by
12769  * {@link #create}. The parameters are the same.
12770  * @param {Array} data An associative Array of data values keyed by the field name.
12771  * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
12772  * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
12773  * not specified an integer id is generated.
12774  */
12775 Roo.data.Record = function(data, id){
12776     this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
12777     this.data = data;
12778 };
12779
12780 /**
12781  * Generate a constructor for a specific record layout.
12782  * @param {Array} o An Array of field definition objects which specify field names, and optionally,
12783  * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
12784  * Each field definition object may contain the following properties: <ul>
12785  * <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,
12786  * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
12787  * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
12788  * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
12789  * is being used, then this is a string containing the javascript expression to reference the data relative to 
12790  * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
12791  * to the data item relative to the record element. If the mapping expression is the same as the field name,
12792  * this may be omitted.</p></li>
12793  * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
12794  * <ul><li>auto (Default, implies no conversion)</li>
12795  * <li>string</li>
12796  * <li>int</li>
12797  * <li>float</li>
12798  * <li>boolean</li>
12799  * <li>date</li></ul></p></li>
12800  * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
12801  * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
12802  * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
12803  * by the Reader into an object that will be stored in the Record. It is passed the
12804  * following parameters:<ul>
12805  * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
12806  * </ul></p></li>
12807  * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
12808  * </ul>
12809  * <br>usage:<br><pre><code>
12810 var TopicRecord = Roo.data.Record.create(
12811     {name: 'title', mapping: 'topic_title'},
12812     {name: 'author', mapping: 'username'},
12813     {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
12814     {name: 'lastPost', mapping: 'post_time', type: 'date'},
12815     {name: 'lastPoster', mapping: 'user2'},
12816     {name: 'excerpt', mapping: 'post_text'}
12817 );
12818
12819 var myNewRecord = new TopicRecord({
12820     title: 'Do my job please',
12821     author: 'noobie',
12822     totalPosts: 1,
12823     lastPost: new Date(),
12824     lastPoster: 'Animal',
12825     excerpt: 'No way dude!'
12826 });
12827 myStore.add(myNewRecord);
12828 </code></pre>
12829  * @method create
12830  * @static
12831  */
12832 Roo.data.Record.create = function(o){
12833     var f = function(){
12834         f.superclass.constructor.apply(this, arguments);
12835     };
12836     Roo.extend(f, Roo.data.Record);
12837     var p = f.prototype;
12838     p.fields = new Roo.util.MixedCollection(false, function(field){
12839         return field.name;
12840     });
12841     for(var i = 0, len = o.length; i < len; i++){
12842         p.fields.add(new Roo.data.Field(o[i]));
12843     }
12844     f.getField = function(name){
12845         return p.fields.get(name);  
12846     };
12847     return f;
12848 };
12849
12850 Roo.data.Record.AUTO_ID = 1000;
12851 Roo.data.Record.EDIT = 'edit';
12852 Roo.data.Record.REJECT = 'reject';
12853 Roo.data.Record.COMMIT = 'commit';
12854
12855 Roo.data.Record.prototype = {
12856     /**
12857      * Readonly flag - true if this record has been modified.
12858      * @type Boolean
12859      */
12860     dirty : false,
12861     editing : false,
12862     error: null,
12863     modified: null,
12864
12865     // private
12866     join : function(store){
12867         this.store = store;
12868     },
12869
12870     /**
12871      * Set the named field to the specified value.
12872      * @param {String} name The name of the field to set.
12873      * @param {Object} value The value to set the field to.
12874      */
12875     set : function(name, value){
12876         if(this.data[name] == value){
12877             return;
12878         }
12879         this.dirty = true;
12880         if(!this.modified){
12881             this.modified = {};
12882         }
12883         if(typeof this.modified[name] == 'undefined'){
12884             this.modified[name] = this.data[name];
12885         }
12886         this.data[name] = value;
12887         if(!this.editing && this.store){
12888             this.store.afterEdit(this);
12889         }       
12890     },
12891
12892     /**
12893      * Get the value of the named field.
12894      * @param {String} name The name of the field to get the value of.
12895      * @return {Object} The value of the field.
12896      */
12897     get : function(name){
12898         return this.data[name]; 
12899     },
12900
12901     // private
12902     beginEdit : function(){
12903         this.editing = true;
12904         this.modified = {}; 
12905     },
12906
12907     // private
12908     cancelEdit : function(){
12909         this.editing = false;
12910         delete this.modified;
12911     },
12912
12913     // private
12914     endEdit : function(){
12915         this.editing = false;
12916         if(this.dirty && this.store){
12917             this.store.afterEdit(this);
12918         }
12919     },
12920
12921     /**
12922      * Usually called by the {@link Roo.data.Store} which owns the Record.
12923      * Rejects all changes made to the Record since either creation, or the last commit operation.
12924      * Modified fields are reverted to their original values.
12925      * <p>
12926      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
12927      * of reject operations.
12928      */
12929     reject : function(){
12930         var m = this.modified;
12931         for(var n in m){
12932             if(typeof m[n] != "function"){
12933                 this.data[n] = m[n];
12934             }
12935         }
12936         this.dirty = false;
12937         delete this.modified;
12938         this.editing = false;
12939         if(this.store){
12940             this.store.afterReject(this);
12941         }
12942     },
12943
12944     /**
12945      * Usually called by the {@link Roo.data.Store} which owns the Record.
12946      * Commits all changes made to the Record since either creation, or the last commit operation.
12947      * <p>
12948      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
12949      * of commit operations.
12950      */
12951     commit : function(){
12952         this.dirty = false;
12953         delete this.modified;
12954         this.editing = false;
12955         if(this.store){
12956             this.store.afterCommit(this);
12957         }
12958     },
12959
12960     // private
12961     hasError : function(){
12962         return this.error != null;
12963     },
12964
12965     // private
12966     clearError : function(){
12967         this.error = null;
12968     },
12969
12970     /**
12971      * Creates a copy of this record.
12972      * @param {String} id (optional) A new record id if you don't want to use this record's id
12973      * @return {Record}
12974      */
12975     copy : function(newId) {
12976         return new this.constructor(Roo.apply({}, this.data), newId || this.id);
12977     }
12978 };/*
12979  * Based on:
12980  * Ext JS Library 1.1.1
12981  * Copyright(c) 2006-2007, Ext JS, LLC.
12982  *
12983  * Originally Released Under LGPL - original licence link has changed is not relivant.
12984  *
12985  * Fork - LGPL
12986  * <script type="text/javascript">
12987  */
12988
12989
12990
12991 /**
12992  * @class Roo.data.Store
12993  * @extends Roo.util.Observable
12994  * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
12995  * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
12996  * <p>
12997  * 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
12998  * has no knowledge of the format of the data returned by the Proxy.<br>
12999  * <p>
13000  * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
13001  * instances from the data object. These records are cached and made available through accessor functions.
13002  * @constructor
13003  * Creates a new Store.
13004  * @param {Object} config A config object containing the objects needed for the Store to access data,
13005  * and read the data into Records.
13006  */
13007 Roo.data.Store = function(config){
13008     this.data = new Roo.util.MixedCollection(false);
13009     this.data.getKey = function(o){
13010         return o.id;
13011     };
13012     this.baseParams = {};
13013     // private
13014     this.paramNames = {
13015         "start" : "start",
13016         "limit" : "limit",
13017         "sort" : "sort",
13018         "dir" : "dir",
13019         "multisort" : "_multisort"
13020     };
13021
13022     if(config && config.data){
13023         this.inlineData = config.data;
13024         delete config.data;
13025     }
13026
13027     Roo.apply(this, config);
13028     
13029     if(this.reader){ // reader passed
13030         this.reader = Roo.factory(this.reader, Roo.data);
13031         this.reader.xmodule = this.xmodule || false;
13032         if(!this.recordType){
13033             this.recordType = this.reader.recordType;
13034         }
13035         if(this.reader.onMetaChange){
13036             this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
13037         }
13038     }
13039
13040     if(this.recordType){
13041         this.fields = this.recordType.prototype.fields;
13042     }
13043     this.modified = [];
13044
13045     this.addEvents({
13046         /**
13047          * @event datachanged
13048          * Fires when the data cache has changed, and a widget which is using this Store
13049          * as a Record cache should refresh its view.
13050          * @param {Store} this
13051          */
13052         datachanged : true,
13053         /**
13054          * @event metachange
13055          * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
13056          * @param {Store} this
13057          * @param {Object} meta The JSON metadata
13058          */
13059         metachange : true,
13060         /**
13061          * @event add
13062          * Fires when Records have been added to the Store
13063          * @param {Store} this
13064          * @param {Roo.data.Record[]} records The array of Records added
13065          * @param {Number} index The index at which the record(s) were added
13066          */
13067         add : true,
13068         /**
13069          * @event remove
13070          * Fires when a Record has been removed from the Store
13071          * @param {Store} this
13072          * @param {Roo.data.Record} record The Record that was removed
13073          * @param {Number} index The index at which the record was removed
13074          */
13075         remove : true,
13076         /**
13077          * @event update
13078          * Fires when a Record has been updated
13079          * @param {Store} this
13080          * @param {Roo.data.Record} record The Record that was updated
13081          * @param {String} operation The update operation being performed.  Value may be one of:
13082          * <pre><code>
13083  Roo.data.Record.EDIT
13084  Roo.data.Record.REJECT
13085  Roo.data.Record.COMMIT
13086          * </code></pre>
13087          */
13088         update : true,
13089         /**
13090          * @event clear
13091          * Fires when the data cache has been cleared.
13092          * @param {Store} this
13093          */
13094         clear : true,
13095         /**
13096          * @event beforeload
13097          * Fires before a request is made for a new data object.  If the beforeload handler returns false
13098          * the load action will be canceled.
13099          * @param {Store} this
13100          * @param {Object} options The loading options that were specified (see {@link #load} for details)
13101          */
13102         beforeload : true,
13103         /**
13104          * @event beforeloadadd
13105          * Fires after a new set of Records has been loaded.
13106          * @param {Store} this
13107          * @param {Roo.data.Record[]} records The Records that were loaded
13108          * @param {Object} options The loading options that were specified (see {@link #load} for details)
13109          */
13110         beforeloadadd : true,
13111         /**
13112          * @event load
13113          * Fires after a new set of Records has been loaded, before they are added to the store.
13114          * @param {Store} this
13115          * @param {Roo.data.Record[]} records The Records that were loaded
13116          * @param {Object} options The loading options that were specified (see {@link #load} for details)
13117          * @params {Object} return from reader
13118          */
13119         load : true,
13120         /**
13121          * @event loadexception
13122          * Fires if an exception occurs in the Proxy during loading.
13123          * Called with the signature of the Proxy's "loadexception" event.
13124          * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
13125          * 
13126          * @param {Proxy} 
13127          * @param {Object} return from JsonData.reader() - success, totalRecords, records
13128          * @param {Object} load options 
13129          * @param {Object} jsonData from your request (normally this contains the Exception)
13130          */
13131         loadexception : true
13132     });
13133     
13134     if(this.proxy){
13135         this.proxy = Roo.factory(this.proxy, Roo.data);
13136         this.proxy.xmodule = this.xmodule || false;
13137         this.relayEvents(this.proxy,  ["loadexception"]);
13138     }
13139     this.sortToggle = {};
13140     this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
13141
13142     Roo.data.Store.superclass.constructor.call(this);
13143
13144     if(this.inlineData){
13145         this.loadData(this.inlineData);
13146         delete this.inlineData;
13147     }
13148 };
13149
13150 Roo.extend(Roo.data.Store, Roo.util.Observable, {
13151      /**
13152     * @cfg {boolean} isLocal   flag if data is locally available (and can be always looked up
13153     * without a remote query - used by combo/forms at present.
13154     */
13155     
13156     /**
13157     * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
13158     */
13159     /**
13160     * @cfg {Array} data Inline data to be loaded when the store is initialized.
13161     */
13162     /**
13163     * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
13164     * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
13165     */
13166     /**
13167     * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
13168     * on any HTTP request
13169     */
13170     /**
13171     * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
13172     */
13173     /**
13174     * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
13175     */
13176     multiSort: false,
13177     /**
13178     * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
13179     * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
13180     */
13181     remoteSort : false,
13182
13183     /**
13184     * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
13185      * loaded or when a record is removed. (defaults to false).
13186     */
13187     pruneModifiedRecords : false,
13188
13189     // private
13190     lastOptions : null,
13191
13192     /**
13193      * Add Records to the Store and fires the add event.
13194      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
13195      */
13196     add : function(records){
13197         records = [].concat(records);
13198         for(var i = 0, len = records.length; i < len; i++){
13199             records[i].join(this);
13200         }
13201         var index = this.data.length;
13202         this.data.addAll(records);
13203         this.fireEvent("add", this, records, index);
13204     },
13205
13206     /**
13207      * Remove a Record from the Store and fires the remove event.
13208      * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
13209      */
13210     remove : function(record){
13211         var index = this.data.indexOf(record);
13212         this.data.removeAt(index);
13213  
13214         if(this.pruneModifiedRecords){
13215             this.modified.remove(record);
13216         }
13217         this.fireEvent("remove", this, record, index);
13218     },
13219
13220     /**
13221      * Remove all Records from the Store and fires the clear event.
13222      */
13223     removeAll : function(){
13224         this.data.clear();
13225         if(this.pruneModifiedRecords){
13226             this.modified = [];
13227         }
13228         this.fireEvent("clear", this);
13229     },
13230
13231     /**
13232      * Inserts Records to the Store at the given index and fires the add event.
13233      * @param {Number} index The start index at which to insert the passed Records.
13234      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
13235      */
13236     insert : function(index, records){
13237         records = [].concat(records);
13238         for(var i = 0, len = records.length; i < len; i++){
13239             this.data.insert(index, records[i]);
13240             records[i].join(this);
13241         }
13242         this.fireEvent("add", this, records, index);
13243     },
13244
13245     /**
13246      * Get the index within the cache of the passed Record.
13247      * @param {Roo.data.Record} record The Roo.data.Record object to to find.
13248      * @return {Number} The index of the passed Record. Returns -1 if not found.
13249      */
13250     indexOf : function(record){
13251         return this.data.indexOf(record);
13252     },
13253
13254     /**
13255      * Get the index within the cache of the Record with the passed id.
13256      * @param {String} id The id of the Record to find.
13257      * @return {Number} The index of the Record. Returns -1 if not found.
13258      */
13259     indexOfId : function(id){
13260         return this.data.indexOfKey(id);
13261     },
13262
13263     /**
13264      * Get the Record with the specified id.
13265      * @param {String} id The id of the Record to find.
13266      * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
13267      */
13268     getById : function(id){
13269         return this.data.key(id);
13270     },
13271
13272     /**
13273      * Get the Record at the specified index.
13274      * @param {Number} index The index of the Record to find.
13275      * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
13276      */
13277     getAt : function(index){
13278         return this.data.itemAt(index);
13279     },
13280
13281     /**
13282      * Returns a range of Records between specified indices.
13283      * @param {Number} startIndex (optional) The starting index (defaults to 0)
13284      * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
13285      * @return {Roo.data.Record[]} An array of Records
13286      */
13287     getRange : function(start, end){
13288         return this.data.getRange(start, end);
13289     },
13290
13291     // private
13292     storeOptions : function(o){
13293         o = Roo.apply({}, o);
13294         delete o.callback;
13295         delete o.scope;
13296         this.lastOptions = o;
13297     },
13298
13299     /**
13300      * Loads the Record cache from the configured Proxy using the configured Reader.
13301      * <p>
13302      * If using remote paging, then the first load call must specify the <em>start</em>
13303      * and <em>limit</em> properties in the options.params property to establish the initial
13304      * position within the dataset, and the number of Records to cache on each read from the Proxy.
13305      * <p>
13306      * <strong>It is important to note that for remote data sources, loading is asynchronous,
13307      * and this call will return before the new data has been loaded. Perform any post-processing
13308      * in a callback function, or in a "load" event handler.</strong>
13309      * <p>
13310      * @param {Object} options An object containing properties which control loading options:<ul>
13311      * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
13312      * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
13313      * passed the following arguments:<ul>
13314      * <li>r : Roo.data.Record[]</li>
13315      * <li>options: Options object from the load call</li>
13316      * <li>success: Boolean success indicator</li></ul></li>
13317      * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
13318      * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
13319      * </ul>
13320      */
13321     load : function(options){
13322         options = options || {};
13323         if(this.fireEvent("beforeload", this, options) !== false){
13324             this.storeOptions(options);
13325             var p = Roo.apply(options.params || {}, this.baseParams);
13326             // if meta was not loaded from remote source.. try requesting it.
13327             if (!this.reader.metaFromRemote) {
13328                 p._requestMeta = 1;
13329             }
13330             if(this.sortInfo && this.remoteSort){
13331                 var pn = this.paramNames;
13332                 p[pn["sort"]] = this.sortInfo.field;
13333                 p[pn["dir"]] = this.sortInfo.direction;
13334             }
13335             if (this.multiSort) {
13336                 var pn = this.paramNames;
13337                 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
13338             }
13339             
13340             this.proxy.load(p, this.reader, this.loadRecords, this, options);
13341         }
13342     },
13343
13344     /**
13345      * Reloads the Record cache from the configured Proxy using the configured Reader and
13346      * the options from the last load operation performed.
13347      * @param {Object} options (optional) An object containing properties which may override the options
13348      * used in the last load operation. See {@link #load} for details (defaults to null, in which case
13349      * the most recently used options are reused).
13350      */
13351     reload : function(options){
13352         this.load(Roo.applyIf(options||{}, this.lastOptions));
13353     },
13354
13355     // private
13356     // Called as a callback by the Reader during a load operation.
13357     loadRecords : function(o, options, success){
13358         if(!o || success === false){
13359             if(success !== false){
13360                 this.fireEvent("load", this, [], options, o);
13361             }
13362             if(options.callback){
13363                 options.callback.call(options.scope || this, [], options, false);
13364             }
13365             return;
13366         }
13367         // if data returned failure - throw an exception.
13368         if (o.success === false) {
13369             // show a message if no listener is registered.
13370             if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
13371                     Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
13372             }
13373             // loadmask wil be hooked into this..
13374             this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
13375             return;
13376         }
13377         var r = o.records, t = o.totalRecords || r.length;
13378         
13379         this.fireEvent("beforeloadadd", this, r, options, o);
13380         
13381         if(!options || options.add !== true){
13382             if(this.pruneModifiedRecords){
13383                 this.modified = [];
13384             }
13385             for(var i = 0, len = r.length; i < len; i++){
13386                 r[i].join(this);
13387             }
13388             if(this.snapshot){
13389                 this.data = this.snapshot;
13390                 delete this.snapshot;
13391             }
13392             this.data.clear();
13393             this.data.addAll(r);
13394             this.totalLength = t;
13395             this.applySort();
13396             this.fireEvent("datachanged", this);
13397         }else{
13398             this.totalLength = Math.max(t, this.data.length+r.length);
13399             this.add(r);
13400         }
13401         
13402         if(this.parent && !Roo.isIOS && !this.useNativeIOS && this.parent.emptyTitle.length) {
13403                 
13404             var e = new Roo.data.Record({});
13405
13406             e.set(this.parent.displayField, this.parent.emptyTitle);
13407             e.set(this.parent.valueField, '');
13408
13409             this.insert(0, e);
13410         }
13411             
13412         this.fireEvent("load", this, r, options, o);
13413         if(options.callback){
13414             options.callback.call(options.scope || this, r, options, true);
13415         }
13416     },
13417
13418
13419     /**
13420      * Loads data from a passed data block. A Reader which understands the format of the data
13421      * must have been configured in the constructor.
13422      * @param {Object} data The data block from which to read the Records.  The format of the data expected
13423      * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
13424      * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
13425      */
13426     loadData : function(o, append){
13427         var r = this.reader.readRecords(o);
13428         this.loadRecords(r, {add: append}, true);
13429     },
13430     
13431      /**
13432      * using 'cn' the nested child reader read the child array into it's child stores.
13433      * @param {Object} rec The record with a 'children array
13434      */
13435     loadDataFromChildren : function(rec)
13436     {
13437         this.loadData(this.reader.toLoadData(rec));
13438     },
13439     
13440
13441     /**
13442      * Gets the number of cached records.
13443      * <p>
13444      * <em>If using paging, this may not be the total size of the dataset. If the data object
13445      * used by the Reader contains the dataset size, then the getTotalCount() function returns
13446      * the data set size</em>
13447      */
13448     getCount : function(){
13449         return this.data.length || 0;
13450     },
13451
13452     /**
13453      * Gets the total number of records in the dataset as returned by the server.
13454      * <p>
13455      * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
13456      * the dataset size</em>
13457      */
13458     getTotalCount : function(){
13459         return this.totalLength || 0;
13460     },
13461
13462     /**
13463      * Returns the sort state of the Store as an object with two properties:
13464      * <pre><code>
13465  field {String} The name of the field by which the Records are sorted
13466  direction {String} The sort order, "ASC" or "DESC"
13467      * </code></pre>
13468      */
13469     getSortState : function(){
13470         return this.sortInfo;
13471     },
13472
13473     // private
13474     applySort : function(){
13475         if(this.sortInfo && !this.remoteSort){
13476             var s = this.sortInfo, f = s.field;
13477             var st = this.fields.get(f).sortType;
13478             var fn = function(r1, r2){
13479                 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
13480                 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
13481             };
13482             this.data.sort(s.direction, fn);
13483             if(this.snapshot && this.snapshot != this.data){
13484                 this.snapshot.sort(s.direction, fn);
13485             }
13486         }
13487     },
13488
13489     /**
13490      * Sets the default sort column and order to be used by the next load operation.
13491      * @param {String} fieldName The name of the field to sort by.
13492      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
13493      */
13494     setDefaultSort : function(field, dir){
13495         this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
13496     },
13497
13498     /**
13499      * Sort the Records.
13500      * If remote sorting is used, the sort is performed on the server, and the cache is
13501      * reloaded. If local sorting is used, the cache is sorted internally.
13502      * @param {String} fieldName The name of the field to sort by.
13503      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
13504      */
13505     sort : function(fieldName, dir){
13506         var f = this.fields.get(fieldName);
13507         if(!dir){
13508             this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
13509             
13510             if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
13511                 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
13512             }else{
13513                 dir = f.sortDir;
13514             }
13515         }
13516         this.sortToggle[f.name] = dir;
13517         this.sortInfo = {field: f.name, direction: dir};
13518         if(!this.remoteSort){
13519             this.applySort();
13520             this.fireEvent("datachanged", this);
13521         }else{
13522             this.load(this.lastOptions);
13523         }
13524     },
13525
13526     /**
13527      * Calls the specified function for each of the Records in the cache.
13528      * @param {Function} fn The function to call. The Record is passed as the first parameter.
13529      * Returning <em>false</em> aborts and exits the iteration.
13530      * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
13531      */
13532     each : function(fn, scope){
13533         this.data.each(fn, scope);
13534     },
13535
13536     /**
13537      * Gets all records modified since the last commit.  Modified records are persisted across load operations
13538      * (e.g., during paging).
13539      * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
13540      */
13541     getModifiedRecords : function(){
13542         return this.modified;
13543     },
13544
13545     // private
13546     createFilterFn : function(property, value, anyMatch){
13547         if(!value.exec){ // not a regex
13548             value = String(value);
13549             if(value.length == 0){
13550                 return false;
13551             }
13552             value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
13553         }
13554         return function(r){
13555             return value.test(r.data[property]);
13556         };
13557     },
13558
13559     /**
13560      * Sums the value of <i>property</i> for each record between start and end and returns the result.
13561      * @param {String} property A field on your records
13562      * @param {Number} start The record index to start at (defaults to 0)
13563      * @param {Number} end The last record index to include (defaults to length - 1)
13564      * @return {Number} The sum
13565      */
13566     sum : function(property, start, end){
13567         var rs = this.data.items, v = 0;
13568         start = start || 0;
13569         end = (end || end === 0) ? end : rs.length-1;
13570
13571         for(var i = start; i <= end; i++){
13572             v += (rs[i].data[property] || 0);
13573         }
13574         return v;
13575     },
13576
13577     /**
13578      * Filter the records by a specified property.
13579      * @param {String} field A field on your records
13580      * @param {String/RegExp} value Either a string that the field
13581      * should start with or a RegExp to test against the field
13582      * @param {Boolean} anyMatch True to match any part not just the beginning
13583      */
13584     filter : function(property, value, anyMatch){
13585         var fn = this.createFilterFn(property, value, anyMatch);
13586         return fn ? this.filterBy(fn) : this.clearFilter();
13587     },
13588
13589     /**
13590      * Filter by a function. The specified function will be called with each
13591      * record in this data source. If the function returns true the record is included,
13592      * otherwise it is filtered.
13593      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
13594      * @param {Object} scope (optional) The scope of the function (defaults to this)
13595      */
13596     filterBy : function(fn, scope){
13597         this.snapshot = this.snapshot || this.data;
13598         this.data = this.queryBy(fn, scope||this);
13599         this.fireEvent("datachanged", this);
13600     },
13601
13602     /**
13603      * Query the records by a specified property.
13604      * @param {String} field A field on your records
13605      * @param {String/RegExp} value Either a string that the field
13606      * should start with or a RegExp to test against the field
13607      * @param {Boolean} anyMatch True to match any part not just the beginning
13608      * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
13609      */
13610     query : function(property, value, anyMatch){
13611         var fn = this.createFilterFn(property, value, anyMatch);
13612         return fn ? this.queryBy(fn) : this.data.clone();
13613     },
13614
13615     /**
13616      * Query by a function. The specified function will be called with each
13617      * record in this data source. If the function returns true the record is included
13618      * in the results.
13619      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
13620      * @param {Object} scope (optional) The scope of the function (defaults to this)
13621       @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
13622      **/
13623     queryBy : function(fn, scope){
13624         var data = this.snapshot || this.data;
13625         return data.filterBy(fn, scope||this);
13626     },
13627
13628     /**
13629      * Collects unique values for a particular dataIndex from this store.
13630      * @param {String} dataIndex The property to collect
13631      * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
13632      * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
13633      * @return {Array} An array of the unique values
13634      **/
13635     collect : function(dataIndex, allowNull, bypassFilter){
13636         var d = (bypassFilter === true && this.snapshot) ?
13637                 this.snapshot.items : this.data.items;
13638         var v, sv, r = [], l = {};
13639         for(var i = 0, len = d.length; i < len; i++){
13640             v = d[i].data[dataIndex];
13641             sv = String(v);
13642             if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
13643                 l[sv] = true;
13644                 r[r.length] = v;
13645             }
13646         }
13647         return r;
13648     },
13649
13650     /**
13651      * Revert to a view of the Record cache with no filtering applied.
13652      * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
13653      */
13654     clearFilter : function(suppressEvent){
13655         if(this.snapshot && this.snapshot != this.data){
13656             this.data = this.snapshot;
13657             delete this.snapshot;
13658             if(suppressEvent !== true){
13659                 this.fireEvent("datachanged", this);
13660             }
13661         }
13662     },
13663
13664     // private
13665     afterEdit : function(record){
13666         if(this.modified.indexOf(record) == -1){
13667             this.modified.push(record);
13668         }
13669         this.fireEvent("update", this, record, Roo.data.Record.EDIT);
13670     },
13671     
13672     // private
13673     afterReject : function(record){
13674         this.modified.remove(record);
13675         this.fireEvent("update", this, record, Roo.data.Record.REJECT);
13676     },
13677
13678     // private
13679     afterCommit : function(record){
13680         this.modified.remove(record);
13681         this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
13682     },
13683
13684     /**
13685      * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
13686      * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
13687      */
13688     commitChanges : function(){
13689         var m = this.modified.slice(0);
13690         this.modified = [];
13691         for(var i = 0, len = m.length; i < len; i++){
13692             m[i].commit();
13693         }
13694     },
13695
13696     /**
13697      * Cancel outstanding changes on all changed records.
13698      */
13699     rejectChanges : function(){
13700         var m = this.modified.slice(0);
13701         this.modified = [];
13702         for(var i = 0, len = m.length; i < len; i++){
13703             m[i].reject();
13704         }
13705     },
13706
13707     onMetaChange : function(meta, rtype, o){
13708         this.recordType = rtype;
13709         this.fields = rtype.prototype.fields;
13710         delete this.snapshot;
13711         this.sortInfo = meta.sortInfo || this.sortInfo;
13712         this.modified = [];
13713         this.fireEvent('metachange', this, this.reader.meta);
13714     },
13715     
13716     moveIndex : function(data, type)
13717     {
13718         var index = this.indexOf(data);
13719         
13720         var newIndex = index + type;
13721         
13722         this.remove(data);
13723         
13724         this.insert(newIndex, data);
13725         
13726     }
13727 });/*
13728  * Based on:
13729  * Ext JS Library 1.1.1
13730  * Copyright(c) 2006-2007, Ext JS, LLC.
13731  *
13732  * Originally Released Under LGPL - original licence link has changed is not relivant.
13733  *
13734  * Fork - LGPL
13735  * <script type="text/javascript">
13736  */
13737
13738 /**
13739  * @class Roo.data.SimpleStore
13740  * @extends Roo.data.Store
13741  * Small helper class to make creating Stores from Array data easier.
13742  * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
13743  * @cfg {Array} fields An array of field definition objects, or field name strings.
13744  * @cfg {Object} an existing reader (eg. copied from another store)
13745  * @cfg {Array} data The multi-dimensional array of data
13746  * @constructor
13747  * @param {Object} config
13748  */
13749 Roo.data.SimpleStore = function(config)
13750 {
13751     Roo.data.SimpleStore.superclass.constructor.call(this, {
13752         isLocal : true,
13753         reader: typeof(config.reader) != 'undefined' ? config.reader : new Roo.data.ArrayReader({
13754                 id: config.id
13755             },
13756             Roo.data.Record.create(config.fields)
13757         ),
13758         proxy : new Roo.data.MemoryProxy(config.data)
13759     });
13760     this.load();
13761 };
13762 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
13763  * Based on:
13764  * Ext JS Library 1.1.1
13765  * Copyright(c) 2006-2007, Ext JS, LLC.
13766  *
13767  * Originally Released Under LGPL - original licence link has changed is not relivant.
13768  *
13769  * Fork - LGPL
13770  * <script type="text/javascript">
13771  */
13772
13773 /**
13774 /**
13775  * @extends Roo.data.Store
13776  * @class Roo.data.JsonStore
13777  * Small helper class to make creating Stores for JSON data easier. <br/>
13778 <pre><code>
13779 var store = new Roo.data.JsonStore({
13780     url: 'get-images.php',
13781     root: 'images',
13782     fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
13783 });
13784 </code></pre>
13785  * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
13786  * JsonReader and HttpProxy (unless inline data is provided).</b>
13787  * @cfg {Array} fields An array of field definition objects, or field name strings.
13788  * @constructor
13789  * @param {Object} config
13790  */
13791 Roo.data.JsonStore = function(c){
13792     Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
13793         proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
13794         reader: new Roo.data.JsonReader(c, c.fields)
13795     }));
13796 };
13797 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
13798  * Based on:
13799  * Ext JS Library 1.1.1
13800  * Copyright(c) 2006-2007, Ext JS, LLC.
13801  *
13802  * Originally Released Under LGPL - original licence link has changed is not relivant.
13803  *
13804  * Fork - LGPL
13805  * <script type="text/javascript">
13806  */
13807
13808  
13809 Roo.data.Field = function(config){
13810     if(typeof config == "string"){
13811         config = {name: config};
13812     }
13813     Roo.apply(this, config);
13814     
13815     if(!this.type){
13816         this.type = "auto";
13817     }
13818     
13819     var st = Roo.data.SortTypes;
13820     // named sortTypes are supported, here we look them up
13821     if(typeof this.sortType == "string"){
13822         this.sortType = st[this.sortType];
13823     }
13824     
13825     // set default sortType for strings and dates
13826     if(!this.sortType){
13827         switch(this.type){
13828             case "string":
13829                 this.sortType = st.asUCString;
13830                 break;
13831             case "date":
13832                 this.sortType = st.asDate;
13833                 break;
13834             default:
13835                 this.sortType = st.none;
13836         }
13837     }
13838
13839     // define once
13840     var stripRe = /[\$,%]/g;
13841
13842     // prebuilt conversion function for this field, instead of
13843     // switching every time we're reading a value
13844     if(!this.convert){
13845         var cv, dateFormat = this.dateFormat;
13846         switch(this.type){
13847             case "":
13848             case "auto":
13849             case undefined:
13850                 cv = function(v){ return v; };
13851                 break;
13852             case "string":
13853                 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
13854                 break;
13855             case "int":
13856                 cv = function(v){
13857                     return v !== undefined && v !== null && v !== '' ?
13858                            parseInt(String(v).replace(stripRe, ""), 10) : '';
13859                     };
13860                 break;
13861             case "float":
13862                 cv = function(v){
13863                     return v !== undefined && v !== null && v !== '' ?
13864                            parseFloat(String(v).replace(stripRe, ""), 10) : ''; 
13865                     };
13866                 break;
13867             case "bool":
13868             case "boolean":
13869                 cv = function(v){ return v === true || v === "true" || v == 1; };
13870                 break;
13871             case "date":
13872                 cv = function(v){
13873                     if(!v){
13874                         return '';
13875                     }
13876                     if(v instanceof Date){
13877                         return v;
13878                     }
13879                     if(dateFormat){
13880                         if(dateFormat == "timestamp"){
13881                             return new Date(v*1000);
13882                         }
13883                         return Date.parseDate(v, dateFormat);
13884                     }
13885                     var parsed = Date.parse(v);
13886                     return parsed ? new Date(parsed) : null;
13887                 };
13888              break;
13889             
13890         }
13891         this.convert = cv;
13892     }
13893 };
13894
13895 Roo.data.Field.prototype = {
13896     dateFormat: null,
13897     defaultValue: "",
13898     mapping: null,
13899     sortType : null,
13900     sortDir : "ASC"
13901 };/*
13902  * Based on:
13903  * Ext JS Library 1.1.1
13904  * Copyright(c) 2006-2007, Ext JS, LLC.
13905  *
13906  * Originally Released Under LGPL - original licence link has changed is not relivant.
13907  *
13908  * Fork - LGPL
13909  * <script type="text/javascript">
13910  */
13911  
13912 // Base class for reading structured data from a data source.  This class is intended to be
13913 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
13914
13915 /**
13916  * @class Roo.data.DataReader
13917  * Base class for reading structured data from a data source.  This class is intended to be
13918  * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
13919  */
13920
13921 Roo.data.DataReader = function(meta, recordType){
13922     
13923     this.meta = meta;
13924     
13925     this.recordType = recordType instanceof Array ? 
13926         Roo.data.Record.create(recordType) : recordType;
13927 };
13928
13929 Roo.data.DataReader.prototype = {
13930     
13931     
13932     readerType : 'Data',
13933      /**
13934      * Create an empty record
13935      * @param {Object} data (optional) - overlay some values
13936      * @return {Roo.data.Record} record created.
13937      */
13938     newRow :  function(d) {
13939         var da =  {};
13940         this.recordType.prototype.fields.each(function(c) {
13941             switch( c.type) {
13942                 case 'int' : da[c.name] = 0; break;
13943                 case 'date' : da[c.name] = new Date(); break;
13944                 case 'float' : da[c.name] = 0.0; break;
13945                 case 'boolean' : da[c.name] = false; break;
13946                 default : da[c.name] = ""; break;
13947             }
13948             
13949         });
13950         return new this.recordType(Roo.apply(da, d));
13951     }
13952     
13953     
13954 };/*
13955  * Based on:
13956  * Ext JS Library 1.1.1
13957  * Copyright(c) 2006-2007, Ext JS, LLC.
13958  *
13959  * Originally Released Under LGPL - original licence link has changed is not relivant.
13960  *
13961  * Fork - LGPL
13962  * <script type="text/javascript">
13963  */
13964
13965 /**
13966  * @class Roo.data.DataProxy
13967  * @extends Roo.data.Observable
13968  * This class is an abstract base class for implementations which provide retrieval of
13969  * unformatted data objects.<br>
13970  * <p>
13971  * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
13972  * (of the appropriate type which knows how to parse the data object) to provide a block of
13973  * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
13974  * <p>
13975  * Custom implementations must implement the load method as described in
13976  * {@link Roo.data.HttpProxy#load}.
13977  */
13978 Roo.data.DataProxy = function(){
13979     this.addEvents({
13980         /**
13981          * @event beforeload
13982          * Fires before a network request is made to retrieve a data object.
13983          * @param {Object} This DataProxy object.
13984          * @param {Object} params The params parameter to the load function.
13985          */
13986         beforeload : true,
13987         /**
13988          * @event load
13989          * Fires before the load method's callback is called.
13990          * @param {Object} This DataProxy object.
13991          * @param {Object} o The data object.
13992          * @param {Object} arg The callback argument object passed to the load function.
13993          */
13994         load : true,
13995         /**
13996          * @event loadexception
13997          * Fires if an Exception occurs during data retrieval.
13998          * @param {Object} This DataProxy object.
13999          * @param {Object} o The data object.
14000          * @param {Object} arg The callback argument object passed to the load function.
14001          * @param {Object} e The Exception.
14002          */
14003         loadexception : true
14004     });
14005     Roo.data.DataProxy.superclass.constructor.call(this);
14006 };
14007
14008 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
14009
14010     /**
14011      * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
14012      */
14013 /*
14014  * Based on:
14015  * Ext JS Library 1.1.1
14016  * Copyright(c) 2006-2007, Ext JS, LLC.
14017  *
14018  * Originally Released Under LGPL - original licence link has changed is not relivant.
14019  *
14020  * Fork - LGPL
14021  * <script type="text/javascript">
14022  */
14023 /**
14024  * @class Roo.data.MemoryProxy
14025  * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
14026  * to the Reader when its load method is called.
14027  * @constructor
14028  * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
14029  */
14030 Roo.data.MemoryProxy = function(data){
14031     if (data.data) {
14032         data = data.data;
14033     }
14034     Roo.data.MemoryProxy.superclass.constructor.call(this);
14035     this.data = data;
14036 };
14037
14038 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
14039     
14040     /**
14041      * Load data from the requested source (in this case an in-memory
14042      * data object passed to the constructor), read the data object into
14043      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
14044      * process that block using the passed callback.
14045      * @param {Object} params This parameter is not used by the MemoryProxy class.
14046      * @param {Roo.data.DataReader} reader The Reader object which converts the data
14047      * object into a block of Roo.data.Records.
14048      * @param {Function} callback The function into which to pass the block of Roo.data.records.
14049      * The function must be passed <ul>
14050      * <li>The Record block object</li>
14051      * <li>The "arg" argument from the load function</li>
14052      * <li>A boolean success indicator</li>
14053      * </ul>
14054      * @param {Object} scope The scope in which to call the callback
14055      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
14056      */
14057     load : function(params, reader, callback, scope, arg){
14058         params = params || {};
14059         var result;
14060         try {
14061             result = reader.readRecords(params.data ? params.data :this.data);
14062         }catch(e){
14063             this.fireEvent("loadexception", this, arg, null, e);
14064             callback.call(scope, null, arg, false);
14065             return;
14066         }
14067         callback.call(scope, result, arg, true);
14068     },
14069     
14070     // private
14071     update : function(params, records){
14072         
14073     }
14074 });/*
14075  * Based on:
14076  * Ext JS Library 1.1.1
14077  * Copyright(c) 2006-2007, Ext JS, LLC.
14078  *
14079  * Originally Released Under LGPL - original licence link has changed is not relivant.
14080  *
14081  * Fork - LGPL
14082  * <script type="text/javascript">
14083  */
14084 /**
14085  * @class Roo.data.HttpProxy
14086  * @extends Roo.data.DataProxy
14087  * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
14088  * configured to reference a certain URL.<br><br>
14089  * <p>
14090  * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
14091  * from which the running page was served.<br><br>
14092  * <p>
14093  * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
14094  * <p>
14095  * Be aware that to enable the browser to parse an XML document, the server must set
14096  * the Content-Type header in the HTTP response to "text/xml".
14097  * @constructor
14098  * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
14099  * an {@link Roo.data.Connection} object.  If a Connection config is passed, the singleton {@link Roo.Ajax} object
14100  * will be used to make the request.
14101  */
14102 Roo.data.HttpProxy = function(conn){
14103     Roo.data.HttpProxy.superclass.constructor.call(this);
14104     // is conn a conn config or a real conn?
14105     this.conn = conn;
14106     this.useAjax = !conn || !conn.events;
14107   
14108 };
14109
14110 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
14111     // thse are take from connection...
14112     
14113     /**
14114      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
14115      */
14116     /**
14117      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
14118      * extra parameters to each request made by this object. (defaults to undefined)
14119      */
14120     /**
14121      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
14122      *  to each request made by this object. (defaults to undefined)
14123      */
14124     /**
14125      * @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)
14126      */
14127     /**
14128      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
14129      */
14130      /**
14131      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
14132      * @type Boolean
14133      */
14134   
14135
14136     /**
14137      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
14138      * @type Boolean
14139      */
14140     /**
14141      * Return the {@link Roo.data.Connection} object being used by this Proxy.
14142      * @return {Connection} The Connection object. This object may be used to subscribe to events on
14143      * a finer-grained basis than the DataProxy events.
14144      */
14145     getConnection : function(){
14146         return this.useAjax ? Roo.Ajax : this.conn;
14147     },
14148
14149     /**
14150      * Load data from the configured {@link Roo.data.Connection}, read the data object into
14151      * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
14152      * process that block using the passed callback.
14153      * @param {Object} params An object containing properties which are to be used as HTTP parameters
14154      * for the request to the remote server.
14155      * @param {Roo.data.DataReader} reader The Reader object which converts the data
14156      * object into a block of Roo.data.Records.
14157      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
14158      * The function must be passed <ul>
14159      * <li>The Record block object</li>
14160      * <li>The "arg" argument from the load function</li>
14161      * <li>A boolean success indicator</li>
14162      * </ul>
14163      * @param {Object} scope The scope in which to call the callback
14164      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
14165      */
14166     load : function(params, reader, callback, scope, arg){
14167         if(this.fireEvent("beforeload", this, params) !== false){
14168             var  o = {
14169                 params : params || {},
14170                 request: {
14171                     callback : callback,
14172                     scope : scope,
14173                     arg : arg
14174                 },
14175                 reader: reader,
14176                 callback : this.loadResponse,
14177                 scope: this
14178             };
14179             if(this.useAjax){
14180                 Roo.applyIf(o, this.conn);
14181                 if(this.activeRequest){
14182                     Roo.Ajax.abort(this.activeRequest);
14183                 }
14184                 this.activeRequest = Roo.Ajax.request(o);
14185             }else{
14186                 this.conn.request(o);
14187             }
14188         }else{
14189             callback.call(scope||this, null, arg, false);
14190         }
14191     },
14192
14193     // private
14194     loadResponse : function(o, success, response){
14195         delete this.activeRequest;
14196         if(!success){
14197             this.fireEvent("loadexception", this, o, response);
14198             o.request.callback.call(o.request.scope, null, o.request.arg, false);
14199             return;
14200         }
14201         var result;
14202         try {
14203             result = o.reader.read(response);
14204         }catch(e){
14205             this.fireEvent("loadexception", this, o, response, e);
14206             o.request.callback.call(o.request.scope, null, o.request.arg, false);
14207             return;
14208         }
14209         
14210         this.fireEvent("load", this, o, o.request.arg);
14211         o.request.callback.call(o.request.scope, result, o.request.arg, true);
14212     },
14213
14214     // private
14215     update : function(dataSet){
14216
14217     },
14218
14219     // private
14220     updateResponse : function(dataSet){
14221
14222     }
14223 });/*
14224  * Based on:
14225  * Ext JS Library 1.1.1
14226  * Copyright(c) 2006-2007, Ext JS, LLC.
14227  *
14228  * Originally Released Under LGPL - original licence link has changed is not relivant.
14229  *
14230  * Fork - LGPL
14231  * <script type="text/javascript">
14232  */
14233
14234 /**
14235  * @class Roo.data.ScriptTagProxy
14236  * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
14237  * other than the originating domain of the running page.<br><br>
14238  * <p>
14239  * <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
14240  * of the running page, you must use this class, rather than DataProxy.</em><br><br>
14241  * <p>
14242  * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
14243  * source code that is used as the source inside a &lt;script> tag.<br><br>
14244  * <p>
14245  * In order for the browser to process the returned data, the server must wrap the data object
14246  * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
14247  * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
14248  * depending on whether the callback name was passed:
14249  * <p>
14250  * <pre><code>
14251 boolean scriptTag = false;
14252 String cb = request.getParameter("callback");
14253 if (cb != null) {
14254     scriptTag = true;
14255     response.setContentType("text/javascript");
14256 } else {
14257     response.setContentType("application/x-json");
14258 }
14259 Writer out = response.getWriter();
14260 if (scriptTag) {
14261     out.write(cb + "(");
14262 }
14263 out.print(dataBlock.toJsonString());
14264 if (scriptTag) {
14265     out.write(");");
14266 }
14267 </pre></code>
14268  *
14269  * @constructor
14270  * @param {Object} config A configuration object.
14271  */
14272 Roo.data.ScriptTagProxy = function(config){
14273     Roo.data.ScriptTagProxy.superclass.constructor.call(this);
14274     Roo.apply(this, config);
14275     this.head = document.getElementsByTagName("head")[0];
14276 };
14277
14278 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
14279
14280 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
14281     /**
14282      * @cfg {String} url The URL from which to request the data object.
14283      */
14284     /**
14285      * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
14286      */
14287     timeout : 30000,
14288     /**
14289      * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
14290      * the server the name of the callback function set up by the load call to process the returned data object.
14291      * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
14292      * javascript output which calls this named function passing the data object as its only parameter.
14293      */
14294     callbackParam : "callback",
14295     /**
14296      *  @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
14297      * name to the request.
14298      */
14299     nocache : true,
14300
14301     /**
14302      * Load data from the configured URL, read the data object into
14303      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
14304      * process that block using the passed callback.
14305      * @param {Object} params An object containing properties which are to be used as HTTP parameters
14306      * for the request to the remote server.
14307      * @param {Roo.data.DataReader} reader The Reader object which converts the data
14308      * object into a block of Roo.data.Records.
14309      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
14310      * The function must be passed <ul>
14311      * <li>The Record block object</li>
14312      * <li>The "arg" argument from the load function</li>
14313      * <li>A boolean success indicator</li>
14314      * </ul>
14315      * @param {Object} scope The scope in which to call the callback
14316      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
14317      */
14318     load : function(params, reader, callback, scope, arg){
14319         if(this.fireEvent("beforeload", this, params) !== false){
14320
14321             var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
14322
14323             var url = this.url;
14324             url += (url.indexOf("?") != -1 ? "&" : "?") + p;
14325             if(this.nocache){
14326                 url += "&_dc=" + (new Date().getTime());
14327             }
14328             var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
14329             var trans = {
14330                 id : transId,
14331                 cb : "stcCallback"+transId,
14332                 scriptId : "stcScript"+transId,
14333                 params : params,
14334                 arg : arg,
14335                 url : url,
14336                 callback : callback,
14337                 scope : scope,
14338                 reader : reader
14339             };
14340             var conn = this;
14341
14342             window[trans.cb] = function(o){
14343                 conn.handleResponse(o, trans);
14344             };
14345
14346             url += String.format("&{0}={1}", this.callbackParam, trans.cb);
14347
14348             if(this.autoAbort !== false){
14349                 this.abort();
14350             }
14351
14352             trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
14353
14354             var script = document.createElement("script");
14355             script.setAttribute("src", url);
14356             script.setAttribute("type", "text/javascript");
14357             script.setAttribute("id", trans.scriptId);
14358             this.head.appendChild(script);
14359
14360             this.trans = trans;
14361         }else{
14362             callback.call(scope||this, null, arg, false);
14363         }
14364     },
14365
14366     // private
14367     isLoading : function(){
14368         return this.trans ? true : false;
14369     },
14370
14371     /**
14372      * Abort the current server request.
14373      */
14374     abort : function(){
14375         if(this.isLoading()){
14376             this.destroyTrans(this.trans);
14377         }
14378     },
14379
14380     // private
14381     destroyTrans : function(trans, isLoaded){
14382         this.head.removeChild(document.getElementById(trans.scriptId));
14383         clearTimeout(trans.timeoutId);
14384         if(isLoaded){
14385             window[trans.cb] = undefined;
14386             try{
14387                 delete window[trans.cb];
14388             }catch(e){}
14389         }else{
14390             // if hasn't been loaded, wait for load to remove it to prevent script error
14391             window[trans.cb] = function(){
14392                 window[trans.cb] = undefined;
14393                 try{
14394                     delete window[trans.cb];
14395                 }catch(e){}
14396             };
14397         }
14398     },
14399
14400     // private
14401     handleResponse : function(o, trans){
14402         this.trans = false;
14403         this.destroyTrans(trans, true);
14404         var result;
14405         try {
14406             result = trans.reader.readRecords(o);
14407         }catch(e){
14408             this.fireEvent("loadexception", this, o, trans.arg, e);
14409             trans.callback.call(trans.scope||window, null, trans.arg, false);
14410             return;
14411         }
14412         this.fireEvent("load", this, o, trans.arg);
14413         trans.callback.call(trans.scope||window, result, trans.arg, true);
14414     },
14415
14416     // private
14417     handleFailure : function(trans){
14418         this.trans = false;
14419         this.destroyTrans(trans, false);
14420         this.fireEvent("loadexception", this, null, trans.arg);
14421         trans.callback.call(trans.scope||window, null, trans.arg, false);
14422     }
14423 });/*
14424  * Based on:
14425  * Ext JS Library 1.1.1
14426  * Copyright(c) 2006-2007, Ext JS, LLC.
14427  *
14428  * Originally Released Under LGPL - original licence link has changed is not relivant.
14429  *
14430  * Fork - LGPL
14431  * <script type="text/javascript">
14432  */
14433
14434 /**
14435  * @class Roo.data.JsonReader
14436  * @extends Roo.data.DataReader
14437  * Data reader class to create an Array of Roo.data.Record objects from a JSON response
14438  * based on mappings in a provided Roo.data.Record constructor.
14439  * 
14440  * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
14441  * in the reply previously. 
14442  * 
14443  * <p>
14444  * Example code:
14445  * <pre><code>
14446 var RecordDef = Roo.data.Record.create([
14447     {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
14448     {name: 'occupation'}                 // This field will use "occupation" as the mapping.
14449 ]);
14450 var myReader = new Roo.data.JsonReader({
14451     totalProperty: "results",    // The property which contains the total dataset size (optional)
14452     root: "rows",                // The property which contains an Array of row objects
14453     id: "id"                     // The property within each row object that provides an ID for the record (optional)
14454 }, RecordDef);
14455 </code></pre>
14456  * <p>
14457  * This would consume a JSON file like this:
14458  * <pre><code>
14459 { 'results': 2, 'rows': [
14460     { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
14461     { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
14462 }
14463 </code></pre>
14464  * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
14465  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
14466  * paged from the remote server.
14467  * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
14468  * @cfg {String} root name of the property which contains the Array of row objects.
14469  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
14470  * @cfg {Array} fields Array of field definition objects
14471  * @constructor
14472  * Create a new JsonReader
14473  * @param {Object} meta Metadata configuration options
14474  * @param {Object} recordType Either an Array of field definition objects,
14475  * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
14476  */
14477 Roo.data.JsonReader = function(meta, recordType){
14478     
14479     meta = meta || {};
14480     // set some defaults:
14481     Roo.applyIf(meta, {
14482         totalProperty: 'total',
14483         successProperty : 'success',
14484         root : 'data',
14485         id : 'id'
14486     });
14487     
14488     Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
14489 };
14490 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
14491     
14492     readerType : 'Json',
14493     
14494     /**
14495      * @prop {Boolean} metaFromRemote  - if the meta data was loaded from the remote source.
14496      * Used by Store query builder to append _requestMeta to params.
14497      * 
14498      */
14499     metaFromRemote : false,
14500     /**
14501      * This method is only used by a DataProxy which has retrieved data from a remote server.
14502      * @param {Object} response The XHR object which contains the JSON data in its responseText.
14503      * @return {Object} data A data block which is used by an Roo.data.Store object as
14504      * a cache of Roo.data.Records.
14505      */
14506     read : function(response){
14507         var json = response.responseText;
14508        
14509         var o = /* eval:var:o */ eval("("+json+")");
14510         if(!o) {
14511             throw {message: "JsonReader.read: Json object not found"};
14512         }
14513         
14514         if(o.metaData){
14515             
14516             delete this.ef;
14517             this.metaFromRemote = true;
14518             this.meta = o.metaData;
14519             this.recordType = Roo.data.Record.create(o.metaData.fields);
14520             this.onMetaChange(this.meta, this.recordType, o);
14521         }
14522         return this.readRecords(o);
14523     },
14524
14525     // private function a store will implement
14526     onMetaChange : function(meta, recordType, o){
14527
14528     },
14529
14530     /**
14531          * @ignore
14532          */
14533     simpleAccess: function(obj, subsc) {
14534         return obj[subsc];
14535     },
14536
14537         /**
14538          * @ignore
14539          */
14540     getJsonAccessor: function(){
14541         var re = /[\[\.]/;
14542         return function(expr) {
14543             try {
14544                 return(re.test(expr))
14545                     ? new Function("obj", "return obj." + expr)
14546                     : function(obj){
14547                         return obj[expr];
14548                     };
14549             } catch(e){}
14550             return Roo.emptyFn;
14551         };
14552     }(),
14553
14554     /**
14555      * Create a data block containing Roo.data.Records from an XML document.
14556      * @param {Object} o An object which contains an Array of row objects in the property specified
14557      * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
14558      * which contains the total size of the dataset.
14559      * @return {Object} data A data block which is used by an Roo.data.Store object as
14560      * a cache of Roo.data.Records.
14561      */
14562     readRecords : function(o){
14563         /**
14564          * After any data loads, the raw JSON data is available for further custom processing.
14565          * @type Object
14566          */
14567         this.o = o;
14568         var s = this.meta, Record = this.recordType,
14569             f = Record ? Record.prototype.fields : null, fi = f ? f.items : [], fl = f ? f.length : 0;
14570
14571 //      Generate extraction functions for the totalProperty, the root, the id, and for each field
14572         if (!this.ef) {
14573             if(s.totalProperty) {
14574                     this.getTotal = this.getJsonAccessor(s.totalProperty);
14575                 }
14576                 if(s.successProperty) {
14577                     this.getSuccess = this.getJsonAccessor(s.successProperty);
14578                 }
14579                 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
14580                 if (s.id) {
14581                         var g = this.getJsonAccessor(s.id);
14582                         this.getId = function(rec) {
14583                                 var r = g(rec);  
14584                                 return (r === undefined || r === "") ? null : r;
14585                         };
14586                 } else {
14587                         this.getId = function(){return null;};
14588                 }
14589             this.ef = [];
14590             for(var jj = 0; jj < fl; jj++){
14591                 f = fi[jj];
14592                 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
14593                 this.ef[jj] = this.getJsonAccessor(map);
14594             }
14595         }
14596
14597         var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
14598         if(s.totalProperty){
14599             var vt = parseInt(this.getTotal(o), 10);
14600             if(!isNaN(vt)){
14601                 totalRecords = vt;
14602             }
14603         }
14604         if(s.successProperty){
14605             var vs = this.getSuccess(o);
14606             if(vs === false || vs === 'false'){
14607                 success = false;
14608             }
14609         }
14610         var records = [];
14611         for(var i = 0; i < c; i++){
14612                 var n = root[i];
14613             var values = {};
14614             var id = this.getId(n);
14615             for(var j = 0; j < fl; j++){
14616                 f = fi[j];
14617             var v = this.ef[j](n);
14618             if (!f.convert) {
14619                 Roo.log('missing convert for ' + f.name);
14620                 Roo.log(f);
14621                 continue;
14622             }
14623             values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
14624             }
14625             var record = new Record(values, id);
14626             record.json = n;
14627             records[i] = record;
14628         }
14629         return {
14630             raw : o,
14631             success : success,
14632             records : records,
14633             totalRecords : totalRecords
14634         };
14635     },
14636     // used when loading children.. @see loadDataFromChildren
14637     toLoadData: function(rec)
14638     {
14639         // expect rec just to be an array.. eg [a,b,c, [...] << cn ]
14640         var data = typeof(rec.data.cn) == 'undefined' ? [] : rec.data.cn;
14641         return { data : data, total : data.length };
14642         
14643     }
14644 });/*
14645  * Based on:
14646  * Ext JS Library 1.1.1
14647  * Copyright(c) 2006-2007, Ext JS, LLC.
14648  *
14649  * Originally Released Under LGPL - original licence link has changed is not relivant.
14650  *
14651  * Fork - LGPL
14652  * <script type="text/javascript">
14653  */
14654
14655 /**
14656  * @class Roo.data.ArrayReader
14657  * @extends Roo.data.DataReader
14658  * Data reader class to create an Array of Roo.data.Record objects from an Array.
14659  * Each element of that Array represents a row of data fields. The
14660  * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
14661  * of the field definition if it exists, or the field's ordinal position in the definition.<br>
14662  * <p>
14663  * Example code:.
14664  * <pre><code>
14665 var RecordDef = Roo.data.Record.create([
14666     {name: 'name', mapping: 1},         // "mapping" only needed if an "id" field is present which
14667     {name: 'occupation', mapping: 2}    // precludes using the ordinal position as the index.
14668 ]);
14669 var myReader = new Roo.data.ArrayReader({
14670     id: 0                     // The subscript within row Array that provides an ID for the Record (optional)
14671 }, RecordDef);
14672 </code></pre>
14673  * <p>
14674  * This would consume an Array like this:
14675  * <pre><code>
14676 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
14677   </code></pre>
14678  
14679  * @constructor
14680  * Create a new JsonReader
14681  * @param {Object} meta Metadata configuration options.
14682  * @param {Object|Array} recordType Either an Array of field definition objects
14683  * 
14684  * @cfg {Array} fields Array of field definition objects
14685  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
14686  * as specified to {@link Roo.data.Record#create},
14687  * or an {@link Roo.data.Record} object
14688  *
14689  * 
14690  * created using {@link Roo.data.Record#create}.
14691  */
14692 Roo.data.ArrayReader = function(meta, recordType)
14693 {    
14694     Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType||meta.fields);
14695 };
14696
14697 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
14698     
14699       /**
14700      * Create a data block containing Roo.data.Records from an XML document.
14701      * @param {Object} o An Array of row objects which represents the dataset.
14702      * @return {Object} A data block which is used by an {@link Roo.data.Store} object as
14703      * a cache of Roo.data.Records.
14704      */
14705     readRecords : function(o)
14706     {
14707         var sid = this.meta ? this.meta.id : null;
14708         var recordType = this.recordType, fields = recordType.prototype.fields;
14709         var records = [];
14710         var root = o;
14711         for(var i = 0; i < root.length; i++){
14712                 var n = root[i];
14713             var values = {};
14714             var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
14715             for(var j = 0, jlen = fields.length; j < jlen; j++){
14716                 var f = fields.items[j];
14717                 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
14718                 var v = n[k] !== undefined ? n[k] : f.defaultValue;
14719                 v = f.convert(v);
14720                 values[f.name] = v;
14721             }
14722             var record = new recordType(values, id);
14723             record.json = n;
14724             records[records.length] = record;
14725         }
14726         return {
14727             records : records,
14728             totalRecords : records.length
14729         };
14730     },
14731     // used when loading children.. @see loadDataFromChildren
14732     toLoadData: function(rec)
14733     {
14734         // expect rec just to be an array.. eg [a,b,c, [...] << cn ]
14735         return typeof(rec.data.cn) == 'undefined' ? [] : rec.data.cn;
14736         
14737     }
14738     
14739     
14740 });/*
14741  * - LGPL
14742  * * 
14743  */
14744
14745 /**
14746  * @class Roo.bootstrap.ComboBox
14747  * @extends Roo.bootstrap.TriggerField
14748  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
14749  * @cfg {Boolean} append (true|false) default false
14750  * @cfg {Boolean} autoFocus (true|false) auto focus the first item, default true
14751  * @cfg {Boolean} tickable ComboBox with tickable selections (true|false), default false
14752  * @cfg {Boolean} triggerList trigger show the list or not (true|false) default true
14753  * @cfg {Boolean} showToggleBtn show toggle button or not (true|false) default true
14754  * @cfg {String} btnPosition set the position of the trigger button (left | right) default right
14755  * @cfg {Boolean} animate default true
14756  * @cfg {Boolean} emptyResultText only for touch device
14757  * @cfg {String} triggerText multiple combobox trigger button text default 'Select'
14758  * @cfg {String} emptyTitle default ''
14759  * @cfg {Number} width fixed with? experimental
14760  * @constructor
14761  * Create a new ComboBox.
14762  * @param {Object} config Configuration options
14763  */
14764 Roo.bootstrap.ComboBox = function(config){
14765     Roo.bootstrap.ComboBox.superclass.constructor.call(this, config);
14766     this.addEvents({
14767         /**
14768          * @event expand
14769          * Fires when the dropdown list is expanded
14770         * @param {Roo.bootstrap.ComboBox} combo This combo box
14771         */
14772         'expand' : true,
14773         /**
14774          * @event collapse
14775          * Fires when the dropdown list is collapsed
14776         * @param {Roo.bootstrap.ComboBox} combo This combo box
14777         */
14778         'collapse' : true,
14779         /**
14780          * @event beforeselect
14781          * Fires before a list item is selected. Return false to cancel the selection.
14782         * @param {Roo.bootstrap.ComboBox} combo This combo box
14783         * @param {Roo.data.Record} record The data record returned from the underlying store
14784         * @param {Number} index The index of the selected item in the dropdown list
14785         */
14786         'beforeselect' : true,
14787         /**
14788          * @event select
14789          * Fires when a list item is selected
14790         * @param {Roo.bootstrap.ComboBox} combo This combo box
14791         * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
14792         * @param {Number} index The index of the selected item in the dropdown list
14793         */
14794         'select' : true,
14795         /**
14796          * @event beforequery
14797          * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
14798          * The event object passed has these properties:
14799         * @param {Roo.bootstrap.ComboBox} combo This combo box
14800         * @param {String} query The query
14801         * @param {Boolean} forceAll true to force "all" query
14802         * @param {Boolean} cancel true to cancel the query
14803         * @param {Object} e The query event object
14804         */
14805         'beforequery': true,
14806          /**
14807          * @event add
14808          * Fires when the 'add' icon is pressed (add a listener to enable add button)
14809         * @param {Roo.bootstrap.ComboBox} combo This combo box
14810         */
14811         'add' : true,
14812         /**
14813          * @event edit
14814          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
14815         * @param {Roo.bootstrap.ComboBox} combo This combo box
14816         * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
14817         */
14818         'edit' : true,
14819         /**
14820          * @event remove
14821          * Fires when the remove value from the combobox array
14822         * @param {Roo.bootstrap.ComboBox} combo This combo box
14823         */
14824         'remove' : true,
14825         /**
14826          * @event afterremove
14827          * Fires when the remove value from the combobox array
14828         * @param {Roo.bootstrap.ComboBox} combo This combo box
14829         */
14830         'afterremove' : true,
14831         /**
14832          * @event specialfilter
14833          * Fires when specialfilter
14834             * @param {Roo.bootstrap.ComboBox} combo This combo box
14835             */
14836         'specialfilter' : true,
14837         /**
14838          * @event tick
14839          * Fires when tick the element
14840             * @param {Roo.bootstrap.ComboBox} combo This combo box
14841             */
14842         'tick' : true,
14843         /**
14844          * @event touchviewdisplay
14845          * Fires when touch view require special display (default is using displayField)
14846             * @param {Roo.bootstrap.ComboBox} combo This combo box
14847             * @param {Object} cfg set html .
14848             */
14849         'touchviewdisplay' : true
14850         
14851     });
14852     
14853     this.item = [];
14854     this.tickItems = [];
14855     
14856     this.selectedIndex = -1;
14857     if(this.mode == 'local'){
14858         if(config.queryDelay === undefined){
14859             this.queryDelay = 10;
14860         }
14861         if(config.minChars === undefined){
14862             this.minChars = 0;
14863         }
14864     }
14865 };
14866
14867 Roo.extend(Roo.bootstrap.ComboBox, Roo.bootstrap.TriggerField, {
14868      
14869     /**
14870      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
14871      * rendering into an Roo.Editor, defaults to false)
14872      */
14873     /**
14874      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
14875      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
14876      */
14877     /**
14878      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
14879      */
14880     /**
14881      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
14882      * the dropdown list (defaults to undefined, with no header element)
14883      */
14884
14885      /**
14886      * @cfg {String/Roo.Template} tpl The template to use to render the output default is  '<a class="dropdown-item" href="#">{' + this.displayField + '}</a>' 
14887      */
14888      
14889      /**
14890      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
14891      */
14892     listWidth: undefined,
14893     /**
14894      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
14895      * mode = 'remote' or 'text' if mode = 'local')
14896      */
14897     displayField: undefined,
14898     
14899     /**
14900      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
14901      * mode = 'remote' or 'value' if mode = 'local'). 
14902      * Note: use of a valueField requires the user make a selection
14903      * in order for a value to be mapped.
14904      */
14905     valueField: undefined,
14906     /**
14907      * @cfg {String} modalTitle The title of the dialog that pops up on mobile views.
14908      */
14909     modalTitle : '',
14910     
14911     /**
14912      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
14913      * field's data value (defaults to the underlying DOM element's name)
14914      */
14915     hiddenName: undefined,
14916     /**
14917      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
14918      */
14919     listClass: '',
14920     /**
14921      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
14922      */
14923     selectedClass: 'active',
14924     
14925     /**
14926      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
14927      */
14928     shadow:'sides',
14929     /**
14930      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
14931      * anchor positions (defaults to 'tl-bl')
14932      */
14933     listAlign: 'tl-bl?',
14934     /**
14935      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
14936      */
14937     maxHeight: 300,
14938     /**
14939      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
14940      * query specified by the allQuery config option (defaults to 'query')
14941      */
14942     triggerAction: 'query',
14943     /**
14944      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
14945      * (defaults to 4, does not apply if editable = false)
14946      */
14947     minChars : 4,
14948     /**
14949      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
14950      * delay (typeAheadDelay) if it matches a known value (defaults to false)
14951      */
14952     typeAhead: false,
14953     /**
14954      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
14955      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
14956      */
14957     queryDelay: 500,
14958     /**
14959      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
14960      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
14961      */
14962     pageSize: 0,
14963     /**
14964      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
14965      * when editable = true (defaults to false)
14966      */
14967     selectOnFocus:false,
14968     /**
14969      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
14970      */
14971     queryParam: 'query',
14972     /**
14973      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
14974      * when mode = 'remote' (defaults to 'Loading...')
14975      */
14976     loadingText: 'Loading...',
14977     /**
14978      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
14979      */
14980     resizable: false,
14981     /**
14982      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
14983      */
14984     handleHeight : 8,
14985     /**
14986      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
14987      * traditional select (defaults to true)
14988      */
14989     editable: true,
14990     /**
14991      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
14992      */
14993     allQuery: '',
14994     /**
14995      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
14996      */
14997     mode: 'remote',
14998     /**
14999      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
15000      * listWidth has a higher value)
15001      */
15002     minListWidth : 70,
15003     /**
15004      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
15005      * allow the user to set arbitrary text into the field (defaults to false)
15006      */
15007     forceSelection:false,
15008     /**
15009      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
15010      * if typeAhead = true (defaults to 250)
15011      */
15012     typeAheadDelay : 250,
15013     /**
15014      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
15015      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
15016      */
15017     valueNotFoundText : undefined,
15018     /**
15019      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
15020      */
15021     blockFocus : false,
15022     
15023     /**
15024      * @cfg {Boolean} disableClear Disable showing of clear button.
15025      */
15026     disableClear : false,
15027     /**
15028      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
15029      */
15030     alwaysQuery : false,
15031     
15032     /**
15033      * @cfg {Boolean} multiple  (true|false) ComboBobArray, default false
15034      */
15035     multiple : false,
15036     
15037     /**
15038      * @cfg {String} invalidClass DEPRICATED - uses BS4 is-valid now
15039      */
15040     invalidClass : "has-warning",
15041     
15042     /**
15043      * @cfg {String} validClass DEPRICATED - uses BS4 is-valid now
15044      */
15045     validClass : "has-success",
15046     
15047     /**
15048      * @cfg {Boolean} specialFilter (true|false) special filter default false
15049      */
15050     specialFilter : false,
15051     
15052     /**
15053      * @cfg {Boolean} mobileTouchView (true|false) show mobile touch view when using a mobile default true
15054      */
15055     mobileTouchView : true,
15056     
15057     /**
15058      * @cfg {Boolean} useNativeIOS (true|false) render it as classic select for ios, not support dynamic load data (default false)
15059      */
15060     useNativeIOS : false,
15061     
15062     /**
15063      * @cfg {Boolean} mobile_restrict_height (true|false) restrict height for touch view
15064      */
15065     mobile_restrict_height : false,
15066     
15067     ios_options : false,
15068     
15069     //private
15070     addicon : false,
15071     editicon: false,
15072     
15073     page: 0,
15074     hasQuery: false,
15075     append: false,
15076     loadNext: false,
15077     autoFocus : true,
15078     tickable : false,
15079     btnPosition : 'right',
15080     triggerList : true,
15081     showToggleBtn : true,
15082     animate : true,
15083     emptyResultText: 'Empty',
15084     triggerText : 'Select',
15085     emptyTitle : '',
15086     width : false,
15087     
15088     // element that contains real text value.. (when hidden is used..)
15089     
15090     getAutoCreate : function()
15091     {   
15092         var cfg = false;
15093         //render
15094         /*
15095          * Render classic select for iso
15096          */
15097         
15098         if(Roo.isIOS && this.useNativeIOS){
15099             cfg = this.getAutoCreateNativeIOS();
15100             return cfg;
15101         }
15102         
15103         /*
15104          * Touch Devices
15105          */
15106         
15107         if(Roo.isTouch && this.mobileTouchView){
15108             cfg = this.getAutoCreateTouchView();
15109             return cfg;;
15110         }
15111         
15112         /*
15113          *  Normal ComboBox
15114          */
15115         if(!this.tickable){
15116             cfg = Roo.bootstrap.ComboBox.superclass.getAutoCreate.call(this);
15117             return cfg;
15118         }
15119         
15120         /*
15121          *  ComboBox with tickable selections
15122          */
15123              
15124         var align = this.labelAlign || this.parentLabelAlign();
15125         
15126         cfg = {
15127             cls : 'form-group roo-combobox-tickable' //input-group
15128         };
15129         
15130         var btn_text_select = '';
15131         var btn_text_done = '';
15132         var btn_text_cancel = '';
15133         
15134         if (this.btn_text_show) {
15135             btn_text_select = 'Select';
15136             btn_text_done = 'Done';
15137             btn_text_cancel = 'Cancel'; 
15138         }
15139         
15140         var buttons = {
15141             tag : 'div',
15142             cls : 'tickable-buttons',
15143             cn : [
15144                 {
15145                     tag : 'button',
15146                     type : 'button',
15147                     cls : 'btn btn-link btn-edit pull-' + this.btnPosition,
15148                     //html : this.triggerText
15149                     html: btn_text_select
15150                 },
15151                 {
15152                     tag : 'button',
15153                     type : 'button',
15154                     name : 'ok',
15155                     cls : 'btn btn-link btn-ok pull-' + this.btnPosition,
15156                     //html : 'Done'
15157                     html: btn_text_done
15158                 },
15159                 {
15160                     tag : 'button',
15161                     type : 'button',
15162                     name : 'cancel',
15163                     cls : 'btn btn-link btn-cancel pull-' + this.btnPosition,
15164                     //html : 'Cancel'
15165                     html: btn_text_cancel
15166                 }
15167             ]
15168         };
15169         
15170         if(this.editable){
15171             buttons.cn.unshift({
15172                 tag: 'input',
15173                 cls: 'roo-select2-search-field-input'
15174             });
15175         }
15176         
15177         var _this = this;
15178         
15179         Roo.each(buttons.cn, function(c){
15180             if (_this.size) {
15181                 c.cls += ' btn-' + _this.size;
15182             }
15183
15184             if (_this.disabled) {
15185                 c.disabled = true;
15186             }
15187         });
15188         
15189         var box = {
15190             tag: 'div',
15191             style : 'display: contents',
15192             cn: [
15193                 {
15194                     tag: 'input',
15195                     type : 'hidden',
15196                     cls: 'form-hidden-field'
15197                 },
15198                 {
15199                     tag: 'ul',
15200                     cls: 'roo-select2-choices',
15201                     cn:[
15202                         {
15203                             tag: 'li',
15204                             cls: 'roo-select2-search-field',
15205                             cn: [
15206                                 buttons
15207                             ]
15208                         }
15209                     ]
15210                 }
15211             ]
15212         };
15213         
15214         var combobox = {
15215             cls: 'roo-select2-container input-group roo-select2-container-multi',
15216             cn: [
15217                 
15218                 box
15219 //                {
15220 //                    tag: 'ul',
15221 //                    cls: 'typeahead typeahead-long dropdown-menu',
15222 //                    style: 'display:none; max-height:' + this.maxHeight + 'px;'
15223 //                }
15224             ]
15225         };
15226         
15227         if(this.hasFeedback && !this.allowBlank){
15228             
15229             var feedback = {
15230                 tag: 'span',
15231                 cls: 'glyphicon form-control-feedback'
15232             };
15233
15234             combobox.cn.push(feedback);
15235         }
15236         
15237         
15238         
15239         var indicator = {
15240             tag : 'i',
15241             cls : 'roo-required-indicator ' + (this.indicatorpos == 'right'  ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
15242             tooltip : 'This field is required'
15243         };
15244         if (Roo.bootstrap.version == 4) {
15245             indicator = {
15246                 tag : 'i',
15247                 style : 'display:none'
15248             };
15249         }
15250         if (align ==='left' && this.fieldLabel.length) {
15251             
15252             cfg.cls += ' roo-form-group-label-left'  + (Roo.bootstrap.version == 4 ? ' row' : '');
15253             
15254             cfg.cn = [
15255                 indicator,
15256                 {
15257                     tag: 'label',
15258                     'for' :  id,
15259                     cls : 'control-label col-form-label',
15260                     html : this.fieldLabel
15261
15262                 },
15263                 {
15264                     cls : "", 
15265                     cn: [
15266                         combobox
15267                     ]
15268                 }
15269
15270             ];
15271             
15272             var labelCfg = cfg.cn[1];
15273             var contentCfg = cfg.cn[2];
15274             
15275
15276             if(this.indicatorpos == 'right'){
15277                 
15278                 cfg.cn = [
15279                     {
15280                         tag: 'label',
15281                         'for' :  id,
15282                         cls : 'control-label col-form-label',
15283                         cn : [
15284                             {
15285                                 tag : 'span',
15286                                 html : this.fieldLabel
15287                             },
15288                             indicator
15289                         ]
15290                     },
15291                     {
15292                         cls : "",
15293                         cn: [
15294                             combobox
15295                         ]
15296                     }
15297
15298                 ];
15299                 
15300                 
15301                 
15302                 labelCfg = cfg.cn[0];
15303                 contentCfg = cfg.cn[1];
15304             
15305             }
15306             
15307             if(this.labelWidth > 12){
15308                 labelCfg.style = "width: " + this.labelWidth + 'px';
15309             }
15310             if(this.width * 1 > 0){
15311                 contentCfg.style = "width: " + this.width + 'px';
15312             }
15313             if(this.labelWidth < 13 && this.labelmd == 0){
15314                 this.labelmd = this.labelWidth;
15315             }
15316             
15317             if(this.labellg > 0){
15318                 labelCfg.cls += ' col-lg-' + this.labellg;
15319                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
15320             }
15321             
15322             if(this.labelmd > 0){
15323                 labelCfg.cls += ' col-md-' + this.labelmd;
15324                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
15325             }
15326             
15327             if(this.labelsm > 0){
15328                 labelCfg.cls += ' col-sm-' + this.labelsm;
15329                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
15330             }
15331             
15332             if(this.labelxs > 0){
15333                 labelCfg.cls += ' col-xs-' + this.labelxs;
15334                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
15335             }
15336                 
15337                 
15338         } else if ( this.fieldLabel.length) {
15339 //                Roo.log(" label");
15340                  cfg.cn = [
15341                    indicator,
15342                     {
15343                         tag: 'label',
15344                         //cls : 'input-group-addon',
15345                         html : this.fieldLabel
15346                     },
15347                     combobox
15348                 ];
15349                 
15350                 if(this.indicatorpos == 'right'){
15351                     cfg.cn = [
15352                         {
15353                             tag: 'label',
15354                             //cls : 'input-group-addon',
15355                             html : this.fieldLabel
15356                         },
15357                         indicator,
15358                         combobox
15359                     ];
15360                     
15361                 }
15362
15363         } else {
15364             
15365 //                Roo.log(" no label && no align");
15366                 cfg = combobox
15367                      
15368                 
15369         }
15370          
15371         var settings=this;
15372         ['xs','sm','md','lg'].map(function(size){
15373             if (settings[size]) {
15374                 cfg.cls += ' col-' + size + '-' + settings[size];
15375             }
15376         });
15377         
15378         return cfg;
15379         
15380     },
15381     
15382     _initEventsCalled : false,
15383     
15384     // private
15385     initEvents: function()
15386     {   
15387         if (this._initEventsCalled) { // as we call render... prevent looping...
15388             return;
15389         }
15390         this._initEventsCalled = true;
15391         
15392         if (!this.store) {
15393             throw "can not find store for combo";
15394         }
15395         
15396         this.indicator = this.indicatorEl();
15397         
15398         this.store = Roo.factory(this.store, Roo.data);
15399         this.store.parent = this;
15400         
15401         // if we are building from html. then this element is so complex, that we can not really
15402         // use the rendered HTML.
15403         // so we have to trash and replace the previous code.
15404         if (Roo.XComponent.build_from_html) {
15405             // remove this element....
15406             var e = this.el.dom, k=0;
15407             while (e ) { e = e.previousSibling;  ++k;}
15408
15409             this.el.remove();
15410             
15411             this.el=false;
15412             this.rendered = false;
15413             
15414             this.render(this.parent().getChildContainer(true), k);
15415         }
15416         
15417         if(Roo.isIOS && this.useNativeIOS){
15418             this.initIOSView();
15419             return;
15420         }
15421         
15422         /*
15423          * Touch Devices
15424          */
15425         
15426         if(Roo.isTouch && this.mobileTouchView){
15427             this.initTouchView();
15428             return;
15429         }
15430         
15431         if(this.tickable){
15432             this.initTickableEvents();
15433             return;
15434         }
15435         
15436         Roo.bootstrap.ComboBox.superclass.initEvents.call(this);
15437         
15438         if(this.hiddenName){
15439             
15440             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
15441             
15442             this.hiddenField.dom.value =
15443                 this.hiddenValue !== undefined ? this.hiddenValue :
15444                 this.value !== undefined ? this.value : '';
15445
15446             // prevent input submission
15447             this.el.dom.removeAttribute('name');
15448             this.hiddenField.dom.setAttribute('name', this.hiddenName);
15449              
15450              
15451         }
15452         //if(Roo.isGecko){
15453         //    this.el.dom.setAttribute('autocomplete', 'off');
15454         //}
15455         
15456         var cls = 'x-combo-list';
15457         
15458         //this.list = new Roo.Layer({
15459         //    shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
15460         //});
15461         
15462         var _this = this;
15463         
15464         (function(){
15465             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
15466             _this.list.setWidth(lw);
15467         }).defer(100);
15468         
15469         this.list.on('mouseover', this.onViewOver, this);
15470         this.list.on('mousemove', this.onViewMove, this);
15471         this.list.on('scroll', this.onViewScroll, this);
15472         
15473         /*
15474         this.list.swallowEvent('mousewheel');
15475         this.assetHeight = 0;
15476
15477         if(this.title){
15478             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
15479             this.assetHeight += this.header.getHeight();
15480         }
15481
15482         this.innerList = this.list.createChild({cls:cls+'-inner'});
15483         this.innerList.on('mouseover', this.onViewOver, this);
15484         this.innerList.on('mousemove', this.onViewMove, this);
15485         this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
15486         
15487         if(this.allowBlank && !this.pageSize && !this.disableClear){
15488             this.footer = this.list.createChild({cls:cls+'-ft'});
15489             this.pageTb = new Roo.Toolbar(this.footer);
15490            
15491         }
15492         if(this.pageSize){
15493             this.footer = this.list.createChild({cls:cls+'-ft'});
15494             this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
15495                     {pageSize: this.pageSize});
15496             
15497         }
15498         
15499         if (this.pageTb && this.allowBlank && !this.disableClear) {
15500             var _this = this;
15501             this.pageTb.add(new Roo.Toolbar.Fill(), {
15502                 cls: 'x-btn-icon x-btn-clear',
15503                 text: '&#160;',
15504                 handler: function()
15505                 {
15506                     _this.collapse();
15507                     _this.clearValue();
15508                     _this.onSelect(false, -1);
15509                 }
15510             });
15511         }
15512         if (this.footer) {
15513             this.assetHeight += this.footer.getHeight();
15514         }
15515         */
15516             
15517         if(!this.tpl){
15518             this.tpl = Roo.bootstrap.version == 4 ?
15519                 '<a class="dropdown-item" href="#">{' + this.displayField + '}</a>' :  // 4 does not need <li> and it get's really confisued.
15520                 '<li><a class="dropdown-item" href="#">{' + this.displayField + '}</a></li>';
15521         }
15522
15523         this.view = new Roo.View(this.list, this.tpl, {
15524             singleSelect:true, store: this.store, selectedClass: this.selectedClass
15525         });
15526         //this.view.wrapEl.setDisplayed(false);
15527         this.view.on('click', this.onViewClick, this);
15528         
15529         
15530         this.store.on('beforeload', this.onBeforeLoad, this);
15531         this.store.on('load', this.onLoad, this);
15532         this.store.on('loadexception', this.onLoadException, this);
15533         /*
15534         if(this.resizable){
15535             this.resizer = new Roo.Resizable(this.list,  {
15536                pinned:true, handles:'se'
15537             });
15538             this.resizer.on('resize', function(r, w, h){
15539                 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
15540                 this.listWidth = w;
15541                 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
15542                 this.restrictHeight();
15543             }, this);
15544             this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
15545         }
15546         */
15547         if(!this.editable){
15548             this.editable = true;
15549             this.setEditable(false);
15550         }
15551         
15552         /*
15553         
15554         if (typeof(this.events.add.listeners) != 'undefined') {
15555             
15556             this.addicon = this.wrap.createChild(
15557                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });  
15558        
15559             this.addicon.on('click', function(e) {
15560                 this.fireEvent('add', this);
15561             }, this);
15562         }
15563         if (typeof(this.events.edit.listeners) != 'undefined') {
15564             
15565             this.editicon = this.wrap.createChild(
15566                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });  
15567             if (this.addicon) {
15568                 this.editicon.setStyle('margin-left', '40px');
15569             }
15570             this.editicon.on('click', function(e) {
15571                 
15572                 // we fire even  if inothing is selected..
15573                 this.fireEvent('edit', this, this.lastData );
15574                 
15575             }, this);
15576         }
15577         */
15578         
15579         this.keyNav = new Roo.KeyNav(this.inputEl(), {
15580             "up" : function(e){
15581                 this.inKeyMode = true;
15582                 this.selectPrev();
15583             },
15584
15585             "down" : function(e){
15586                 if(!this.isExpanded()){
15587                     this.onTriggerClick();
15588                 }else{
15589                     this.inKeyMode = true;
15590                     this.selectNext();
15591                 }
15592             },
15593
15594             "enter" : function(e){
15595 //                this.onViewClick();
15596                 //return true;
15597                 this.collapse();
15598                 
15599                 if(this.fireEvent("specialkey", this, e)){
15600                     this.onViewClick(false);
15601                 }
15602                 
15603                 return true;
15604             },
15605
15606             "esc" : function(e){
15607                 this.collapse();
15608             },
15609
15610             "tab" : function(e){
15611                 this.collapse();
15612                 
15613                 if(this.fireEvent("specialkey", this, e)){
15614                     this.onViewClick(false);
15615                 }
15616                 
15617                 return true;
15618             },
15619
15620             scope : this,
15621
15622             doRelay : function(foo, bar, hname){
15623                 if(hname == 'down' || this.scope.isExpanded()){
15624                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
15625                 }
15626                 return true;
15627             },
15628
15629             forceKeyDown: true
15630         });
15631         
15632         
15633         this.queryDelay = Math.max(this.queryDelay || 10,
15634                 this.mode == 'local' ? 10 : 250);
15635         
15636         
15637         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
15638         
15639         if(this.typeAhead){
15640             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
15641         }
15642         if(this.editable !== false){
15643             this.inputEl().on("keyup", this.onKeyUp, this);
15644         }
15645         if(this.forceSelection){
15646             this.inputEl().on('blur', this.doForce, this);
15647         }
15648         
15649         if(this.multiple){
15650             this.choices = this.el.select('ul.roo-select2-choices', true).first();
15651             this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
15652         }
15653     },
15654     
15655     initTickableEvents: function()
15656     {   
15657         this.createList();
15658         
15659         if(this.hiddenName){
15660             
15661             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
15662             
15663             this.hiddenField.dom.value =
15664                 this.hiddenValue !== undefined ? this.hiddenValue :
15665                 this.value !== undefined ? this.value : '';
15666
15667             // prevent input submission
15668             this.el.dom.removeAttribute('name');
15669             this.hiddenField.dom.setAttribute('name', this.hiddenName);
15670              
15671              
15672         }
15673         
15674 //        this.list = this.el.select('ul.dropdown-menu',true).first();
15675         
15676         this.choices = this.el.select('ul.roo-select2-choices', true).first();
15677         this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
15678         if(this.triggerList){
15679             this.searchField.on("click", this.onSearchFieldClick, this, {preventDefault:true});
15680         }
15681          
15682         this.trigger = this.el.select('.tickable-buttons > .btn-edit', true).first();
15683         this.trigger.on("click", this.onTickableTriggerClick, this, {preventDefault:true});
15684         
15685         this.okBtn = this.el.select('.tickable-buttons > .btn-ok', true).first();
15686         this.cancelBtn = this.el.select('.tickable-buttons > .btn-cancel', true).first();
15687         
15688         this.okBtn.on('click', this.onTickableFooterButtonClick, this, this.okBtn);
15689         this.cancelBtn.on('click', this.onTickableFooterButtonClick, this, this.cancelBtn);
15690         
15691         this.trigger.setVisibilityMode(Roo.Element.DISPLAY);
15692         this.okBtn.setVisibilityMode(Roo.Element.DISPLAY);
15693         this.cancelBtn.setVisibilityMode(Roo.Element.DISPLAY);
15694         
15695         this.okBtn.hide();
15696         this.cancelBtn.hide();
15697         
15698         var _this = this;
15699         
15700         (function(){
15701             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
15702             _this.list.setWidth(lw);
15703         }).defer(100);
15704         
15705         this.list.on('mouseover', this.onViewOver, this);
15706         this.list.on('mousemove', this.onViewMove, this);
15707         
15708         this.list.on('scroll', this.onViewScroll, this);
15709         
15710         if(!this.tpl){
15711             this.tpl = '<li class="roo-select2-result"><div class="checkbox"><input id="{roo-id}"' + 
15712                 'type="checkbox" {roo-data-checked}><label for="{roo-id}"><b>{' + this.displayField + '}</b></label></div></li>';
15713         }
15714
15715         this.view = new Roo.View(this.list, this.tpl, {
15716             singleSelect:true,
15717             tickable:true,
15718             parent:this,
15719             store: this.store,
15720             selectedClass: this.selectedClass
15721         });
15722         
15723         //this.view.wrapEl.setDisplayed(false);
15724         this.view.on('click', this.onViewClick, this);
15725         
15726         
15727         
15728         this.store.on('beforeload', this.onBeforeLoad, this);
15729         this.store.on('load', this.onLoad, this);
15730         this.store.on('loadexception', this.onLoadException, this);
15731         
15732         if(this.editable){
15733             this.keyNav = new Roo.KeyNav(this.tickableInputEl(), {
15734                 "up" : function(e){
15735                     this.inKeyMode = true;
15736                     this.selectPrev();
15737                 },
15738
15739                 "down" : function(e){
15740                     this.inKeyMode = true;
15741                     this.selectNext();
15742                 },
15743
15744                 "enter" : function(e){
15745                     if(this.fireEvent("specialkey", this, e)){
15746                         this.onViewClick(false);
15747                     }
15748                     
15749                     return true;
15750                 },
15751
15752                 "esc" : function(e){
15753                     this.onTickableFooterButtonClick(e, false, false);
15754                 },
15755
15756                 "tab" : function(e){
15757                     this.fireEvent("specialkey", this, e);
15758                     
15759                     this.onTickableFooterButtonClick(e, false, false);
15760                     
15761                     return true;
15762                 },
15763
15764                 scope : this,
15765
15766                 doRelay : function(e, fn, key){
15767                     if(this.scope.isExpanded()){
15768                        return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
15769                     }
15770                     return true;
15771                 },
15772
15773                 forceKeyDown: true
15774             });
15775         }
15776         
15777         this.queryDelay = Math.max(this.queryDelay || 10,
15778                 this.mode == 'local' ? 10 : 250);
15779         
15780         
15781         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
15782         
15783         if(this.typeAhead){
15784             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
15785         }
15786         
15787         if(this.editable !== false){
15788             this.tickableInputEl().on("keyup", this.onKeyUp, this);
15789         }
15790         
15791         this.indicator = this.indicatorEl();
15792         
15793         if(this.indicator){
15794             this.indicator.setVisibilityMode(Roo.Element.DISPLAY);
15795             this.indicator.hide();
15796         }
15797         
15798     },
15799
15800     onDestroy : function(){
15801         if(this.view){
15802             this.view.setStore(null);
15803             this.view.el.removeAllListeners();
15804             this.view.el.remove();
15805             this.view.purgeListeners();
15806         }
15807         if(this.list){
15808             this.list.dom.innerHTML  = '';
15809         }
15810         
15811         if(this.store){
15812             this.store.un('beforeload', this.onBeforeLoad, this);
15813             this.store.un('load', this.onLoad, this);
15814             this.store.un('loadexception', this.onLoadException, this);
15815         }
15816         Roo.bootstrap.ComboBox.superclass.onDestroy.call(this);
15817     },
15818
15819     // private
15820     fireKey : function(e){
15821         if(e.isNavKeyPress() && !this.list.isVisible()){
15822             this.fireEvent("specialkey", this, e);
15823         }
15824     },
15825
15826     // private
15827     onResize: function(w, h)
15828     {
15829         
15830         
15831 //        Roo.bootstrap.ComboBox.superclass.onResize.apply(this, arguments);
15832 //        
15833 //        if(typeof w != 'number'){
15834 //            // we do not handle it!?!?
15835 //            return;
15836 //        }
15837 //        var tw = this.trigger.getWidth();
15838 //       // tw += this.addicon ? this.addicon.getWidth() : 0;
15839 //       // tw += this.editicon ? this.editicon.getWidth() : 0;
15840 //        var x = w - tw;
15841 //        this.inputEl().setWidth( this.adjustWidth('input', x));
15842 //            
15843 //        //this.trigger.setStyle('left', x+'px');
15844 //        
15845 //        if(this.list && this.listWidth === undefined){
15846 //            var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
15847 //            this.list.setWidth(lw);
15848 //            this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
15849 //        }
15850         
15851     
15852         
15853     },
15854
15855     /**
15856      * Allow or prevent the user from directly editing the field text.  If false is passed,
15857      * the user will only be able to select from the items defined in the dropdown list.  This method
15858      * is the runtime equivalent of setting the 'editable' config option at config time.
15859      * @param {Boolean} value True to allow the user to directly edit the field text
15860      */
15861     setEditable : function(value){
15862         if(value == this.editable){
15863             return;
15864         }
15865         this.editable = value;
15866         if(!value){
15867             this.inputEl().dom.setAttribute('readOnly', true);
15868             this.inputEl().on('mousedown', this.onTriggerClick,  this);
15869             this.inputEl().addClass('x-combo-noedit');
15870         }else{
15871             this.inputEl().dom.setAttribute('readOnly', false);
15872             this.inputEl().un('mousedown', this.onTriggerClick,  this);
15873             this.inputEl().removeClass('x-combo-noedit');
15874         }
15875     },
15876
15877     // private
15878     
15879     onBeforeLoad : function(combo,opts){
15880         if(!this.hasFocus){
15881             return;
15882         }
15883          if (!opts.add) {
15884             this.list.dom.innerHTML = '<li class="loading-indicator">'+(this.loadingText||'loading')+'</li>' ;
15885          }
15886         this.restrictHeight();
15887         this.selectedIndex = -1;
15888     },
15889
15890     // private
15891     onLoad : function(){
15892         
15893         this.hasQuery = false;
15894         
15895         if(!this.hasFocus){
15896             return;
15897         }
15898         
15899         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
15900             this.loading.hide();
15901         }
15902         
15903         if(this.store.getCount() > 0){
15904             
15905             this.expand();
15906             this.restrictHeight();
15907             if(this.lastQuery == this.allQuery){
15908                 if(this.editable && !this.tickable){
15909                     this.inputEl().dom.select();
15910                 }
15911                 
15912                 if(
15913                     !this.selectByValue(this.value, true) &&
15914                     this.autoFocus && 
15915                     (
15916                         !this.store.lastOptions ||
15917                         typeof(this.store.lastOptions.add) == 'undefined' || 
15918                         this.store.lastOptions.add != true
15919                     )
15920                 ){
15921                     this.select(0, true);
15922                 }
15923             }else{
15924                 if(this.autoFocus){
15925                     this.selectNext();
15926                 }
15927                 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
15928                     this.taTask.delay(this.typeAheadDelay);
15929                 }
15930             }
15931         }else{
15932             this.onEmptyResults();
15933         }
15934         
15935         //this.el.focus();
15936     },
15937     // private
15938     onLoadException : function()
15939     {
15940         this.hasQuery = false;
15941         
15942         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
15943             this.loading.hide();
15944         }
15945         
15946         if(this.tickable && this.editable){
15947             return;
15948         }
15949         
15950         this.collapse();
15951         // only causes errors at present
15952         //Roo.log(this.store.reader.jsonData);
15953         //if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
15954             // fixme
15955             //Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
15956         //}
15957         
15958         
15959     },
15960     // private
15961     onTypeAhead : function(){
15962         if(this.store.getCount() > 0){
15963             var r = this.store.getAt(0);
15964             var newValue = r.data[this.displayField];
15965             var len = newValue.length;
15966             var selStart = this.getRawValue().length;
15967             
15968             if(selStart != len){
15969                 this.setRawValue(newValue);
15970                 this.selectText(selStart, newValue.length);
15971             }
15972         }
15973     },
15974
15975     // private
15976     onSelect : function(record, index){
15977         
15978         if(this.fireEvent('beforeselect', this, record, index) !== false){
15979         
15980             this.setFromData(index > -1 ? record.data : false);
15981             
15982             this.collapse();
15983             this.fireEvent('select', this, record, index);
15984         }
15985     },
15986
15987     /**
15988      * Returns the currently selected field value or empty string if no value is set.
15989      * @return {String} value The selected value
15990      */
15991     getValue : function()
15992     {
15993         if(Roo.isIOS && this.useNativeIOS){
15994             return this.ios_options[this.inputEl().dom.selectedIndex].data[this.valueField];
15995         }
15996         
15997         if(this.multiple){
15998             return (this.hiddenField) ? this.hiddenField.dom.value : this.value;
15999         }
16000         
16001         if(this.valueField){
16002             return typeof this.value != 'undefined' ? this.value : '';
16003         }else{
16004             return Roo.bootstrap.ComboBox.superclass.getValue.call(this);
16005         }
16006     },
16007     
16008     getRawValue : function()
16009     {
16010         if(Roo.isIOS && this.useNativeIOS){
16011             return this.ios_options[this.inputEl().dom.selectedIndex].data[this.displayField];
16012         }
16013         
16014         var v = this.inputEl().getValue();
16015         
16016         return v;
16017     },
16018
16019     /**
16020      * Clears any text/value currently set in the field
16021      */
16022     clearValue : function(){
16023         
16024         if(this.hiddenField){
16025             this.hiddenField.dom.value = '';
16026         }
16027         this.value = '';
16028         this.setRawValue('');
16029         this.lastSelectionText = '';
16030         this.lastData = false;
16031         
16032         var close = this.closeTriggerEl();
16033         
16034         if(close){
16035             close.hide();
16036         }
16037         
16038         this.validate();
16039         
16040     },
16041
16042     /**
16043      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
16044      * will be displayed in the field.  If the value does not match the data value of an existing item,
16045      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
16046      * Otherwise the field will be blank (although the value will still be set).
16047      * @param {String} value The value to match
16048      */
16049     setValue : function(v)
16050     {
16051         if(Roo.isIOS && this.useNativeIOS){
16052             this.setIOSValue(v);
16053             return;
16054         }
16055         
16056         if(this.multiple){
16057             this.syncValue();
16058             return;
16059         }
16060         
16061         var text = v;
16062         if(this.valueField){
16063             var r = this.findRecord(this.valueField, v);
16064             if(r){
16065                 text = r.data[this.displayField];
16066             }else if(this.valueNotFoundText !== undefined){
16067                 text = this.valueNotFoundText;
16068             }
16069         }
16070         this.lastSelectionText = text;
16071         if(this.hiddenField){
16072             this.hiddenField.dom.value = v;
16073         }
16074         Roo.bootstrap.ComboBox.superclass.setValue.call(this, text);
16075         this.value = v;
16076         
16077         var close = this.closeTriggerEl();
16078         
16079         if(close){
16080             (v && (v.length || v * 1 > 0)) ? close.show() : close.hide();
16081         }
16082         
16083         this.validate();
16084     },
16085     /**
16086      * @property {Object} the last set data for the element
16087      */
16088     
16089     lastData : false,
16090     /**
16091      * Sets the value of the field based on a object which is related to the record format for the store.
16092      * @param {Object} value the value to set as. or false on reset?
16093      */
16094     setFromData : function(o){
16095         
16096         if(this.multiple){
16097             this.addItem(o);
16098             return;
16099         }
16100             
16101         var dv = ''; // display value
16102         var vv = ''; // value value..
16103         this.lastData = o;
16104         if (this.displayField) {
16105             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
16106         } else {
16107             // this is an error condition!!!
16108             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
16109         }
16110         
16111         if(this.valueField){
16112             vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
16113         }
16114         
16115         var close = this.closeTriggerEl();
16116         
16117         if(close){
16118             if(dv.length || vv * 1 > 0){
16119                 close.show() ;
16120                 this.blockFocus=true;
16121             } else {
16122                 close.hide();
16123             }             
16124         }
16125         
16126         if(this.hiddenField){
16127             this.hiddenField.dom.value = vv;
16128             
16129             this.lastSelectionText = dv;
16130             Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
16131             this.value = vv;
16132             return;
16133         }
16134         // no hidden field.. - we store the value in 'value', but still display
16135         // display field!!!!
16136         this.lastSelectionText = dv;
16137         Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
16138         this.value = vv;
16139         
16140         
16141         
16142     },
16143     // private
16144     reset : function(){
16145         // overridden so that last data is reset..
16146         
16147         if(this.multiple){
16148             this.clearItem();
16149             return;
16150         }
16151         
16152         this.setValue(this.originalValue);
16153         //this.clearInvalid();
16154         this.lastData = false;
16155         if (this.view) {
16156             this.view.clearSelections();
16157         }
16158         
16159         this.validate();
16160     },
16161     // private
16162     findRecord : function(prop, value){
16163         var record;
16164         if(this.store.getCount() > 0){
16165             this.store.each(function(r){
16166                 if(r.data[prop] == value){
16167                     record = r;
16168                     return false;
16169                 }
16170                 return true;
16171             });
16172         }
16173         return record;
16174     },
16175     
16176     getName: function()
16177     {
16178         // returns hidden if it's set..
16179         if (!this.rendered) {return ''};
16180         return !this.hiddenName && this.inputEl().dom.name  ? this.inputEl().dom.name : (this.hiddenName || '');
16181         
16182     },
16183     // private
16184     onViewMove : function(e, t){
16185         this.inKeyMode = false;
16186     },
16187
16188     // private
16189     onViewOver : function(e, t){
16190         if(this.inKeyMode){ // prevent key nav and mouse over conflicts
16191             return;
16192         }
16193         var item = this.view.findItemFromChild(t);
16194         
16195         if(item){
16196             var index = this.view.indexOf(item);
16197             this.select(index, false);
16198         }
16199     },
16200
16201     // private
16202     onViewClick : function(view, doFocus, el, e)
16203     {
16204         var index = this.view.getSelectedIndexes()[0];
16205         
16206         var r = this.store.getAt(index);
16207         
16208         if(this.tickable){
16209             
16210             if(typeof(e) != 'undefined' && e.getTarget().nodeName.toLowerCase() != 'input'){
16211                 return;
16212             }
16213             
16214             var rm = false;
16215             var _this = this;
16216             
16217             Roo.each(this.tickItems, function(v,k){
16218                 
16219                 if(typeof(v) != 'undefined' && v[_this.valueField] == r.data[_this.valueField]){
16220                     Roo.log(v);
16221                     _this.tickItems.splice(k, 1);
16222                     
16223                     if(typeof(e) == 'undefined' && view == false){
16224                         Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = false;
16225                     }
16226                     
16227                     rm = true;
16228                     return;
16229                 }
16230             });
16231             
16232             if(rm){
16233                 return;
16234             }
16235             
16236             if(this.fireEvent('tick', this, r, index, Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked) !== false){
16237                 this.tickItems.push(r.data);
16238             }
16239             
16240             if(typeof(e) == 'undefined' && view == false){
16241                 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = true;
16242             }
16243                     
16244             return;
16245         }
16246         
16247         if(r){
16248             this.onSelect(r, index);
16249         }
16250         if(doFocus !== false && !this.blockFocus){
16251             this.inputEl().focus();
16252         }
16253     },
16254
16255     // private
16256     restrictHeight : function(){
16257         //this.innerList.dom.style.height = '';
16258         //var inner = this.innerList.dom;
16259         //var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
16260         //this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
16261         //this.list.beginUpdate();
16262         //this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
16263         this.list.alignTo(this.inputEl(), this.listAlign);
16264         this.list.alignTo(this.inputEl(), this.listAlign);
16265         //this.list.endUpdate();
16266     },
16267
16268     // private
16269     onEmptyResults : function(){
16270         
16271         if(this.tickable && this.editable){
16272             this.hasFocus = false;
16273             this.restrictHeight();
16274             return;
16275         }
16276         
16277         this.collapse();
16278     },
16279
16280     /**
16281      * Returns true if the dropdown list is expanded, else false.
16282      */
16283     isExpanded : function(){
16284         return this.list.isVisible();
16285     },
16286
16287     /**
16288      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
16289      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
16290      * @param {String} value The data value of the item to select
16291      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
16292      * selected item if it is not currently in view (defaults to true)
16293      * @return {Boolean} True if the value matched an item in the list, else false
16294      */
16295     selectByValue : function(v, scrollIntoView){
16296         if(v !== undefined && v !== null){
16297             var r = this.findRecord(this.valueField || this.displayField, v);
16298             if(r){
16299                 this.select(this.store.indexOf(r), scrollIntoView);
16300                 return true;
16301             }
16302         }
16303         return false;
16304     },
16305
16306     /**
16307      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
16308      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
16309      * @param {Number} index The zero-based index of the list item to select
16310      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
16311      * selected item if it is not currently in view (defaults to true)
16312      */
16313     select : function(index, scrollIntoView){
16314         this.selectedIndex = index;
16315         this.view.select(index);
16316         if(scrollIntoView !== false){
16317             var el = this.view.getNode(index);
16318             /*
16319              * el && !this.multiple && !this.tickable // not sure why we disable multiple before..
16320              */
16321             if(el){
16322                 this.list.scrollChildIntoView(el, false);
16323             }
16324         }
16325     },
16326
16327     // private
16328     selectNext : function(){
16329         var ct = this.store.getCount();
16330         if(ct > 0){
16331             if(this.selectedIndex == -1){
16332                 this.select(0);
16333             }else if(this.selectedIndex < ct-1){
16334                 this.select(this.selectedIndex+1);
16335             }
16336         }
16337     },
16338
16339     // private
16340     selectPrev : function(){
16341         var ct = this.store.getCount();
16342         if(ct > 0){
16343             if(this.selectedIndex == -1){
16344                 this.select(0);
16345             }else if(this.selectedIndex != 0){
16346                 this.select(this.selectedIndex-1);
16347             }
16348         }
16349     },
16350
16351     // private
16352     onKeyUp : function(e){
16353         if(this.editable !== false && !e.isSpecialKey()){
16354             this.lastKey = e.getKey();
16355             this.dqTask.delay(this.queryDelay);
16356         }
16357     },
16358
16359     // private
16360     validateBlur : function(){
16361         return !this.list || !this.list.isVisible();   
16362     },
16363
16364     // private
16365     initQuery : function(){
16366         
16367         var v = this.getRawValue();
16368         
16369         if(this.tickable && this.editable){
16370             v = this.tickableInputEl().getValue();
16371         }
16372         
16373         this.doQuery(v);
16374     },
16375
16376     // private
16377     doForce : function(){
16378         if(this.inputEl().dom.value.length > 0){
16379             this.inputEl().dom.value =
16380                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
16381              
16382         }
16383     },
16384
16385     /**
16386      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
16387      * query allowing the query action to be canceled if needed.
16388      * @param {String} query The SQL query to execute
16389      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
16390      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
16391      * saved in the current store (defaults to false)
16392      */
16393     doQuery : function(q, forceAll){
16394         
16395         if(q === undefined || q === null){
16396             q = '';
16397         }
16398         var qe = {
16399             query: q,
16400             forceAll: forceAll,
16401             combo: this,
16402             cancel:false
16403         };
16404         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
16405             return false;
16406         }
16407         q = qe.query;
16408         
16409         forceAll = qe.forceAll;
16410         if(forceAll === true || (q.length >= this.minChars)){
16411             
16412             this.hasQuery = true;
16413             
16414             if(this.lastQuery != q || this.alwaysQuery){
16415                 this.lastQuery = q;
16416                 if(this.mode == 'local'){
16417                     this.selectedIndex = -1;
16418                     if(forceAll){
16419                         this.store.clearFilter();
16420                     }else{
16421                         
16422                         if(this.specialFilter){
16423                             this.fireEvent('specialfilter', this);
16424                             this.onLoad();
16425                             return;
16426                         }
16427                         
16428                         this.store.filter(this.displayField, q);
16429                     }
16430                     
16431                     this.store.fireEvent("datachanged", this.store);
16432                     
16433                     this.onLoad();
16434                     
16435                     
16436                 }else{
16437                     
16438                     this.store.baseParams[this.queryParam] = q;
16439                     
16440                     var options = {params : this.getParams(q)};
16441                     
16442                     if(this.loadNext){
16443                         options.add = true;
16444                         options.params.start = this.page * this.pageSize;
16445                     }
16446                     
16447                     this.store.load(options);
16448                     
16449                     /*
16450                      *  this code will make the page width larger, at the beginning, the list not align correctly, 
16451                      *  we should expand the list on onLoad
16452                      *  so command out it
16453                      */
16454 //                    this.expand();
16455                 }
16456             }else{
16457                 this.selectedIndex = -1;
16458                 this.onLoad();   
16459             }
16460         }
16461         
16462         this.loadNext = false;
16463     },
16464     
16465     // private
16466     getParams : function(q){
16467         var p = {};
16468         //p[this.queryParam] = q;
16469         
16470         if(this.pageSize){
16471             p.start = 0;
16472             p.limit = this.pageSize;
16473         }
16474         return p;
16475     },
16476
16477     /**
16478      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
16479      */
16480     collapse : function(){
16481         if(!this.isExpanded()){
16482             return;
16483         }
16484         
16485         this.list.hide();
16486         
16487         this.hasFocus = false;
16488         
16489         if(this.tickable){
16490             this.okBtn.hide();
16491             this.cancelBtn.hide();
16492             this.trigger.show();
16493             
16494             if(this.editable){
16495                 this.tickableInputEl().dom.value = '';
16496                 this.tickableInputEl().blur();
16497             }
16498             
16499         }
16500         
16501         Roo.get(document).un('mousedown', this.collapseIf, this);
16502         Roo.get(document).un('mousewheel', this.collapseIf, this);
16503         if (!this.editable) {
16504             Roo.get(document).un('keydown', this.listKeyPress, this);
16505         }
16506         this.fireEvent('collapse', this);
16507         
16508         this.validate();
16509     },
16510
16511     // private
16512     collapseIf : function(e){
16513         var in_combo  = e.within(this.el);
16514         var in_list =  e.within(this.list);
16515         var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
16516         
16517         if (in_combo || in_list || is_list) {
16518             //e.stopPropagation();
16519             return;
16520         }
16521         
16522         if(this.tickable){
16523             this.onTickableFooterButtonClick(e, false, false);
16524         }
16525
16526         this.collapse();
16527         
16528     },
16529
16530     /**
16531      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
16532      */
16533     expand : function(){
16534        
16535         if(this.isExpanded() || !this.hasFocus){
16536             return;
16537         }
16538         
16539         var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
16540         this.list.setWidth(lw);
16541         
16542         Roo.log('expand');
16543         
16544         this.list.show();
16545         
16546         this.restrictHeight();
16547         
16548         if(this.tickable){
16549             
16550             this.tickItems = Roo.apply([], this.item);
16551             
16552             this.okBtn.show();
16553             this.cancelBtn.show();
16554             this.trigger.hide();
16555             
16556             if(this.editable){
16557                 this.tickableInputEl().focus();
16558             }
16559             
16560         }
16561         
16562         Roo.get(document).on('mousedown', this.collapseIf, this);
16563         Roo.get(document).on('mousewheel', this.collapseIf, this);
16564         if (!this.editable) {
16565             Roo.get(document).on('keydown', this.listKeyPress, this);
16566         }
16567         
16568         this.fireEvent('expand', this);
16569     },
16570
16571     // private
16572     // Implements the default empty TriggerField.onTriggerClick function
16573     onTriggerClick : function(e)
16574     {
16575         Roo.log('trigger click');
16576         
16577         if(this.disabled || !this.triggerList){
16578             return;
16579         }
16580         
16581         this.page = 0;
16582         this.loadNext = false;
16583         
16584         if(this.isExpanded()){
16585             this.collapse();
16586             if (!this.blockFocus) {
16587                 this.inputEl().focus();
16588             }
16589             
16590         }else {
16591             this.hasFocus = true;
16592             if(this.triggerAction == 'all') {
16593                 this.doQuery(this.allQuery, true);
16594             } else {
16595                 this.doQuery(this.getRawValue());
16596             }
16597             if (!this.blockFocus) {
16598                 this.inputEl().focus();
16599             }
16600         }
16601     },
16602     
16603     onTickableTriggerClick : function(e)
16604     {
16605         if(this.disabled){
16606             return;
16607         }
16608         
16609         this.page = 0;
16610         this.loadNext = false;
16611         this.hasFocus = true;
16612         
16613         if(this.triggerAction == 'all') {
16614             this.doQuery(this.allQuery, true);
16615         } else {
16616             this.doQuery(this.getRawValue());
16617         }
16618     },
16619     
16620     onSearchFieldClick : function(e)
16621     {
16622         if(this.hasFocus && !this.disabled && e.getTarget().nodeName.toLowerCase() != 'button'){
16623             this.onTickableFooterButtonClick(e, false, false);
16624             return;
16625         }
16626         
16627         if(this.hasFocus || this.disabled || e.getTarget().nodeName.toLowerCase() == 'button'){
16628             return;
16629         }
16630         
16631         this.page = 0;
16632         this.loadNext = false;
16633         this.hasFocus = true;
16634         
16635         if(this.triggerAction == 'all') {
16636             this.doQuery(this.allQuery, true);
16637         } else {
16638             this.doQuery(this.getRawValue());
16639         }
16640     },
16641     
16642     listKeyPress : function(e)
16643     {
16644         //Roo.log('listkeypress');
16645         // scroll to first matching element based on key pres..
16646         if (e.isSpecialKey()) {
16647             return false;
16648         }
16649         var k = String.fromCharCode(e.getKey()).toUpperCase();
16650         //Roo.log(k);
16651         var match  = false;
16652         var csel = this.view.getSelectedNodes();
16653         var cselitem = false;
16654         if (csel.length) {
16655             var ix = this.view.indexOf(csel[0]);
16656             cselitem  = this.store.getAt(ix);
16657             if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
16658                 cselitem = false;
16659             }
16660             
16661         }
16662         
16663         this.store.each(function(v) { 
16664             if (cselitem) {
16665                 // start at existing selection.
16666                 if (cselitem.id == v.id) {
16667                     cselitem = false;
16668                 }
16669                 return true;
16670             }
16671                 
16672             if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
16673                 match = this.store.indexOf(v);
16674                 return false;
16675             }
16676             return true;
16677         }, this);
16678         
16679         if (match === false) {
16680             return true; // no more action?
16681         }
16682         // scroll to?
16683         this.view.select(match);
16684         var sn = Roo.get(this.view.getSelectedNodes()[0]);
16685         sn.scrollIntoView(sn.dom.parentNode, false);
16686     },
16687     
16688     onViewScroll : function(e, t){
16689         
16690         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){
16691             return;
16692         }
16693         
16694         this.hasQuery = true;
16695         
16696         this.loading = this.list.select('.loading', true).first();
16697         
16698         if(this.loading === null){
16699             this.list.createChild({
16700                 tag: 'div',
16701                 cls: 'loading roo-select2-more-results roo-select2-active',
16702                 html: 'Loading more results...'
16703             });
16704             
16705             this.loading = this.list.select('.loading', true).first();
16706             
16707             this.loading.setVisibilityMode(Roo.Element.DISPLAY);
16708             
16709             this.loading.hide();
16710         }
16711         
16712         this.loading.show();
16713         
16714         var _combo = this;
16715         
16716         this.page++;
16717         this.loadNext = true;
16718         
16719         (function() { _combo.doQuery(_combo.allQuery, true); }).defer(500);
16720         
16721         return;
16722     },
16723     
16724     addItem : function(o)
16725     {   
16726         var dv = ''; // display value
16727         
16728         if (this.displayField) {
16729             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
16730         } else {
16731             // this is an error condition!!!
16732             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
16733         }
16734         
16735         if(!dv.length){
16736             return;
16737         }
16738         
16739         var choice = this.choices.createChild({
16740             tag: 'li',
16741             cls: 'roo-select2-search-choice',
16742             cn: [
16743                 {
16744                     tag: 'div',
16745                     html: dv
16746                 },
16747                 {
16748                     tag: 'a',
16749                     href: '#',
16750                     cls: 'roo-select2-search-choice-close fa fa-times',
16751                     tabindex: '-1'
16752                 }
16753             ]
16754             
16755         }, this.searchField);
16756         
16757         var close = choice.select('a.roo-select2-search-choice-close', true).first();
16758         
16759         close.on('click', this.onRemoveItem, this, { item : choice, data : o} );
16760         
16761         this.item.push(o);
16762         
16763         this.lastData = o;
16764         
16765         this.syncValue();
16766         
16767         this.inputEl().dom.value = '';
16768         
16769         this.validate();
16770     },
16771     
16772     onRemoveItem : function(e, _self, o)
16773     {
16774         e.preventDefault();
16775         
16776         this.lastItem = Roo.apply([], this.item);
16777         
16778         var index = this.item.indexOf(o.data) * 1;
16779         
16780         if( index < 0){
16781             Roo.log('not this item?!');
16782             return;
16783         }
16784         
16785         this.item.splice(index, 1);
16786         o.item.remove();
16787         
16788         this.syncValue();
16789         
16790         this.fireEvent('remove', this, e);
16791         
16792         this.validate();
16793         
16794     },
16795     
16796     syncValue : function()
16797     {
16798         if(!this.item.length){
16799             this.clearValue();
16800             return;
16801         }
16802             
16803         var value = [];
16804         var _this = this;
16805         Roo.each(this.item, function(i){
16806             if(_this.valueField){
16807                 value.push(i[_this.valueField]);
16808                 return;
16809             }
16810
16811             value.push(i);
16812         });
16813
16814         this.value = value.join(',');
16815
16816         if(this.hiddenField){
16817             this.hiddenField.dom.value = this.value;
16818         }
16819         
16820         this.store.fireEvent("datachanged", this.store);
16821         
16822         this.validate();
16823     },
16824     
16825     clearItem : function()
16826     {
16827         if(!this.multiple){
16828             return;
16829         }
16830         
16831         this.item = [];
16832         
16833         Roo.each(this.choices.select('>li.roo-select2-search-choice', true).elements, function(c){
16834            c.remove();
16835         });
16836         
16837         this.syncValue();
16838         
16839         this.validate();
16840         
16841         if(this.tickable && !Roo.isTouch){
16842             this.view.refresh();
16843         }
16844     },
16845     
16846     inputEl: function ()
16847     {
16848         if(Roo.isIOS && this.useNativeIOS){
16849             return this.el.select('select.roo-ios-select', true).first();
16850         }
16851         
16852         if(Roo.isTouch && this.mobileTouchView){
16853             return this.el.select('input.form-control',true).first();
16854         }
16855         
16856         if(this.tickable){
16857             return this.searchField;
16858         }
16859         
16860         return this.el.select('input.form-control',true).first();
16861     },
16862     
16863     onTickableFooterButtonClick : function(e, btn, el)
16864     {
16865         e.preventDefault();
16866         
16867         this.lastItem = Roo.apply([], this.item);
16868         
16869         if(btn && btn.name == 'cancel'){
16870             this.tickItems = Roo.apply([], this.item);
16871             this.collapse();
16872             return;
16873         }
16874         
16875         this.clearItem();
16876         
16877         var _this = this;
16878         
16879         Roo.each(this.tickItems, function(o){
16880             _this.addItem(o);
16881         });
16882         
16883         this.collapse();
16884         
16885     },
16886     
16887     validate : function()
16888     {
16889         if(this.getVisibilityEl().hasClass('hidden')){
16890             return true;
16891         }
16892         
16893         var v = this.getRawValue();
16894         
16895         if(this.multiple){
16896             v = this.getValue();
16897         }
16898         
16899         if(this.disabled || this.allowBlank || v.length){
16900             this.markValid();
16901             return true;
16902         }
16903         
16904         this.markInvalid();
16905         return false;
16906     },
16907     
16908     tickableInputEl : function()
16909     {
16910         if(!this.tickable || !this.editable){
16911             return this.inputEl();
16912         }
16913         
16914         return this.inputEl().select('.roo-select2-search-field-input', true).first();
16915     },
16916     
16917     
16918     getAutoCreateTouchView : function()
16919     {
16920         var id = Roo.id();
16921         
16922         var cfg = {
16923             cls: 'form-group' //input-group
16924         };
16925         
16926         var input =  {
16927             tag: 'input',
16928             id : id,
16929             type : this.inputType,
16930             cls : 'form-control x-combo-noedit',
16931             autocomplete: 'new-password',
16932             placeholder : this.placeholder || '',
16933             readonly : true
16934         };
16935         
16936         if (this.name) {
16937             input.name = this.name;
16938         }
16939         
16940         if (this.size) {
16941             input.cls += ' input-' + this.size;
16942         }
16943         
16944         if (this.disabled) {
16945             input.disabled = true;
16946         }
16947         
16948         var inputblock = {
16949             cls : 'roo-combobox-wrap',
16950             cn : [
16951                 input
16952             ]
16953         };
16954         
16955         if(this.before){
16956             inputblock.cls += ' input-group';
16957             
16958             inputblock.cn.unshift({
16959                 tag :'span',
16960                 cls : 'input-group-addon input-group-prepend input-group-text',
16961                 html : this.before
16962             });
16963         }
16964         
16965         if(this.removable && !this.multiple){
16966             inputblock.cls += ' roo-removable';
16967             
16968             inputblock.cn.push({
16969                 tag: 'button',
16970                 html : 'x',
16971                 cls : 'roo-combo-removable-btn close'
16972             });
16973         }
16974
16975         if(this.hasFeedback && !this.allowBlank){
16976             
16977             inputblock.cls += ' has-feedback';
16978             
16979             inputblock.cn.push({
16980                 tag: 'span',
16981                 cls: 'glyphicon form-control-feedback'
16982             });
16983             
16984         }
16985         
16986         if (this.after) {
16987             
16988             inputblock.cls += (this.before) ? '' : ' input-group';
16989             
16990             inputblock.cn.push({
16991                 tag :'span',
16992                 cls : 'input-group-addon input-group-append input-group-text',
16993                 html : this.after
16994             });
16995         }
16996
16997         
16998         var ibwrap = inputblock;
16999         
17000         if(this.multiple){
17001             ibwrap = {
17002                 tag: 'ul',
17003                 cls: 'roo-select2-choices',
17004                 cn:[
17005                     {
17006                         tag: 'li',
17007                         cls: 'roo-select2-search-field',
17008                         cn: [
17009
17010                             inputblock
17011                         ]
17012                     }
17013                 ]
17014             };
17015         
17016             
17017         }
17018         
17019         var combobox = {
17020             cls: 'roo-select2-container input-group roo-touchview-combobox ',
17021             cn: [
17022                 {
17023                     tag: 'input',
17024                     type : 'hidden',
17025                     cls: 'form-hidden-field'
17026                 },
17027                 ibwrap
17028             ]
17029         };
17030         
17031         if(!this.multiple && this.showToggleBtn){
17032             
17033             var caret = {
17034                 cls: 'caret'
17035             };
17036             
17037             if (this.caret != false) {
17038                 caret = {
17039                      tag: 'i',
17040                      cls: 'fa fa-' + this.caret
17041                 };
17042                 
17043             }
17044             
17045             combobox.cn.push({
17046                 tag :'span',
17047                 cls : 'input-group-addon input-group-append input-group-text btn dropdown-toggle',
17048                 cn : [
17049                     Roo.bootstrap.version == 3 ? caret : '',
17050                     {
17051                         tag: 'span',
17052                         cls: 'combobox-clear',
17053                         cn  : [
17054                             {
17055                                 tag : 'i',
17056                                 cls: 'icon-remove'
17057                             }
17058                         ]
17059                     }
17060                 ]
17061
17062             })
17063         }
17064         
17065         if(this.multiple){
17066             combobox.cls += ' roo-select2-container-multi';
17067         }
17068         
17069         var align = this.labelAlign || this.parentLabelAlign();
17070         
17071         if (align ==='left' && this.fieldLabel.length) {
17072
17073             cfg.cn = [
17074                 {
17075                    tag : 'i',
17076                    cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
17077                    tooltip : 'This field is required'
17078                 },
17079                 {
17080                     tag: 'label',
17081                     cls : 'control-label col-form-label',
17082                     html : this.fieldLabel
17083
17084                 },
17085                 {
17086                     cls : 'roo-combobox-wrap ', 
17087                     cn: [
17088                         combobox
17089                     ]
17090                 }
17091             ];
17092             
17093             var labelCfg = cfg.cn[1];
17094             var contentCfg = cfg.cn[2];
17095             
17096
17097             if(this.indicatorpos == 'right'){
17098                 cfg.cn = [
17099                     {
17100                         tag: 'label',
17101                         'for' :  id,
17102                         cls : 'control-label col-form-label',
17103                         cn : [
17104                             {
17105                                 tag : 'span',
17106                                 html : this.fieldLabel
17107                             },
17108                             {
17109                                 tag : 'i',
17110                                 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
17111                                 tooltip : 'This field is required'
17112                             }
17113                         ]
17114                     },
17115                     {
17116                         cls : "roo-combobox-wrap ",
17117                         cn: [
17118                             combobox
17119                         ]
17120                     }
17121
17122                 ];
17123                 
17124                 labelCfg = cfg.cn[0];
17125                 contentCfg = cfg.cn[1];
17126             }
17127             
17128            
17129             
17130             if(this.labelWidth > 12){
17131                 labelCfg.style = "width: " + this.labelWidth + 'px';
17132             }
17133            
17134             if(this.labelWidth < 13 && this.labelmd == 0){
17135                 this.labelmd = this.labelWidth;
17136             }
17137             
17138             if(this.labellg > 0){
17139                 labelCfg.cls += ' col-lg-' + this.labellg;
17140                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
17141             }
17142             
17143             if(this.labelmd > 0){
17144                 labelCfg.cls += ' col-md-' + this.labelmd;
17145                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
17146             }
17147             
17148             if(this.labelsm > 0){
17149                 labelCfg.cls += ' col-sm-' + this.labelsm;
17150                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
17151             }
17152             
17153             if(this.labelxs > 0){
17154                 labelCfg.cls += ' col-xs-' + this.labelxs;
17155                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
17156             }
17157                 
17158                 
17159         } else if ( this.fieldLabel.length) {
17160             cfg.cn = [
17161                 {
17162                    tag : 'i',
17163                    cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
17164                    tooltip : 'This field is required'
17165                 },
17166                 {
17167                     tag: 'label',
17168                     cls : 'control-label',
17169                     html : this.fieldLabel
17170
17171                 },
17172                 {
17173                     cls : '', 
17174                     cn: [
17175                         combobox
17176                     ]
17177                 }
17178             ];
17179             
17180             if(this.indicatorpos == 'right'){
17181                 cfg.cn = [
17182                     {
17183                         tag: 'label',
17184                         cls : 'control-label',
17185                         html : this.fieldLabel,
17186                         cn : [
17187                             {
17188                                tag : 'i',
17189                                cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
17190                                tooltip : 'This field is required'
17191                             }
17192                         ]
17193                     },
17194                     {
17195                         cls : '', 
17196                         cn: [
17197                             combobox
17198                         ]
17199                     }
17200                 ];
17201             }
17202         } else {
17203             cfg.cn = combobox;    
17204         }
17205         
17206         
17207         var settings = this;
17208         
17209         ['xs','sm','md','lg'].map(function(size){
17210             if (settings[size]) {
17211                 cfg.cls += ' col-' + size + '-' + settings[size];
17212             }
17213         });
17214         
17215         return cfg;
17216     },
17217     
17218     initTouchView : function()
17219     {
17220         this.renderTouchView();
17221         
17222         this.touchViewEl.on('scroll', function(){
17223             this.el.dom.scrollTop = 0;
17224         }, this);
17225         
17226         this.originalValue = this.getValue();
17227         
17228         this.triggerEl = this.el.select('span.dropdown-toggle',true).first();
17229         
17230         this.inputEl().on("click", this.showTouchView, this);
17231         if (this.triggerEl) {
17232             this.triggerEl.on("click", this.showTouchView, this);
17233         }
17234         
17235         
17236         this.touchViewFooterEl.select('.roo-touch-view-cancel', true).first().on('click', this.hideTouchView, this);
17237         this.touchViewFooterEl.select('.roo-touch-view-ok', true).first().on('click', this.setTouchViewValue, this);
17238         
17239         this.maskEl = new Roo.LoadMask(this.touchViewEl, { store : this.store, msgCls: 'roo-el-mask-msg' });
17240         
17241         this.store.on('beforeload', this.onTouchViewBeforeLoad, this);
17242         this.store.on('load', this.onTouchViewLoad, this);
17243         this.store.on('loadexception', this.onTouchViewLoadException, this);
17244         
17245         if(this.hiddenName){
17246             
17247             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
17248             
17249             this.hiddenField.dom.value =
17250                 this.hiddenValue !== undefined ? this.hiddenValue :
17251                 this.value !== undefined ? this.value : '';
17252         
17253             this.el.dom.removeAttribute('name');
17254             this.hiddenField.dom.setAttribute('name', this.hiddenName);
17255         }
17256         
17257         if(this.multiple){
17258             this.choices = this.el.select('ul.roo-select2-choices', true).first();
17259             this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
17260         }
17261         
17262         if(this.removable && !this.multiple){
17263             var close = this.closeTriggerEl();
17264             if(close){
17265                 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
17266                 close.on('click', this.removeBtnClick, this, close);
17267             }
17268         }
17269         /*
17270          * fix the bug in Safari iOS8
17271          */
17272         this.inputEl().on("focus", function(e){
17273             document.activeElement.blur();
17274         }, this);
17275         
17276         this._touchViewMask = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
17277         
17278         return;
17279         
17280         
17281     },
17282     
17283     renderTouchView : function()
17284     {
17285         this.touchViewEl = Roo.get(document.body).createChild(Roo.bootstrap.ComboBox.touchViewTemplate);
17286         this.touchViewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17287         
17288         this.touchViewHeaderEl = this.touchViewEl.select('.modal-header', true).first();
17289         this.touchViewHeaderEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17290         
17291         this.touchViewBodyEl = this.touchViewEl.select('.modal-body', true).first();
17292         this.touchViewBodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17293         this.touchViewBodyEl.setStyle('overflow', 'auto');
17294         
17295         this.touchViewListGroup = this.touchViewBodyEl.select('.list-group', true).first();
17296         this.touchViewListGroup.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17297         
17298         this.touchViewFooterEl = this.touchViewEl.select('.modal-footer', true).first();
17299         this.touchViewFooterEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17300         
17301     },
17302     
17303     showTouchView : function()
17304     {
17305         if(this.disabled){
17306             return;
17307         }
17308         
17309         this.touchViewHeaderEl.hide();
17310
17311         if(this.modalTitle.length){
17312             this.touchViewHeaderEl.dom.innerHTML = this.modalTitle;
17313             this.touchViewHeaderEl.show();
17314         }
17315
17316         this.touchViewEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
17317         this.touchViewEl.show();
17318
17319         this.touchViewEl.select('.modal-dialog', true).first().setStyle({ margin : '0px', width : '100%'});
17320         
17321         //this.touchViewEl.select('.modal-dialog > .modal-content', true).first().setSize(
17322         //        Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
17323
17324         var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
17325
17326         if(this.modalTitle.length){
17327             bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
17328         }
17329         
17330         this.touchViewBodyEl.setHeight(bodyHeight);
17331
17332         if(this.animate){
17333             var _this = this;
17334             (function(){ _this.touchViewEl.addClass(['in','show']); }).defer(50);
17335         }else{
17336             this.touchViewEl.addClass(['in','show']);
17337         }
17338         
17339         if(this._touchViewMask){
17340             Roo.get(document.body).addClass("x-body-masked");
17341             this._touchViewMask.setSize(Roo.lib.Dom.getViewWidth(true),   Roo.lib.Dom.getViewHeight(true));
17342             this._touchViewMask.setStyle('z-index', 10000);
17343             this._touchViewMask.addClass('show');
17344         }
17345         
17346         this.doTouchViewQuery();
17347         
17348     },
17349     
17350     hideTouchView : function()
17351     {
17352         this.touchViewEl.removeClass(['in','show']);
17353
17354         if(this.animate){
17355             var _this = this;
17356             (function(){ _this.touchViewEl.setStyle('display', 'none'); }).defer(150);
17357         }else{
17358             this.touchViewEl.setStyle('display', 'none');
17359         }
17360         
17361         if(this._touchViewMask){
17362             this._touchViewMask.removeClass('show');
17363             Roo.get(document.body).removeClass("x-body-masked");
17364         }
17365     },
17366     
17367     setTouchViewValue : function()
17368     {
17369         if(this.multiple){
17370             this.clearItem();
17371         
17372             var _this = this;
17373
17374             Roo.each(this.tickItems, function(o){
17375                 this.addItem(o);
17376             }, this);
17377         }
17378         
17379         this.hideTouchView();
17380     },
17381     
17382     doTouchViewQuery : function()
17383     {
17384         var qe = {
17385             query: '',
17386             forceAll: true,
17387             combo: this,
17388             cancel:false
17389         };
17390         
17391         if(this.fireEvent('beforequery', qe) ===false || qe.cancel){
17392             return false;
17393         }
17394         
17395         if(!this.alwaysQuery || this.mode == 'local'){
17396             this.onTouchViewLoad();
17397             return;
17398         }
17399         
17400         this.store.load();
17401     },
17402     
17403     onTouchViewBeforeLoad : function(combo,opts)
17404     {
17405         return;
17406     },
17407
17408     // private
17409     onTouchViewLoad : function()
17410     {
17411         if(this.store.getCount() < 1){
17412             this.onTouchViewEmptyResults();
17413             return;
17414         }
17415         
17416         this.clearTouchView();
17417         
17418         var rawValue = this.getRawValue();
17419         
17420         var template = (this.multiple) ? Roo.bootstrap.ComboBox.listItemCheckbox : Roo.bootstrap.ComboBox.listItemRadio;
17421         
17422         this.tickItems = [];
17423         
17424         this.store.data.each(function(d, rowIndex){
17425             var row = this.touchViewListGroup.createChild(template);
17426             
17427             if(typeof(d.data.cls) != 'undefined' && d.data.cls.length){
17428                 row.addClass(d.data.cls);
17429             }
17430             
17431             if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
17432                 var cfg = {
17433                     data : d.data,
17434                     html : d.data[this.displayField]
17435                 };
17436                 
17437                 if(this.fireEvent('touchviewdisplay', this, cfg) !== false){
17438                     row.select('.roo-combobox-list-group-item-value', true).first().dom.innerHTML = cfg.html;
17439                 }
17440             }
17441             row.removeClass('selected');
17442             if(!this.multiple && this.valueField &&
17443                     typeof(d.data[this.valueField]) != 'undefined' && d.data[this.valueField] == this.getValue())
17444             {
17445                 // radio buttons..
17446                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
17447                 row.addClass('selected');
17448             }
17449             
17450             if(this.multiple && this.valueField &&
17451                     typeof(d.data[this.valueField]) != 'undefined' && this.getValue().indexOf(d.data[this.valueField]) != -1)
17452             {
17453                 
17454                 // checkboxes...
17455                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
17456                 this.tickItems.push(d.data);
17457             }
17458             
17459             row.on('click', this.onTouchViewClick, this, {row : row, rowIndex : rowIndex});
17460             
17461         }, this);
17462         
17463         var firstChecked = this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).first();
17464         
17465         var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
17466
17467         if(this.modalTitle.length){
17468             bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
17469         }
17470
17471         var listHeight = this.touchViewListGroup.getHeight() + this.touchViewBodyEl.getPadding('tb') * 2;
17472         
17473         if(this.mobile_restrict_height && listHeight < bodyHeight){
17474             this.touchViewBodyEl.setHeight(listHeight);
17475         }
17476         
17477         var _this = this;
17478         
17479         if(firstChecked && listHeight > bodyHeight){
17480             (function() { firstChecked.findParent('li').scrollIntoView(_this.touchViewListGroup.dom); }).defer(500);
17481         }
17482         
17483     },
17484     
17485     onTouchViewLoadException : function()
17486     {
17487         this.hideTouchView();
17488     },
17489     
17490     onTouchViewEmptyResults : function()
17491     {
17492         this.clearTouchView();
17493         
17494         this.touchViewListGroup.createChild(Roo.bootstrap.ComboBox.emptyResult);
17495         
17496         this.touchViewListGroup.select('.roo-combobox-touch-view-empty-result', true).first().dom.innerHTML = this.emptyResultText;
17497         
17498     },
17499     
17500     clearTouchView : function()
17501     {
17502         this.touchViewListGroup.dom.innerHTML = '';
17503     },
17504     
17505     onTouchViewClick : function(e, el, o)
17506     {
17507         e.preventDefault();
17508         
17509         var row = o.row;
17510         var rowIndex = o.rowIndex;
17511         
17512         var r = this.store.getAt(rowIndex);
17513         
17514         if(this.fireEvent('beforeselect', this, r, rowIndex) !== false){
17515             
17516             if(!this.multiple){
17517                 Roo.each(this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).elements, function(c){
17518                     c.dom.removeAttribute('checked');
17519                 }, this);
17520
17521                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
17522
17523                 this.setFromData(r.data);
17524
17525                 var close = this.closeTriggerEl();
17526
17527                 if(close){
17528                     close.show();
17529                 }
17530
17531                 this.hideTouchView();
17532
17533                 this.fireEvent('select', this, r, rowIndex);
17534
17535                 return;
17536             }
17537
17538             if(this.valueField && typeof(r.data[this.valueField]) != 'undefined' && this.getValue().indexOf(r.data[this.valueField]) != -1){
17539                 row.select('.roo-combobox-list-group-item-box > input', true).first().dom.removeAttribute('checked');
17540                 this.tickItems.splice(this.tickItems.indexOf(r.data), 1);
17541                 return;
17542             }
17543
17544             row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
17545             this.addItem(r.data);
17546             this.tickItems.push(r.data);
17547         }
17548     },
17549     
17550     getAutoCreateNativeIOS : function()
17551     {
17552         var cfg = {
17553             cls: 'form-group' //input-group,
17554         };
17555         
17556         var combobox =  {
17557             tag: 'select',
17558             cls : 'roo-ios-select'
17559         };
17560         
17561         if (this.name) {
17562             combobox.name = this.name;
17563         }
17564         
17565         if (this.disabled) {
17566             combobox.disabled = true;
17567         }
17568         
17569         var settings = this;
17570         
17571         ['xs','sm','md','lg'].map(function(size){
17572             if (settings[size]) {
17573                 cfg.cls += ' col-' + size + '-' + settings[size];
17574             }
17575         });
17576         
17577         cfg.cn = combobox;
17578         
17579         return cfg;
17580         
17581     },
17582     
17583     initIOSView : function()
17584     {
17585         this.store.on('load', this.onIOSViewLoad, this);
17586         
17587         return;
17588     },
17589     
17590     onIOSViewLoad : function()
17591     {
17592         if(this.store.getCount() < 1){
17593             return;
17594         }
17595         
17596         this.clearIOSView();
17597         
17598         if(this.allowBlank) {
17599             
17600             var default_text = '-- SELECT --';
17601             
17602             if(this.placeholder.length){
17603                 default_text = this.placeholder;
17604             }
17605             
17606             if(this.emptyTitle.length){
17607                 default_text += ' - ' + this.emptyTitle + ' -';
17608             }
17609             
17610             var opt = this.inputEl().createChild({
17611                 tag: 'option',
17612                 value : 0,
17613                 html : default_text
17614             });
17615             
17616             var o = {};
17617             o[this.valueField] = 0;
17618             o[this.displayField] = default_text;
17619             
17620             this.ios_options.push({
17621                 data : o,
17622                 el : opt
17623             });
17624             
17625         }
17626         
17627         this.store.data.each(function(d, rowIndex){
17628             
17629             var html = '';
17630             
17631             if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
17632                 html = d.data[this.displayField];
17633             }
17634             
17635             var value = '';
17636             
17637             if(this.valueField && typeof(d.data[this.valueField]) != 'undefined'){
17638                 value = d.data[this.valueField];
17639             }
17640             
17641             var option = {
17642                 tag: 'option',
17643                 value : value,
17644                 html : html
17645             };
17646             
17647             if(this.value == d.data[this.valueField]){
17648                 option['selected'] = true;
17649             }
17650             
17651             var opt = this.inputEl().createChild(option);
17652             
17653             this.ios_options.push({
17654                 data : d.data,
17655                 el : opt
17656             });
17657             
17658         }, this);
17659         
17660         this.inputEl().on('change', function(){
17661            this.fireEvent('select', this);
17662         }, this);
17663         
17664     },
17665     
17666     clearIOSView: function()
17667     {
17668         this.inputEl().dom.innerHTML = '';
17669         
17670         this.ios_options = [];
17671     },
17672     
17673     setIOSValue: function(v)
17674     {
17675         this.value = v;
17676         
17677         if(!this.ios_options){
17678             return;
17679         }
17680         
17681         Roo.each(this.ios_options, function(opts){
17682            
17683            opts.el.dom.removeAttribute('selected');
17684            
17685            if(opts.data[this.valueField] != v){
17686                return;
17687            }
17688            
17689            opts.el.dom.setAttribute('selected', true);
17690            
17691         }, this);
17692     }
17693
17694     /** 
17695     * @cfg {Boolean} grow 
17696     * @hide 
17697     */
17698     /** 
17699     * @cfg {Number} growMin 
17700     * @hide 
17701     */
17702     /** 
17703     * @cfg {Number} growMax 
17704     * @hide 
17705     */
17706     /**
17707      * @hide
17708      * @method autoSize
17709      */
17710 });
17711
17712 Roo.apply(Roo.bootstrap.ComboBox,  {
17713     
17714     header : {
17715         tag: 'div',
17716         cls: 'modal-header',
17717         cn: [
17718             {
17719                 tag: 'h4',
17720                 cls: 'modal-title'
17721             }
17722         ]
17723     },
17724     
17725     body : {
17726         tag: 'div',
17727         cls: 'modal-body',
17728         cn: [
17729             {
17730                 tag: 'ul',
17731                 cls: 'list-group'
17732             }
17733         ]
17734     },
17735     
17736     listItemRadio : {
17737         tag: 'li',
17738         cls: 'list-group-item',
17739         cn: [
17740             {
17741                 tag: 'span',
17742                 cls: 'roo-combobox-list-group-item-value'
17743             },
17744             {
17745                 tag: 'div',
17746                 cls: 'roo-combobox-list-group-item-box pull-xs-right radio-inline radio radio-info',
17747                 cn: [
17748                     {
17749                         tag: 'input',
17750                         type: 'radio'
17751                     },
17752                     {
17753                         tag: 'label'
17754                     }
17755                 ]
17756             }
17757         ]
17758     },
17759     
17760     listItemCheckbox : {
17761         tag: 'li',
17762         cls: 'list-group-item',
17763         cn: [
17764             {
17765                 tag: 'span',
17766                 cls: 'roo-combobox-list-group-item-value'
17767             },
17768             {
17769                 tag: 'div',
17770                 cls: 'roo-combobox-list-group-item-box pull-xs-right checkbox-inline checkbox checkbox-info',
17771                 cn: [
17772                     {
17773                         tag: 'input',
17774                         type: 'checkbox'
17775                     },
17776                     {
17777                         tag: 'label'
17778                     }
17779                 ]
17780             }
17781         ]
17782     },
17783     
17784     emptyResult : {
17785         tag: 'div',
17786         cls: 'alert alert-danger roo-combobox-touch-view-empty-result'
17787     },
17788     
17789     footer : {
17790         tag: 'div',
17791         cls: 'modal-footer',
17792         cn: [
17793             {
17794                 tag: 'div',
17795                 cls: 'row',
17796                 cn: [
17797                     {
17798                         tag: 'div',
17799                         cls: 'col-xs-6 text-left',
17800                         cn: {
17801                             tag: 'button',
17802                             cls: 'btn btn-danger roo-touch-view-cancel',
17803                             html: 'Cancel'
17804                         }
17805                     },
17806                     {
17807                         tag: 'div',
17808                         cls: 'col-xs-6 text-right',
17809                         cn: {
17810                             tag: 'button',
17811                             cls: 'btn btn-success roo-touch-view-ok',
17812                             html: 'OK'
17813                         }
17814                     }
17815                 ]
17816             }
17817         ]
17818         
17819     }
17820 });
17821
17822 Roo.apply(Roo.bootstrap.ComboBox,  {
17823     
17824     touchViewTemplate : {
17825         tag: 'div',
17826         cls: 'modal fade roo-combobox-touch-view',
17827         cn: [
17828             {
17829                 tag: 'div',
17830                 cls: 'modal-dialog',
17831                 style : 'position:fixed', // we have to fix position....
17832                 cn: [
17833                     {
17834                         tag: 'div',
17835                         cls: 'modal-content',
17836                         cn: [
17837                             Roo.bootstrap.ComboBox.header,
17838                             Roo.bootstrap.ComboBox.body,
17839                             Roo.bootstrap.ComboBox.footer
17840                         ]
17841                     }
17842                 ]
17843             }
17844         ]
17845     }
17846 });/*
17847  * Based on:
17848  * Ext JS Library 1.1.1
17849  * Copyright(c) 2006-2007, Ext JS, LLC.
17850  *
17851  * Originally Released Under LGPL - original licence link has changed is not relivant.
17852  *
17853  * Fork - LGPL
17854  * <script type="text/javascript">
17855  */
17856
17857 /**
17858  * @class Roo.View
17859  * @extends Roo.util.Observable
17860  * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template. 
17861  * This class also supports single and multi selection modes. <br>
17862  * Create a data model bound view:
17863  <pre><code>
17864  var store = new Roo.data.Store(...);
17865
17866  var view = new Roo.View({
17867     el : "my-element",
17868     tpl : '&lt;div id="{0}"&gt;{2} - {1}&lt;/div&gt;', // auto create template
17869  
17870     singleSelect: true,
17871     selectedClass: "ydataview-selected",
17872     store: store
17873  });
17874
17875  // listen for node click?
17876  view.on("click", function(vw, index, node, e){
17877  alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
17878  });
17879
17880  // load XML data
17881  dataModel.load("foobar.xml");
17882  </code></pre>
17883  For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
17884  * <br><br>
17885  * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
17886  * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
17887  * 
17888  * Note: old style constructor is still suported (container, template, config)
17889  * 
17890  * @constructor
17891  * Create a new View
17892  * @param {Object} config The config object
17893  * 
17894  */
17895 Roo.View = function(config, depreciated_tpl, depreciated_config){
17896     
17897     this.parent = false;
17898     
17899     if (typeof(depreciated_tpl) == 'undefined') {
17900         // new way.. - universal constructor.
17901         Roo.apply(this, config);
17902         this.el  = Roo.get(this.el);
17903     } else {
17904         // old format..
17905         this.el  = Roo.get(config);
17906         this.tpl = depreciated_tpl;
17907         Roo.apply(this, depreciated_config);
17908     }
17909     this.wrapEl  = this.el.wrap().wrap();
17910     ///this.el = this.wrapEla.appendChild(document.createElement("div"));
17911     
17912     
17913     if(typeof(this.tpl) == "string"){
17914         this.tpl = new Roo.Template(this.tpl);
17915     } else {
17916         // support xtype ctors..
17917         this.tpl = new Roo.factory(this.tpl, Roo);
17918     }
17919     
17920     
17921     this.tpl.compile();
17922     
17923     /** @private */
17924     this.addEvents({
17925         /**
17926          * @event beforeclick
17927          * Fires before a click is processed. Returns false to cancel the default action.
17928          * @param {Roo.View} this
17929          * @param {Number} index The index of the target node
17930          * @param {HTMLElement} node The target node
17931          * @param {Roo.EventObject} e The raw event object
17932          */
17933             "beforeclick" : true,
17934         /**
17935          * @event click
17936          * Fires when a template node is clicked.
17937          * @param {Roo.View} this
17938          * @param {Number} index The index of the target node
17939          * @param {HTMLElement} node The target node
17940          * @param {Roo.EventObject} e The raw event object
17941          */
17942             "click" : true,
17943         /**
17944          * @event dblclick
17945          * Fires when a template node is double clicked.
17946          * @param {Roo.View} this
17947          * @param {Number} index The index of the target node
17948          * @param {HTMLElement} node The target node
17949          * @param {Roo.EventObject} e The raw event object
17950          */
17951             "dblclick" : true,
17952         /**
17953          * @event contextmenu
17954          * Fires when a template node is right clicked.
17955          * @param {Roo.View} this
17956          * @param {Number} index The index of the target node
17957          * @param {HTMLElement} node The target node
17958          * @param {Roo.EventObject} e The raw event object
17959          */
17960             "contextmenu" : true,
17961         /**
17962          * @event selectionchange
17963          * Fires when the selected nodes change.
17964          * @param {Roo.View} this
17965          * @param {Array} selections Array of the selected nodes
17966          */
17967             "selectionchange" : true,
17968     
17969         /**
17970          * @event beforeselect
17971          * Fires before a selection is made. If any handlers return false, the selection is cancelled.
17972          * @param {Roo.View} this
17973          * @param {HTMLElement} node The node to be selected
17974          * @param {Array} selections Array of currently selected nodes
17975          */
17976             "beforeselect" : true,
17977         /**
17978          * @event preparedata
17979          * Fires on every row to render, to allow you to change the data.
17980          * @param {Roo.View} this
17981          * @param {Object} data to be rendered (change this)
17982          */
17983           "preparedata" : true
17984           
17985           
17986         });
17987
17988
17989
17990     this.el.on({
17991         "click": this.onClick,
17992         "dblclick": this.onDblClick,
17993         "contextmenu": this.onContextMenu,
17994         scope:this
17995     });
17996
17997     this.selections = [];
17998     this.nodes = [];
17999     this.cmp = new Roo.CompositeElementLite([]);
18000     if(this.store){
18001         this.store = Roo.factory(this.store, Roo.data);
18002         this.setStore(this.store, true);
18003     }
18004     
18005     if ( this.footer && this.footer.xtype) {
18006            
18007          var fctr = this.wrapEl.appendChild(document.createElement("div"));
18008         
18009         this.footer.dataSource = this.store;
18010         this.footer.container = fctr;
18011         this.footer = Roo.factory(this.footer, Roo);
18012         fctr.insertFirst(this.el);
18013         
18014         // this is a bit insane - as the paging toolbar seems to detach the el..
18015 //        dom.parentNode.parentNode.parentNode
18016          // they get detached?
18017     }
18018     
18019     
18020     Roo.View.superclass.constructor.call(this);
18021     
18022     
18023 };
18024
18025 Roo.extend(Roo.View, Roo.util.Observable, {
18026     
18027      /**
18028      * @cfg {Roo.data.Store} store Data store to load data from.
18029      */
18030     store : false,
18031     
18032     /**
18033      * @cfg {String|Roo.Element} el The container element.
18034      */
18035     el : '',
18036     
18037     /**
18038      * @cfg {String|Roo.Template} tpl The template used by this View 
18039      */
18040     tpl : false,
18041     /**
18042      * @cfg {String} dataName the named area of the template to use as the data area
18043      *                          Works with domtemplates roo-name="name"
18044      */
18045     dataName: false,
18046     /**
18047      * @cfg {String} selectedClass The css class to add to selected nodes
18048      */
18049     selectedClass : "x-view-selected",
18050      /**
18051      * @cfg {String} emptyText The empty text to show when nothing is loaded.
18052      */
18053     emptyText : "",
18054     
18055     /**
18056      * @cfg {String} text to display on mask (default Loading)
18057      */
18058     mask : false,
18059     /**
18060      * @cfg {Boolean} multiSelect Allow multiple selection
18061      */
18062     multiSelect : false,
18063     /**
18064      * @cfg {Boolean} singleSelect Allow single selection
18065      */
18066     singleSelect:  false,
18067     
18068     /**
18069      * @cfg {Boolean} toggleSelect - selecting 
18070      */
18071     toggleSelect : false,
18072     
18073     /**
18074      * @cfg {Boolean} tickable - selecting 
18075      */
18076     tickable : false,
18077     
18078     /**
18079      * Returns the element this view is bound to.
18080      * @return {Roo.Element}
18081      */
18082     getEl : function(){
18083         return this.wrapEl;
18084     },
18085     
18086     
18087
18088     /**
18089      * Refreshes the view. - called by datachanged on the store. - do not call directly.
18090      */
18091     refresh : function(){
18092         //Roo.log('refresh');
18093         var t = this.tpl;
18094         
18095         // if we are using something like 'domtemplate', then
18096         // the what gets used is:
18097         // t.applySubtemplate(NAME, data, wrapping data..)
18098         // the outer template then get' applied with
18099         //     the store 'extra data'
18100         // and the body get's added to the
18101         //      roo-name="data" node?
18102         //      <span class='roo-tpl-{name}'></span> ?????
18103         
18104         
18105         
18106         this.clearSelections();
18107         this.el.update("");
18108         var html = [];
18109         var records = this.store.getRange();
18110         if(records.length < 1) {
18111             
18112             // is this valid??  = should it render a template??
18113             
18114             this.el.update(this.emptyText);
18115             return;
18116         }
18117         var el = this.el;
18118         if (this.dataName) {
18119             this.el.update(t.apply(this.store.meta)); //????
18120             el = this.el.child('.roo-tpl-' + this.dataName);
18121         }
18122         
18123         for(var i = 0, len = records.length; i < len; i++){
18124             var data = this.prepareData(records[i].data, i, records[i]);
18125             this.fireEvent("preparedata", this, data, i, records[i]);
18126             
18127             var d = Roo.apply({}, data);
18128             
18129             if(this.tickable){
18130                 Roo.apply(d, {'roo-id' : Roo.id()});
18131                 
18132                 var _this = this;
18133             
18134                 Roo.each(this.parent.item, function(item){
18135                     if(item[_this.parent.valueField] != data[_this.parent.valueField]){
18136                         return;
18137                     }
18138                     Roo.apply(d, {'roo-data-checked' : 'checked'});
18139                 });
18140             }
18141             
18142             html[html.length] = Roo.util.Format.trim(
18143                 this.dataName ?
18144                     t.applySubtemplate(this.dataName, d, this.store.meta) :
18145                     t.apply(d)
18146             );
18147         }
18148         
18149         
18150         
18151         el.update(html.join(""));
18152         this.nodes = el.dom.childNodes;
18153         this.updateIndexes(0);
18154     },
18155     
18156
18157     /**
18158      * Function to override to reformat the data that is sent to
18159      * the template for each node.
18160      * DEPRICATED - use the preparedata event handler.
18161      * @param {Array/Object} data The raw data (array of colData for a data model bound view or
18162      * a JSON object for an UpdateManager bound view).
18163      */
18164     prepareData : function(data, index, record)
18165     {
18166         this.fireEvent("preparedata", this, data, index, record);
18167         return data;
18168     },
18169
18170     onUpdate : function(ds, record){
18171         // Roo.log('on update');   
18172         this.clearSelections();
18173         var index = this.store.indexOf(record);
18174         var n = this.nodes[index];
18175         this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
18176         n.parentNode.removeChild(n);
18177         this.updateIndexes(index, index);
18178     },
18179
18180     
18181     
18182 // --------- FIXME     
18183     onAdd : function(ds, records, index)
18184     {
18185         //Roo.log(['on Add', ds, records, index] );        
18186         this.clearSelections();
18187         if(this.nodes.length == 0){
18188             this.refresh();
18189             return;
18190         }
18191         var n = this.nodes[index];
18192         for(var i = 0, len = records.length; i < len; i++){
18193             var d = this.prepareData(records[i].data, i, records[i]);
18194             if(n){
18195                 this.tpl.insertBefore(n, d);
18196             }else{
18197                 
18198                 this.tpl.append(this.el, d);
18199             }
18200         }
18201         this.updateIndexes(index);
18202     },
18203
18204     onRemove : function(ds, record, index){
18205        // Roo.log('onRemove');
18206         this.clearSelections();
18207         var el = this.dataName  ?
18208             this.el.child('.roo-tpl-' + this.dataName) :
18209             this.el; 
18210         
18211         el.dom.removeChild(this.nodes[index]);
18212         this.updateIndexes(index);
18213     },
18214
18215     /**
18216      * Refresh an individual node.
18217      * @param {Number} index
18218      */
18219     refreshNode : function(index){
18220         this.onUpdate(this.store, this.store.getAt(index));
18221     },
18222
18223     updateIndexes : function(startIndex, endIndex){
18224         var ns = this.nodes;
18225         startIndex = startIndex || 0;
18226         endIndex = endIndex || ns.length - 1;
18227         for(var i = startIndex; i <= endIndex; i++){
18228             ns[i].nodeIndex = i;
18229         }
18230     },
18231
18232     /**
18233      * Changes the data store this view uses and refresh the view.
18234      * @param {Store} store
18235      */
18236     setStore : function(store, initial){
18237         if(!initial && this.store){
18238             this.store.un("datachanged", this.refresh);
18239             this.store.un("add", this.onAdd);
18240             this.store.un("remove", this.onRemove);
18241             this.store.un("update", this.onUpdate);
18242             this.store.un("clear", this.refresh);
18243             this.store.un("beforeload", this.onBeforeLoad);
18244             this.store.un("load", this.onLoad);
18245             this.store.un("loadexception", this.onLoad);
18246         }
18247         if(store){
18248           
18249             store.on("datachanged", this.refresh, this);
18250             store.on("add", this.onAdd, this);
18251             store.on("remove", this.onRemove, this);
18252             store.on("update", this.onUpdate, this);
18253             store.on("clear", this.refresh, this);
18254             store.on("beforeload", this.onBeforeLoad, this);
18255             store.on("load", this.onLoad, this);
18256             store.on("loadexception", this.onLoad, this);
18257         }
18258         
18259         if(store){
18260             this.refresh();
18261         }
18262     },
18263     /**
18264      * onbeforeLoad - masks the loading area.
18265      *
18266      */
18267     onBeforeLoad : function(store,opts)
18268     {
18269          //Roo.log('onBeforeLoad');   
18270         if (!opts.add) {
18271             this.el.update("");
18272         }
18273         this.el.mask(this.mask ? this.mask : "Loading" ); 
18274     },
18275     onLoad : function ()
18276     {
18277         this.el.unmask();
18278     },
18279     
18280
18281     /**
18282      * Returns the template node the passed child belongs to or null if it doesn't belong to one.
18283      * @param {HTMLElement} node
18284      * @return {HTMLElement} The template node
18285      */
18286     findItemFromChild : function(node){
18287         var el = this.dataName  ?
18288             this.el.child('.roo-tpl-' + this.dataName,true) :
18289             this.el.dom; 
18290         
18291         if(!node || node.parentNode == el){
18292                     return node;
18293             }
18294             var p = node.parentNode;
18295             while(p && p != el){
18296             if(p.parentNode == el){
18297                 return p;
18298             }
18299             p = p.parentNode;
18300         }
18301             return null;
18302     },
18303
18304     /** @ignore */
18305     onClick : function(e){
18306         var item = this.findItemFromChild(e.getTarget());
18307         if(item){
18308             var index = this.indexOf(item);
18309             if(this.onItemClick(item, index, e) !== false){
18310                 this.fireEvent("click", this, index, item, e);
18311             }
18312         }else{
18313             this.clearSelections();
18314         }
18315     },
18316
18317     /** @ignore */
18318     onContextMenu : function(e){
18319         var item = this.findItemFromChild(e.getTarget());
18320         if(item){
18321             this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
18322         }
18323     },
18324
18325     /** @ignore */
18326     onDblClick : function(e){
18327         var item = this.findItemFromChild(e.getTarget());
18328         if(item){
18329             this.fireEvent("dblclick", this, this.indexOf(item), item, e);
18330         }
18331     },
18332
18333     onItemClick : function(item, index, e)
18334     {
18335         if(this.fireEvent("beforeclick", this, index, item, e) === false){
18336             return false;
18337         }
18338         if (this.toggleSelect) {
18339             var m = this.isSelected(item) ? 'unselect' : 'select';
18340             //Roo.log(m);
18341             var _t = this;
18342             _t[m](item, true, false);
18343             return true;
18344         }
18345         if(this.multiSelect || this.singleSelect){
18346             if(this.multiSelect && e.shiftKey && this.lastSelection){
18347                 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
18348             }else{
18349                 this.select(item, this.multiSelect && e.ctrlKey);
18350                 this.lastSelection = item;
18351             }
18352             
18353             if(!this.tickable){
18354                 e.preventDefault();
18355             }
18356             
18357         }
18358         return true;
18359     },
18360
18361     /**
18362      * Get the number of selected nodes.
18363      * @return {Number}
18364      */
18365     getSelectionCount : function(){
18366         return this.selections.length;
18367     },
18368
18369     /**
18370      * Get the currently selected nodes.
18371      * @return {Array} An array of HTMLElements
18372      */
18373     getSelectedNodes : function(){
18374         return this.selections;
18375     },
18376
18377     /**
18378      * Get the indexes of the selected nodes.
18379      * @return {Array}
18380      */
18381     getSelectedIndexes : function(){
18382         var indexes = [], s = this.selections;
18383         for(var i = 0, len = s.length; i < len; i++){
18384             indexes.push(s[i].nodeIndex);
18385         }
18386         return indexes;
18387     },
18388
18389     /**
18390      * Clear all selections
18391      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
18392      */
18393     clearSelections : function(suppressEvent){
18394         if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
18395             this.cmp.elements = this.selections;
18396             this.cmp.removeClass(this.selectedClass);
18397             this.selections = [];
18398             if(!suppressEvent){
18399                 this.fireEvent("selectionchange", this, this.selections);
18400             }
18401         }
18402     },
18403
18404     /**
18405      * Returns true if the passed node is selected
18406      * @param {HTMLElement/Number} node The node or node index
18407      * @return {Boolean}
18408      */
18409     isSelected : function(node){
18410         var s = this.selections;
18411         if(s.length < 1){
18412             return false;
18413         }
18414         node = this.getNode(node);
18415         return s.indexOf(node) !== -1;
18416     },
18417
18418     /**
18419      * Selects nodes.
18420      * @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
18421      * @param {Boolean} keepExisting (optional) true to keep existing selections
18422      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
18423      */
18424     select : function(nodeInfo, keepExisting, suppressEvent){
18425         if(nodeInfo instanceof Array){
18426             if(!keepExisting){
18427                 this.clearSelections(true);
18428             }
18429             for(var i = 0, len = nodeInfo.length; i < len; i++){
18430                 this.select(nodeInfo[i], true, true);
18431             }
18432             return;
18433         } 
18434         var node = this.getNode(nodeInfo);
18435         if(!node || this.isSelected(node)){
18436             return; // already selected.
18437         }
18438         if(!keepExisting){
18439             this.clearSelections(true);
18440         }
18441         
18442         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
18443             Roo.fly(node).addClass(this.selectedClass);
18444             this.selections.push(node);
18445             if(!suppressEvent){
18446                 this.fireEvent("selectionchange", this, this.selections);
18447             }
18448         }
18449         
18450         
18451     },
18452       /**
18453      * Unselects nodes.
18454      * @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
18455      * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
18456      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
18457      */
18458     unselect : function(nodeInfo, keepExisting, suppressEvent)
18459     {
18460         if(nodeInfo instanceof Array){
18461             Roo.each(this.selections, function(s) {
18462                 this.unselect(s, nodeInfo);
18463             }, this);
18464             return;
18465         }
18466         var node = this.getNode(nodeInfo);
18467         if(!node || !this.isSelected(node)){
18468             //Roo.log("not selected");
18469             return; // not selected.
18470         }
18471         // fireevent???
18472         var ns = [];
18473         Roo.each(this.selections, function(s) {
18474             if (s == node ) {
18475                 Roo.fly(node).removeClass(this.selectedClass);
18476
18477                 return;
18478             }
18479             ns.push(s);
18480         },this);
18481         
18482         this.selections= ns;
18483         this.fireEvent("selectionchange", this, this.selections);
18484     },
18485
18486     /**
18487      * Gets a template node.
18488      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
18489      * @return {HTMLElement} The node or null if it wasn't found
18490      */
18491     getNode : function(nodeInfo){
18492         if(typeof nodeInfo == "string"){
18493             return document.getElementById(nodeInfo);
18494         }else if(typeof nodeInfo == "number"){
18495             return this.nodes[nodeInfo];
18496         }
18497         return nodeInfo;
18498     },
18499
18500     /**
18501      * Gets a range template nodes.
18502      * @param {Number} startIndex
18503      * @param {Number} endIndex
18504      * @return {Array} An array of nodes
18505      */
18506     getNodes : function(start, end){
18507         var ns = this.nodes;
18508         start = start || 0;
18509         end = typeof end == "undefined" ? ns.length - 1 : end;
18510         var nodes = [];
18511         if(start <= end){
18512             for(var i = start; i <= end; i++){
18513                 nodes.push(ns[i]);
18514             }
18515         } else{
18516             for(var i = start; i >= end; i--){
18517                 nodes.push(ns[i]);
18518             }
18519         }
18520         return nodes;
18521     },
18522
18523     /**
18524      * Finds the index of the passed node
18525      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
18526      * @return {Number} The index of the node or -1
18527      */
18528     indexOf : function(node){
18529         node = this.getNode(node);
18530         if(typeof node.nodeIndex == "number"){
18531             return node.nodeIndex;
18532         }
18533         var ns = this.nodes;
18534         for(var i = 0, len = ns.length; i < len; i++){
18535             if(ns[i] == node){
18536                 return i;
18537             }
18538         }
18539         return -1;
18540     }
18541 });
18542 /*
18543  * - LGPL
18544  *
18545  * based on jquery fullcalendar
18546  * 
18547  */
18548
18549 Roo.bootstrap = Roo.bootstrap || {};
18550 /**
18551  * @class Roo.bootstrap.Calendar
18552  * @extends Roo.bootstrap.Component
18553  * Bootstrap Calendar class
18554  * @cfg {Boolean} loadMask (true|false) default false
18555  * @cfg {Object} header generate the user specific header of the calendar, default false
18556
18557  * @constructor
18558  * Create a new Container
18559  * @param {Object} config The config object
18560  */
18561
18562
18563
18564 Roo.bootstrap.Calendar = function(config){
18565     Roo.bootstrap.Calendar.superclass.constructor.call(this, config);
18566      this.addEvents({
18567         /**
18568              * @event select
18569              * Fires when a date is selected
18570              * @param {DatePicker} this
18571              * @param {Date} date The selected date
18572              */
18573         'select': true,
18574         /**
18575              * @event monthchange
18576              * Fires when the displayed month changes 
18577              * @param {DatePicker} this
18578              * @param {Date} date The selected month
18579              */
18580         'monthchange': true,
18581         /**
18582              * @event evententer
18583              * Fires when mouse over an event
18584              * @param {Calendar} this
18585              * @param {event} Event
18586              */
18587         'evententer': true,
18588         /**
18589              * @event eventleave
18590              * Fires when the mouse leaves an
18591              * @param {Calendar} this
18592              * @param {event}
18593              */
18594         'eventleave': true,
18595         /**
18596              * @event eventclick
18597              * Fires when the mouse click an
18598              * @param {Calendar} this
18599              * @param {event}
18600              */
18601         'eventclick': true
18602         
18603     });
18604
18605 };
18606
18607 Roo.extend(Roo.bootstrap.Calendar, Roo.bootstrap.Component,  {
18608     
18609      /**
18610      * @cfg {Number} startDay
18611      * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
18612      */
18613     startDay : 0,
18614     
18615     loadMask : false,
18616     
18617     header : false,
18618       
18619     getAutoCreate : function(){
18620         
18621         
18622         var fc_button = function(name, corner, style, content ) {
18623             return Roo.apply({},{
18624                 tag : 'span',
18625                 cls : 'fc-button fc-button-'+name+' fc-state-default ' + 
18626                          (corner.length ?
18627                             'fc-corner-' + corner.split(' ').join(' fc-corner-') :
18628                             ''
18629                         ),
18630                 html : '<SPAN class="fc-text-'+style+ '">'+content +'</SPAN>',
18631                 unselectable: 'on'
18632             });
18633         };
18634         
18635         var header = {};
18636         
18637         if(!this.header){
18638             header = {
18639                 tag : 'table',
18640                 cls : 'fc-header',
18641                 style : 'width:100%',
18642                 cn : [
18643                     {
18644                         tag: 'tr',
18645                         cn : [
18646                             {
18647                                 tag : 'td',
18648                                 cls : 'fc-header-left',
18649                                 cn : [
18650                                     fc_button('prev', 'left', 'arrow', '&#8249;' ),
18651                                     fc_button('next', 'right', 'arrow', '&#8250;' ),
18652                                     { tag: 'span', cls: 'fc-header-space' },
18653                                     fc_button('today', 'left right', '', 'today' )  // neds state disabled..
18654
18655
18656                                 ]
18657                             },
18658
18659                             {
18660                                 tag : 'td',
18661                                 cls : 'fc-header-center',
18662                                 cn : [
18663                                     {
18664                                         tag: 'span',
18665                                         cls: 'fc-header-title',
18666                                         cn : {
18667                                             tag: 'H2',
18668                                             html : 'month / year'
18669                                         }
18670                                     }
18671
18672                                 ]
18673                             },
18674                             {
18675                                 tag : 'td',
18676                                 cls : 'fc-header-right',
18677                                 cn : [
18678                               /*      fc_button('month', 'left', '', 'month' ),
18679                                     fc_button('week', '', '', 'week' ),
18680                                     fc_button('day', 'right', '', 'day' )
18681                                 */    
18682
18683                                 ]
18684                             }
18685
18686                         ]
18687                     }
18688                 ]
18689             };
18690         }
18691         
18692         header = this.header;
18693         
18694        
18695         var cal_heads = function() {
18696             var ret = [];
18697             // fixme - handle this.
18698             
18699             for (var i =0; i < Date.dayNames.length; i++) {
18700                 var d = Date.dayNames[i];
18701                 ret.push({
18702                     tag: 'th',
18703                     cls : 'fc-day-header fc-' + d.substring(0,3).toLowerCase() + ' fc-widget-header',
18704                     html : d.substring(0,3)
18705                 });
18706                 
18707             }
18708             ret[0].cls += ' fc-first';
18709             ret[6].cls += ' fc-last';
18710             return ret;
18711         };
18712         var cal_cell = function(n) {
18713             return  {
18714                 tag: 'td',
18715                 cls : 'fc-day fc-'+n + ' fc-widget-content', ///fc-other-month fc-past
18716                 cn : [
18717                     {
18718                         cn : [
18719                             {
18720                                 cls: 'fc-day-number',
18721                                 html: 'D'
18722                             },
18723                             {
18724                                 cls: 'fc-day-content',
18725                              
18726                                 cn : [
18727                                      {
18728                                         style: 'position: relative;' // height: 17px;
18729                                     }
18730                                 ]
18731                             }
18732                             
18733                             
18734                         ]
18735                     }
18736                 ]
18737                 
18738             }
18739         };
18740         var cal_rows = function() {
18741             
18742             var ret = [];
18743             for (var r = 0; r < 6; r++) {
18744                 var row= {
18745                     tag : 'tr',
18746                     cls : 'fc-week',
18747                     cn : []
18748                 };
18749                 
18750                 for (var i =0; i < Date.dayNames.length; i++) {
18751                     var d = Date.dayNames[i];
18752                     row.cn.push(cal_cell(d.substring(0,3).toLowerCase()));
18753
18754                 }
18755                 row.cn[0].cls+=' fc-first';
18756                 row.cn[0].cn[0].style = 'min-height:90px';
18757                 row.cn[6].cls+=' fc-last';
18758                 ret.push(row);
18759                 
18760             }
18761             ret[0].cls += ' fc-first';
18762             ret[4].cls += ' fc-prev-last';
18763             ret[5].cls += ' fc-last';
18764             return ret;
18765             
18766         };
18767         
18768         var cal_table = {
18769             tag: 'table',
18770             cls: 'fc-border-separate',
18771             style : 'width:100%',
18772             cellspacing  : 0,
18773             cn : [
18774                 { 
18775                     tag: 'thead',
18776                     cn : [
18777                         { 
18778                             tag: 'tr',
18779                             cls : 'fc-first fc-last',
18780                             cn : cal_heads()
18781                         }
18782                     ]
18783                 },
18784                 { 
18785                     tag: 'tbody',
18786                     cn : cal_rows()
18787                 }
18788                   
18789             ]
18790         };
18791          
18792          var cfg = {
18793             cls : 'fc fc-ltr',
18794             cn : [
18795                 header,
18796                 {
18797                     cls : 'fc-content',
18798                     style : "position: relative;",
18799                     cn : [
18800                         {
18801                             cls : 'fc-view fc-view-month fc-grid',
18802                             style : 'position: relative',
18803                             unselectable : 'on',
18804                             cn : [
18805                                 {
18806                                     cls : 'fc-event-container',
18807                                     style : 'position:absolute;z-index:8;top:0;left:0;'
18808                                 },
18809                                 cal_table
18810                             ]
18811                         }
18812                     ]
18813     
18814                 }
18815            ] 
18816             
18817         };
18818         
18819          
18820         
18821         return cfg;
18822     },
18823     
18824     
18825     initEvents : function()
18826     {
18827         if(!this.store){
18828             throw "can not find store for calendar";
18829         }
18830         
18831         var mark = {
18832             tag: "div",
18833             cls:"x-dlg-mask",
18834             style: "text-align:center",
18835             cn: [
18836                 {
18837                     tag: "div",
18838                     style: "background-color:white;width:50%;margin:250 auto",
18839                     cn: [
18840                         {
18841                             tag: "img",
18842                             src: Roo.rootURL + '/images/ux/lightbox/loading.gif' 
18843                         },
18844                         {
18845                             tag: "span",
18846                             html: "Loading"
18847                         }
18848                         
18849                     ]
18850                 }
18851             ]
18852         };
18853         this.maskEl = Roo.DomHelper.append(this.el.select('.fc-content', true).first(), mark, true);
18854         
18855         var size = this.el.select('.fc-content', true).first().getSize();
18856         this.maskEl.setSize(size.width, size.height);
18857         this.maskEl.enableDisplayMode("block");
18858         if(!this.loadMask){
18859             this.maskEl.hide();
18860         }
18861         
18862         this.store = Roo.factory(this.store, Roo.data);
18863         this.store.on('load', this.onLoad, this);
18864         this.store.on('beforeload', this.onBeforeLoad, this);
18865         
18866         this.resize();
18867         
18868         this.cells = this.el.select('.fc-day',true);
18869         //Roo.log(this.cells);
18870         this.textNodes = this.el.query('.fc-day-number');
18871         this.cells.addClassOnOver('fc-state-hover');
18872         
18873         this.el.select('.fc-button-prev',true).on('click', this.showPrevMonth, this);
18874         this.el.select('.fc-button-next',true).on('click', this.showNextMonth, this);
18875         this.el.select('.fc-button-today',true).on('click', this.showToday, this);
18876         this.el.select('.fc-button',true).addClassOnOver('fc-state-hover');
18877         
18878         this.on('monthchange', this.onMonthChange, this);
18879         
18880         this.update(new Date().clearTime());
18881     },
18882     
18883     resize : function() {
18884         var sz  = this.el.getSize();
18885         
18886         this.el.select('.fc-day-header',true).setWidth(sz.width / 7);
18887         this.el.select('.fc-day-content div',true).setHeight(34);
18888     },
18889     
18890     
18891     // private
18892     showPrevMonth : function(e){
18893         this.update(this.activeDate.add("mo", -1));
18894     },
18895     showToday : function(e){
18896         this.update(new Date().clearTime());
18897     },
18898     // private
18899     showNextMonth : function(e){
18900         this.update(this.activeDate.add("mo", 1));
18901     },
18902
18903     // private
18904     showPrevYear : function(){
18905         this.update(this.activeDate.add("y", -1));
18906     },
18907
18908     // private
18909     showNextYear : function(){
18910         this.update(this.activeDate.add("y", 1));
18911     },
18912
18913     
18914    // private
18915     update : function(date)
18916     {
18917         var vd = this.activeDate;
18918         this.activeDate = date;
18919 //        if(vd && this.el){
18920 //            var t = date.getTime();
18921 //            if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
18922 //                Roo.log('using add remove');
18923 //                
18924 //                this.fireEvent('monthchange', this, date);
18925 //                
18926 //                this.cells.removeClass("fc-state-highlight");
18927 //                this.cells.each(function(c){
18928 //                   if(c.dateValue == t){
18929 //                       c.addClass("fc-state-highlight");
18930 //                       setTimeout(function(){
18931 //                            try{c.dom.firstChild.focus();}catch(e){}
18932 //                       }, 50);
18933 //                       return false;
18934 //                   }
18935 //                   return true;
18936 //                });
18937 //                return;
18938 //            }
18939 //        }
18940         
18941         var days = date.getDaysInMonth();
18942         
18943         var firstOfMonth = date.getFirstDateOfMonth();
18944         var startingPos = firstOfMonth.getDay()-this.startDay;
18945         
18946         if(startingPos < this.startDay){
18947             startingPos += 7;
18948         }
18949         
18950         var pm = date.add(Date.MONTH, -1);
18951         var prevStart = pm.getDaysInMonth()-startingPos;
18952 //        
18953         this.cells = this.el.select('.fc-day',true);
18954         this.textNodes = this.el.query('.fc-day-number');
18955         this.cells.addClassOnOver('fc-state-hover');
18956         
18957         var cells = this.cells.elements;
18958         var textEls = this.textNodes;
18959         
18960         Roo.each(cells, function(cell){
18961             cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
18962         });
18963         
18964         days += startingPos;
18965
18966         // convert everything to numbers so it's fast
18967         var day = 86400000;
18968         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
18969         //Roo.log(d);
18970         //Roo.log(pm);
18971         //Roo.log(prevStart);
18972         
18973         var today = new Date().clearTime().getTime();
18974         var sel = date.clearTime().getTime();
18975         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
18976         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
18977         var ddMatch = this.disabledDatesRE;
18978         var ddText = this.disabledDatesText;
18979         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
18980         var ddaysText = this.disabledDaysText;
18981         var format = this.format;
18982         
18983         var setCellClass = function(cal, cell){
18984             cell.row = 0;
18985             cell.events = [];
18986             cell.more = [];
18987             //Roo.log('set Cell Class');
18988             cell.title = "";
18989             var t = d.getTime();
18990             
18991             //Roo.log(d);
18992             
18993             cell.dateValue = t;
18994             if(t == today){
18995                 cell.className += " fc-today";
18996                 cell.className += " fc-state-highlight";
18997                 cell.title = cal.todayText;
18998             }
18999             if(t == sel){
19000                 // disable highlight in other month..
19001                 //cell.className += " fc-state-highlight";
19002                 
19003             }
19004             // disabling
19005             if(t < min) {
19006                 cell.className = " fc-state-disabled";
19007                 cell.title = cal.minText;
19008                 return;
19009             }
19010             if(t > max) {
19011                 cell.className = " fc-state-disabled";
19012                 cell.title = cal.maxText;
19013                 return;
19014             }
19015             if(ddays){
19016                 if(ddays.indexOf(d.getDay()) != -1){
19017                     cell.title = ddaysText;
19018                     cell.className = " fc-state-disabled";
19019                 }
19020             }
19021             if(ddMatch && format){
19022                 var fvalue = d.dateFormat(format);
19023                 if(ddMatch.test(fvalue)){
19024                     cell.title = ddText.replace("%0", fvalue);
19025                     cell.className = " fc-state-disabled";
19026                 }
19027             }
19028             
19029             if (!cell.initialClassName) {
19030                 cell.initialClassName = cell.dom.className;
19031             }
19032             
19033             cell.dom.className = cell.initialClassName  + ' ' +  cell.className;
19034         };
19035
19036         var i = 0;
19037         
19038         for(; i < startingPos; i++) {
19039             textEls[i].innerHTML = (++prevStart);
19040             d.setDate(d.getDate()+1);
19041             
19042             cells[i].className = "fc-past fc-other-month";
19043             setCellClass(this, cells[i]);
19044         }
19045         
19046         var intDay = 0;
19047         
19048         for(; i < days; i++){
19049             intDay = i - startingPos + 1;
19050             textEls[i].innerHTML = (intDay);
19051             d.setDate(d.getDate()+1);
19052             
19053             cells[i].className = ''; // "x-date-active";
19054             setCellClass(this, cells[i]);
19055         }
19056         var extraDays = 0;
19057         
19058         for(; i < 42; i++) {
19059             textEls[i].innerHTML = (++extraDays);
19060             d.setDate(d.getDate()+1);
19061             
19062             cells[i].className = "fc-future fc-other-month";
19063             setCellClass(this, cells[i]);
19064         }
19065         
19066         this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
19067         
19068         var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
19069         
19070         this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
19071         this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
19072         
19073         if(totalRows != 6){
19074             this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
19075             this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
19076         }
19077         
19078         this.fireEvent('monthchange', this, date);
19079         
19080         
19081         /*
19082         if(!this.internalRender){
19083             var main = this.el.dom.firstChild;
19084             var w = main.offsetWidth;
19085             this.el.setWidth(w + this.el.getBorderWidth("lr"));
19086             Roo.fly(main).setWidth(w);
19087             this.internalRender = true;
19088             // opera does not respect the auto grow header center column
19089             // then, after it gets a width opera refuses to recalculate
19090             // without a second pass
19091             if(Roo.isOpera && !this.secondPass){
19092                 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
19093                 this.secondPass = true;
19094                 this.update.defer(10, this, [date]);
19095             }
19096         }
19097         */
19098         
19099     },
19100     
19101     findCell : function(dt) {
19102         dt = dt.clearTime().getTime();
19103         var ret = false;
19104         this.cells.each(function(c){
19105             //Roo.log("check " +c.dateValue + '?=' + dt);
19106             if(c.dateValue == dt){
19107                 ret = c;
19108                 return false;
19109             }
19110             return true;
19111         });
19112         
19113         return ret;
19114     },
19115     
19116     findCells : function(ev) {
19117         var s = ev.start.clone().clearTime().getTime();
19118        // Roo.log(s);
19119         var e= ev.end.clone().clearTime().getTime();
19120        // Roo.log(e);
19121         var ret = [];
19122         this.cells.each(function(c){
19123              ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
19124             
19125             if(c.dateValue > e){
19126                 return ;
19127             }
19128             if(c.dateValue < s){
19129                 return ;
19130             }
19131             ret.push(c);
19132         });
19133         
19134         return ret;    
19135     },
19136     
19137 //    findBestRow: function(cells)
19138 //    {
19139 //        var ret = 0;
19140 //        
19141 //        for (var i =0 ; i < cells.length;i++) {
19142 //            ret  = Math.max(cells[i].rows || 0,ret);
19143 //        }
19144 //        return ret;
19145 //        
19146 //    },
19147     
19148     
19149     addItem : function(ev)
19150     {
19151         // look for vertical location slot in
19152         var cells = this.findCells(ev);
19153         
19154 //        ev.row = this.findBestRow(cells);
19155         
19156         // work out the location.
19157         
19158         var crow = false;
19159         var rows = [];
19160         for(var i =0; i < cells.length; i++) {
19161             
19162             cells[i].row = cells[0].row;
19163             
19164             if(i == 0){
19165                 cells[i].row = cells[i].row + 1;
19166             }
19167             
19168             if (!crow) {
19169                 crow = {
19170                     start : cells[i],
19171                     end :  cells[i]
19172                 };
19173                 continue;
19174             }
19175             if (crow.start.getY() == cells[i].getY()) {
19176                 // on same row.
19177                 crow.end = cells[i];
19178                 continue;
19179             }
19180             // different row.
19181             rows.push(crow);
19182             crow = {
19183                 start: cells[i],
19184                 end : cells[i]
19185             };
19186             
19187         }
19188         
19189         rows.push(crow);
19190         ev.els = [];
19191         ev.rows = rows;
19192         ev.cells = cells;
19193         
19194         cells[0].events.push(ev);
19195         
19196         this.calevents.push(ev);
19197     },
19198     
19199     clearEvents: function() {
19200         
19201         if(!this.calevents){
19202             return;
19203         }
19204         
19205         Roo.each(this.cells.elements, function(c){
19206             c.row = 0;
19207             c.events = [];
19208             c.more = [];
19209         });
19210         
19211         Roo.each(this.calevents, function(e) {
19212             Roo.each(e.els, function(el) {
19213                 el.un('mouseenter' ,this.onEventEnter, this);
19214                 el.un('mouseleave' ,this.onEventLeave, this);
19215                 el.remove();
19216             },this);
19217         },this);
19218         
19219         Roo.each(Roo.select('.fc-more-event', true).elements, function(e){
19220             e.remove();
19221         });
19222         
19223     },
19224     
19225     renderEvents: function()
19226     {   
19227         var _this = this;
19228         
19229         this.cells.each(function(c) {
19230             
19231             if(c.row < 5){
19232                 return;
19233             }
19234             
19235             var ev = c.events;
19236             
19237             var r = 4;
19238             if(c.row != c.events.length){
19239                 r = 4 - (4 - (c.row - c.events.length));
19240             }
19241             
19242             c.events = ev.slice(0, r);
19243             c.more = ev.slice(r);
19244             
19245             if(c.more.length && c.more.length == 1){
19246                 c.events.push(c.more.pop());
19247             }
19248             
19249             c.row = (c.row - ev.length) + c.events.length + ((c.more.length) ? 1 : 0);
19250             
19251         });
19252             
19253         this.cells.each(function(c) {
19254             
19255             c.select('.fc-day-content div',true).first().setHeight(Math.max(34, c.row * 20));
19256             
19257             
19258             for (var e = 0; e < c.events.length; e++){
19259                 var ev = c.events[e];
19260                 var rows = ev.rows;
19261                 
19262                 for(var i = 0; i < rows.length; i++) {
19263                 
19264                     // how many rows should it span..
19265
19266                     var  cfg = {
19267                         cls : 'roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable',
19268                         style : 'position: absolute', // left: 387px; width: 121px; top: 359px;
19269
19270                         unselectable : "on",
19271                         cn : [
19272                             {
19273                                 cls: 'fc-event-inner',
19274                                 cn : [
19275     //                                {
19276     //                                  tag:'span',
19277     //                                  cls: 'fc-event-time',
19278     //                                  html : cells.length > 1 ? '' : ev.time
19279     //                                },
19280                                     {
19281                                       tag:'span',
19282                                       cls: 'fc-event-title',
19283                                       html : String.format('{0}', ev.title)
19284                                     }
19285
19286
19287                                 ]
19288                             },
19289                             {
19290                                 cls: 'ui-resizable-handle ui-resizable-e',
19291                                 html : '&nbsp;&nbsp;&nbsp'
19292                             }
19293
19294                         ]
19295                     };
19296
19297                     if (i == 0) {
19298                         cfg.cls += ' fc-event-start';
19299                     }
19300                     if ((i+1) == rows.length) {
19301                         cfg.cls += ' fc-event-end';
19302                     }
19303
19304                     var ctr = _this.el.select('.fc-event-container',true).first();
19305                     var cg = ctr.createChild(cfg);
19306
19307                     var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
19308                     var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
19309
19310                     var r = (c.more.length) ? 1 : 0;
19311                     cg.setXY([sbox.x +2, sbox.y + ((c.row - c.events.length - r + e) * 20)]);    
19312                     cg.setWidth(ebox.right - sbox.x -2);
19313
19314                     cg.on('mouseenter' ,_this.onEventEnter, _this, ev);
19315                     cg.on('mouseleave' ,_this.onEventLeave, _this, ev);
19316                     cg.on('click', _this.onEventClick, _this, ev);
19317
19318                     ev.els.push(cg);
19319                     
19320                 }
19321                 
19322             }
19323             
19324             
19325             if(c.more.length){
19326                 var  cfg = {
19327                     cls : 'fc-more-event roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable fc-event-start fc-event-end',
19328                     style : 'position: absolute',
19329                     unselectable : "on",
19330                     cn : [
19331                         {
19332                             cls: 'fc-event-inner',
19333                             cn : [
19334                                 {
19335                                   tag:'span',
19336                                   cls: 'fc-event-title',
19337                                   html : 'More'
19338                                 }
19339
19340
19341                             ]
19342                         },
19343                         {
19344                             cls: 'ui-resizable-handle ui-resizable-e',
19345                             html : '&nbsp;&nbsp;&nbsp'
19346                         }
19347
19348                     ]
19349                 };
19350
19351                 var ctr = _this.el.select('.fc-event-container',true).first();
19352                 var cg = ctr.createChild(cfg);
19353
19354                 var sbox = c.select('.fc-day-content',true).first().getBox();
19355                 var ebox = c.select('.fc-day-content',true).first().getBox();
19356                 //Roo.log(cg);
19357                 cg.setXY([sbox.x +2, sbox.y +((c.row - 1) * 20)]);    
19358                 cg.setWidth(ebox.right - sbox.x -2);
19359
19360                 cg.on('click', _this.onMoreEventClick, _this, c.more);
19361                 
19362             }
19363             
19364         });
19365         
19366         
19367         
19368     },
19369     
19370     onEventEnter: function (e, el,event,d) {
19371         this.fireEvent('evententer', this, el, event);
19372     },
19373     
19374     onEventLeave: function (e, el,event,d) {
19375         this.fireEvent('eventleave', this, el, event);
19376     },
19377     
19378     onEventClick: function (e, el,event,d) {
19379         this.fireEvent('eventclick', this, el, event);
19380     },
19381     
19382     onMonthChange: function () {
19383         this.store.load();
19384     },
19385     
19386     onMoreEventClick: function(e, el, more)
19387     {
19388         var _this = this;
19389         
19390         this.calpopover.placement = 'right';
19391         this.calpopover.setTitle('More');
19392         
19393         this.calpopover.setContent('');
19394         
19395         var ctr = this.calpopover.el.select('.popover-content', true).first();
19396         
19397         Roo.each(more, function(m){
19398             var cfg = {
19399                 cls : 'fc-event-hori fc-event-draggable',
19400                 html : m.title
19401             };
19402             var cg = ctr.createChild(cfg);
19403             
19404             cg.on('click', _this.onEventClick, _this, m);
19405         });
19406         
19407         this.calpopover.show(el);
19408         
19409         
19410     },
19411     
19412     onLoad: function () 
19413     {   
19414         this.calevents = [];
19415         var cal = this;
19416         
19417         if(this.store.getCount() > 0){
19418             this.store.data.each(function(d){
19419                cal.addItem({
19420                     id : d.data.id,
19421                     start: (typeof(d.data.start_dt) === 'string') ? new Date.parseDate(d.data.start_dt, 'Y-m-d H:i:s') : d.data.start_dt,
19422                     end : (typeof(d.data.end_dt) === 'string') ? new Date.parseDate(d.data.end_dt, 'Y-m-d H:i:s') : d.data.end_dt,
19423                     time : d.data.start_time,
19424                     title : d.data.title,
19425                     description : d.data.description,
19426                     venue : d.data.venue
19427                 });
19428             });
19429         }
19430         
19431         this.renderEvents();
19432         
19433         if(this.calevents.length && this.loadMask){
19434             this.maskEl.hide();
19435         }
19436     },
19437     
19438     onBeforeLoad: function()
19439     {
19440         this.clearEvents();
19441         if(this.loadMask){
19442             this.maskEl.show();
19443         }
19444     }
19445 });
19446
19447  
19448  /*
19449  * - LGPL
19450  *
19451  * element
19452  * 
19453  */
19454
19455 /**
19456  * @class Roo.bootstrap.Popover
19457  * @extends Roo.bootstrap.Component
19458  * Bootstrap Popover class
19459  * @cfg {String} html contents of the popover   (or false to use children..)
19460  * @cfg {String} title of popover (or false to hide)
19461  * @cfg {String} placement how it is placed
19462  * @cfg {String} trigger click || hover (or false to trigger manually)
19463  * @cfg {String} over what (parent or false to trigger manually.)
19464  * @cfg {Number} delay - delay before showing
19465  
19466  * @constructor
19467  * Create a new Popover
19468  * @param {Object} config The config object
19469  */
19470
19471 Roo.bootstrap.Popover = function(config){
19472     Roo.bootstrap.Popover.superclass.constructor.call(this, config);
19473     
19474     this.addEvents({
19475         // raw events
19476          /**
19477          * @event show
19478          * After the popover show
19479          * 
19480          * @param {Roo.bootstrap.Popover} this
19481          */
19482         "show" : true,
19483         /**
19484          * @event hide
19485          * After the popover hide
19486          * 
19487          * @param {Roo.bootstrap.Popover} this
19488          */
19489         "hide" : true
19490     });
19491 };
19492
19493 Roo.extend(Roo.bootstrap.Popover, Roo.bootstrap.Component,  {
19494     
19495     title: 'Fill in a title',
19496     html: false,
19497     
19498     placement : 'right',
19499     trigger : 'hover', // hover
19500     
19501     delay : 0,
19502     
19503     over: 'parent',
19504     
19505     can_build_overlaid : false,
19506     
19507     getChildContainer : function()
19508     {
19509         return this.el.select('.popover-content',true).first();
19510     },
19511     
19512     getAutoCreate : function(){
19513          
19514         var cfg = {
19515            cls : 'popover roo-dynamic',
19516            style: 'display:block',
19517            cn : [
19518                 {
19519                     cls : 'arrow'
19520                 },
19521                 {
19522                     cls : 'popover-inner',
19523                     cn : [
19524                         {
19525                             tag: 'h3',
19526                             cls: 'popover-title popover-header',
19527                             html : this.title
19528                         },
19529                         {
19530                             cls : 'popover-content popover-body',
19531                             html : this.html
19532                         }
19533                     ]
19534                     
19535                 }
19536            ]
19537         };
19538         
19539         return cfg;
19540     },
19541     setTitle: function(str)
19542     {
19543         this.title = str;
19544         this.el.select('.popover-title',true).first().dom.innerHTML = str;
19545     },
19546     setContent: function(str)
19547     {
19548         this.html = str;
19549         this.el.select('.popover-content',true).first().dom.innerHTML = str;
19550     },
19551     // as it get's added to the bottom of the page.
19552     onRender : function(ct, position)
19553     {
19554         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
19555         if(!this.el){
19556             var cfg = Roo.apply({},  this.getAutoCreate());
19557             cfg.id = Roo.id();
19558             
19559             if (this.cls) {
19560                 cfg.cls += ' ' + this.cls;
19561             }
19562             if (this.style) {
19563                 cfg.style = this.style;
19564             }
19565             //Roo.log("adding to ");
19566             this.el = Roo.get(document.body).createChild(cfg, position);
19567 //            Roo.log(this.el);
19568         }
19569         this.initEvents();
19570     },
19571     
19572     initEvents : function()
19573     {
19574         this.el.select('.popover-title',true).setVisibilityMode(Roo.Element.DISPLAY);
19575         this.el.enableDisplayMode('block');
19576         this.el.hide();
19577         if (this.over === false) {
19578             return; 
19579         }
19580         if (this.triggers === false) {
19581             return;
19582         }
19583         var on_el = (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
19584         var triggers = this.trigger ? this.trigger.split(' ') : [];
19585         Roo.each(triggers, function(trigger) {
19586         
19587             if (trigger == 'click') {
19588                 on_el.on('click', this.toggle, this);
19589             } else if (trigger != 'manual') {
19590                 var eventIn  = trigger == 'hover' ? 'mouseenter' : 'focusin';
19591                 var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout';
19592       
19593                 on_el.on(eventIn  ,this.enter, this);
19594                 on_el.on(eventOut, this.leave, this);
19595             }
19596         }, this);
19597         
19598     },
19599     
19600     
19601     // private
19602     timeout : null,
19603     hoverState : null,
19604     
19605     toggle : function () {
19606         this.hoverState == 'in' ? this.leave() : this.enter();
19607     },
19608     
19609     enter : function () {
19610         
19611         clearTimeout(this.timeout);
19612     
19613         this.hoverState = 'in';
19614     
19615         if (!this.delay || !this.delay.show) {
19616             this.show();
19617             return;
19618         }
19619         var _t = this;
19620         this.timeout = setTimeout(function () {
19621             if (_t.hoverState == 'in') {
19622                 _t.show();
19623             }
19624         }, this.delay.show)
19625     },
19626     
19627     leave : function() {
19628         clearTimeout(this.timeout);
19629     
19630         this.hoverState = 'out';
19631     
19632         if (!this.delay || !this.delay.hide) {
19633             this.hide();
19634             return;
19635         }
19636         var _t = this;
19637         this.timeout = setTimeout(function () {
19638             if (_t.hoverState == 'out') {
19639                 _t.hide();
19640             }
19641         }, this.delay.hide)
19642     },
19643     
19644     show : function (on_el)
19645     {
19646         if (!on_el) {
19647             on_el= (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
19648         }
19649         
19650         // set content.
19651         this.el.select('.popover-title',true).first().dom.innerHtml = this.title;
19652         if (this.html !== false) {
19653             this.el.select('.popover-content',true).first().dom.innerHtml = this.html;
19654         }
19655         this.el.removeClass([
19656             'fade','top','bottom', 'left', 'right','in',
19657             'bs-popover-top','bs-popover-bottom', 'bs-popover-left', 'bs-popover-right'
19658         ]);
19659         if (!this.title.length) {
19660             this.el.select('.popover-title',true).hide();
19661         }
19662         
19663         var placement = typeof this.placement == 'function' ?
19664             this.placement.call(this, this.el, on_el) :
19665             this.placement;
19666             
19667         var autoToken = /\s?auto?\s?/i;
19668         var autoPlace = autoToken.test(placement);
19669         if (autoPlace) {
19670             placement = placement.replace(autoToken, '') || 'top';
19671         }
19672         
19673         //this.el.detach()
19674         //this.el.setXY([0,0]);
19675         this.el.show();
19676         this.el.dom.style.display='block';
19677         this.el.addClass(placement);
19678         
19679         //this.el.appendTo(on_el);
19680         
19681         var p = this.getPosition();
19682         var box = this.el.getBox();
19683         
19684         if (autoPlace) {
19685             // fixme..
19686         }
19687         var align = Roo.bootstrap.Popover.alignment[placement];
19688         
19689 //        Roo.log(align);
19690         this.el.alignTo(on_el, align[0],align[1]);
19691         //var arrow = this.el.select('.arrow',true).first();
19692         //arrow.set(align[2], 
19693         
19694         this.el.addClass('in');
19695         
19696         
19697         if (this.el.hasClass('fade')) {
19698             // fade it?
19699         }
19700         
19701         this.hoverState = 'in';
19702         
19703         this.fireEvent('show', this);
19704         
19705     },
19706     hide : function()
19707     {
19708         this.el.setXY([0,0]);
19709         this.el.removeClass('in');
19710         this.el.hide();
19711         this.hoverState = null;
19712         
19713         this.fireEvent('hide', this);
19714     }
19715     
19716 });
19717
19718 Roo.bootstrap.Popover.alignment = {
19719     'left' : ['r-l', [-10,0], 'right bs-popover-right'],
19720     'right' : ['l-r', [10,0], 'left bs-popover-left'],
19721     'bottom' : ['t-b', [0,10], 'top bs-popover-top'],
19722     'top' : [ 'b-t', [0,-10], 'bottom bs-popover-bottom']
19723 };
19724
19725  /*
19726  * - LGPL
19727  *
19728  * Progress
19729  * 
19730  */
19731
19732 /**
19733  * @class Roo.bootstrap.Progress
19734  * @extends Roo.bootstrap.Component
19735  * Bootstrap Progress class
19736  * @cfg {Boolean} striped striped of the progress bar
19737  * @cfg {Boolean} active animated of the progress bar
19738  * 
19739  * 
19740  * @constructor
19741  * Create a new Progress
19742  * @param {Object} config The config object
19743  */
19744
19745 Roo.bootstrap.Progress = function(config){
19746     Roo.bootstrap.Progress.superclass.constructor.call(this, config);
19747 };
19748
19749 Roo.extend(Roo.bootstrap.Progress, Roo.bootstrap.Component,  {
19750     
19751     striped : false,
19752     active: false,
19753     
19754     getAutoCreate : function(){
19755         var cfg = {
19756             tag: 'div',
19757             cls: 'progress'
19758         };
19759         
19760         
19761         if(this.striped){
19762             cfg.cls += ' progress-striped';
19763         }
19764       
19765         if(this.active){
19766             cfg.cls += ' active';
19767         }
19768         
19769         
19770         return cfg;
19771     }
19772    
19773 });
19774
19775  
19776
19777  /*
19778  * - LGPL
19779  *
19780  * ProgressBar
19781  * 
19782  */
19783
19784 /**
19785  * @class Roo.bootstrap.ProgressBar
19786  * @extends Roo.bootstrap.Component
19787  * Bootstrap ProgressBar class
19788  * @cfg {Number} aria_valuenow aria-value now
19789  * @cfg {Number} aria_valuemin aria-value min
19790  * @cfg {Number} aria_valuemax aria-value max
19791  * @cfg {String} label label for the progress bar
19792  * @cfg {String} panel (success | info | warning | danger )
19793  * @cfg {String} role role of the progress bar
19794  * @cfg {String} sr_only text
19795  * 
19796  * 
19797  * @constructor
19798  * Create a new ProgressBar
19799  * @param {Object} config The config object
19800  */
19801
19802 Roo.bootstrap.ProgressBar = function(config){
19803     Roo.bootstrap.ProgressBar.superclass.constructor.call(this, config);
19804 };
19805
19806 Roo.extend(Roo.bootstrap.ProgressBar, Roo.bootstrap.Component,  {
19807     
19808     aria_valuenow : 0,
19809     aria_valuemin : 0,
19810     aria_valuemax : 100,
19811     label : false,
19812     panel : false,
19813     role : false,
19814     sr_only: false,
19815     
19816     getAutoCreate : function()
19817     {
19818         
19819         var cfg = {
19820             tag: 'div',
19821             cls: 'progress-bar',
19822             style: 'width:' + Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%'
19823         };
19824         
19825         if(this.sr_only){
19826             cfg.cn = {
19827                 tag: 'span',
19828                 cls: 'sr-only',
19829                 html: this.sr_only
19830             }
19831         }
19832         
19833         if(this.role){
19834             cfg.role = this.role;
19835         }
19836         
19837         if(this.aria_valuenow){
19838             cfg['aria-valuenow'] = this.aria_valuenow;
19839         }
19840         
19841         if(this.aria_valuemin){
19842             cfg['aria-valuemin'] = this.aria_valuemin;
19843         }
19844         
19845         if(this.aria_valuemax){
19846             cfg['aria-valuemax'] = this.aria_valuemax;
19847         }
19848         
19849         if(this.label && !this.sr_only){
19850             cfg.html = this.label;
19851         }
19852         
19853         if(this.panel){
19854             cfg.cls += ' progress-bar-' + this.panel;
19855         }
19856         
19857         return cfg;
19858     },
19859     
19860     update : function(aria_valuenow)
19861     {
19862         this.aria_valuenow = aria_valuenow;
19863         
19864         this.el.setStyle('width', Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%');
19865     }
19866    
19867 });
19868
19869  
19870
19871  /*
19872  * - LGPL
19873  *
19874  * column
19875  * 
19876  */
19877
19878 /**
19879  * @class Roo.bootstrap.TabGroup
19880  * @extends Roo.bootstrap.Column
19881  * Bootstrap Column class
19882  * @cfg {String} navId the navigation id (for use with navbars) - will be auto generated if it does not exist..
19883  * @cfg {Boolean} carousel true to make the group behave like a carousel
19884  * @cfg {Boolean} bullets show bullets for the panels
19885  * @cfg {Boolean} autoslide (true|false) auto slide .. default false
19886  * @cfg {Number} timer auto slide timer .. default 0 millisecond
19887  * @cfg {Boolean} showarrow (true|false) show arrow default true
19888  * 
19889  * @constructor
19890  * Create a new TabGroup
19891  * @param {Object} config The config object
19892  */
19893
19894 Roo.bootstrap.TabGroup = function(config){
19895     Roo.bootstrap.TabGroup.superclass.constructor.call(this, config);
19896     if (!this.navId) {
19897         this.navId = Roo.id();
19898     }
19899     this.tabs = [];
19900     Roo.bootstrap.TabGroup.register(this);
19901     
19902 };
19903
19904 Roo.extend(Roo.bootstrap.TabGroup, Roo.bootstrap.Column,  {
19905     
19906     carousel : false,
19907     transition : false,
19908     bullets : 0,
19909     timer : 0,
19910     autoslide : false,
19911     slideFn : false,
19912     slideOnTouch : false,
19913     showarrow : true,
19914     
19915     getAutoCreate : function()
19916     {
19917         var cfg = Roo.apply({}, Roo.bootstrap.TabGroup.superclass.getAutoCreate.call(this));
19918         
19919         cfg.cls += ' tab-content';
19920         
19921         if (this.carousel) {
19922             cfg.cls += ' carousel slide';
19923             
19924             cfg.cn = [{
19925                cls : 'carousel-inner',
19926                cn : []
19927             }];
19928         
19929             if(this.bullets  && !Roo.isTouch){
19930                 
19931                 var bullets = {
19932                     cls : 'carousel-bullets',
19933                     cn : []
19934                 };
19935                
19936                 if(this.bullets_cls){
19937                     bullets.cls = bullets.cls + ' ' + this.bullets_cls;
19938                 }
19939                 
19940                 bullets.cn.push({
19941                     cls : 'clear'
19942                 });
19943                 
19944                 cfg.cn[0].cn.push(bullets);
19945             }
19946             
19947             if(this.showarrow){
19948                 cfg.cn[0].cn.push({
19949                     tag : 'div',
19950                     class : 'carousel-arrow',
19951                     cn : [
19952                         {
19953                             tag : 'div',
19954                             class : 'carousel-prev',
19955                             cn : [
19956                                 {
19957                                     tag : 'i',
19958                                     class : 'fa fa-chevron-left'
19959                                 }
19960                             ]
19961                         },
19962                         {
19963                             tag : 'div',
19964                             class : 'carousel-next',
19965                             cn : [
19966                                 {
19967                                     tag : 'i',
19968                                     class : 'fa fa-chevron-right'
19969                                 }
19970                             ]
19971                         }
19972                     ]
19973                 });
19974             }
19975             
19976         }
19977         
19978         return cfg;
19979     },
19980     
19981     initEvents:  function()
19982     {
19983 //        if(Roo.isTouch && this.slideOnTouch && !this.showarrow){
19984 //            this.el.on("touchstart", this.onTouchStart, this);
19985 //        }
19986         
19987         if(this.autoslide){
19988             var _this = this;
19989             
19990             this.slideFn = window.setInterval(function() {
19991                 _this.showPanelNext();
19992             }, this.timer);
19993         }
19994         
19995         if(this.showarrow){
19996             this.el.select('.carousel-prev', true).first().on('click', this.showPanelPrev, this);
19997             this.el.select('.carousel-next', true).first().on('click', this.showPanelNext, this);
19998         }
19999         
20000         
20001     },
20002     
20003 //    onTouchStart : function(e, el, o)
20004 //    {
20005 //        if(!this.slideOnTouch || !Roo.isTouch || Roo.get(e.getTarget()).hasClass('roo-button-text')){
20006 //            return;
20007 //        }
20008 //        
20009 //        this.showPanelNext();
20010 //    },
20011     
20012     
20013     getChildContainer : function()
20014     {
20015         return this.carousel ? this.el.select('.carousel-inner', true).first() : this.el;
20016     },
20017     
20018     /**
20019     * register a Navigation item
20020     * @param {Roo.bootstrap.NavItem} the navitem to add
20021     */
20022     register : function(item)
20023     {
20024         this.tabs.push( item);
20025         item.navId = this.navId; // not really needed..
20026         this.addBullet();
20027     
20028     },
20029     
20030     getActivePanel : function()
20031     {
20032         var r = false;
20033         Roo.each(this.tabs, function(t) {
20034             if (t.active) {
20035                 r = t;
20036                 return false;
20037             }
20038             return null;
20039         });
20040         return r;
20041         
20042     },
20043     getPanelByName : function(n)
20044     {
20045         var r = false;
20046         Roo.each(this.tabs, function(t) {
20047             if (t.tabId == n) {
20048                 r = t;
20049                 return false;
20050             }
20051             return null;
20052         });
20053         return r;
20054     },
20055     indexOfPanel : function(p)
20056     {
20057         var r = false;
20058         Roo.each(this.tabs, function(t,i) {
20059             if (t.tabId == p.tabId) {
20060                 r = i;
20061                 return false;
20062             }
20063             return null;
20064         });
20065         return r;
20066     },
20067     /**
20068      * show a specific panel
20069      * @param {Roo.bootstrap.TabPanel|number|string} panel to change to (use the tabId to specify a specific one)
20070      * @return {boolean} false if panel was not shown (invalid entry or beforedeactivate fails.)
20071      */
20072     showPanel : function (pan)
20073     {
20074         if(this.transition || typeof(pan) == 'undefined'){
20075             Roo.log("waiting for the transitionend");
20076             return false;
20077         }
20078         
20079         if (typeof(pan) == 'number') {
20080             pan = this.tabs[pan];
20081         }
20082         
20083         if (typeof(pan) == 'string') {
20084             pan = this.getPanelByName(pan);
20085         }
20086         
20087         var cur = this.getActivePanel();
20088         
20089         if(!pan || !cur){
20090             Roo.log('pan or acitve pan is undefined');
20091             return false;
20092         }
20093         
20094         if (pan.tabId == this.getActivePanel().tabId) {
20095             return true;
20096         }
20097         
20098         if (false === cur.fireEvent('beforedeactivate')) {
20099             return false;
20100         }
20101         
20102         if(this.bullets > 0 && !Roo.isTouch){
20103             this.setActiveBullet(this.indexOfPanel(pan));
20104         }
20105         
20106         if (this.carousel && typeof(Roo.get(document.body).dom.style.transition) != 'undefined') {
20107             
20108             //class="carousel-item carousel-item-next carousel-item-left"
20109             
20110             this.transition = true;
20111             var dir = this.indexOfPanel(pan) > this.indexOfPanel(cur)  ? 'next' : 'prev';
20112             var lr = dir == 'next' ? 'left' : 'right';
20113             pan.el.addClass(dir); // or prev
20114             pan.el.addClass('carousel-item-' + dir); // or prev
20115             pan.el.dom.offsetWidth; // find the offset with - causing a reflow?
20116             cur.el.addClass(lr); // or right
20117             pan.el.addClass(lr);
20118             cur.el.addClass('carousel-item-' +lr); // or right
20119             pan.el.addClass('carousel-item-' +lr);
20120             
20121             
20122             var _this = this;
20123             cur.el.on('transitionend', function() {
20124                 Roo.log("trans end?");
20125                 
20126                 pan.el.removeClass([lr,dir, 'carousel-item-' + lr, 'carousel-item-' + dir]);
20127                 pan.setActive(true);
20128                 
20129                 cur.el.removeClass([lr, 'carousel-item-' + lr]);
20130                 cur.setActive(false);
20131                 
20132                 _this.transition = false;
20133                 
20134             }, this, { single:  true } );
20135             
20136             return true;
20137         }
20138         
20139         cur.setActive(false);
20140         pan.setActive(true);
20141         
20142         return true;
20143         
20144     },
20145     showPanelNext : function()
20146     {
20147         var i = this.indexOfPanel(this.getActivePanel());
20148         
20149         if (i >= this.tabs.length - 1 && !this.autoslide) {
20150             return;
20151         }
20152         
20153         if (i >= this.tabs.length - 1 && this.autoslide) {
20154             i = -1;
20155         }
20156         
20157         this.showPanel(this.tabs[i+1]);
20158     },
20159     
20160     showPanelPrev : function()
20161     {
20162         var i = this.indexOfPanel(this.getActivePanel());
20163         
20164         if (i  < 1 && !this.autoslide) {
20165             return;
20166         }
20167         
20168         if (i < 1 && this.autoslide) {
20169             i = this.tabs.length;
20170         }
20171         
20172         this.showPanel(this.tabs[i-1]);
20173     },
20174     
20175     
20176     addBullet: function()
20177     {
20178         if(!this.bullets || Roo.isTouch){
20179             return;
20180         }
20181         var ctr = this.el.select('.carousel-bullets',true).first();
20182         var i = this.el.select('.carousel-bullets .bullet',true).getCount() ;
20183         var bullet = ctr.createChild({
20184             cls : 'bullet bullet-' + i
20185         },ctr.dom.lastChild);
20186         
20187         
20188         var _this = this;
20189         
20190         bullet.on('click', (function(e, el, o, ii, t){
20191
20192             e.preventDefault();
20193
20194             this.showPanel(ii);
20195
20196             if(this.autoslide && this.slideFn){
20197                 clearInterval(this.slideFn);
20198                 this.slideFn = window.setInterval(function() {
20199                     _this.showPanelNext();
20200                 }, this.timer);
20201             }
20202
20203         }).createDelegate(this, [i, bullet], true));
20204                 
20205         
20206     },
20207      
20208     setActiveBullet : function(i)
20209     {
20210         if(Roo.isTouch){
20211             return;
20212         }
20213         
20214         Roo.each(this.el.select('.bullet', true).elements, function(el){
20215             el.removeClass('selected');
20216         });
20217
20218         var bullet = this.el.select('.bullet-' + i, true).first();
20219         
20220         if(!bullet){
20221             return;
20222         }
20223         
20224         bullet.addClass('selected');
20225     }
20226     
20227     
20228   
20229 });
20230
20231  
20232
20233  
20234  
20235 Roo.apply(Roo.bootstrap.TabGroup, {
20236     
20237     groups: {},
20238      /**
20239     * register a Navigation Group
20240     * @param {Roo.bootstrap.NavGroup} the navgroup to add
20241     */
20242     register : function(navgrp)
20243     {
20244         this.groups[navgrp.navId] = navgrp;
20245         
20246     },
20247     /**
20248     * fetch a Navigation Group based on the navigation ID
20249     * if one does not exist , it will get created.
20250     * @param {string} the navgroup to add
20251     * @returns {Roo.bootstrap.NavGroup} the navgroup 
20252     */
20253     get: function(navId) {
20254         if (typeof(this.groups[navId]) == 'undefined') {
20255             this.register(new Roo.bootstrap.TabGroup({ navId : navId }));
20256         }
20257         return this.groups[navId] ;
20258     }
20259     
20260     
20261     
20262 });
20263
20264  /*
20265  * - LGPL
20266  *
20267  * TabPanel
20268  * 
20269  */
20270
20271 /**
20272  * @class Roo.bootstrap.TabPanel
20273  * @extends Roo.bootstrap.Component
20274  * Bootstrap TabPanel class
20275  * @cfg {Boolean} active panel active
20276  * @cfg {String} html panel content
20277  * @cfg {String} tabId  unique tab ID (will be autogenerated if not set. - used to match TabItem to Panel)
20278  * @cfg {String} navId The Roo.bootstrap.NavGroup which triggers show hide ()
20279  * @cfg {String} href click to link..
20280  * @cfg {Boolean} touchSlide if swiping slides tab to next panel (default off)
20281  * 
20282  * 
20283  * @constructor
20284  * Create a new TabPanel
20285  * @param {Object} config The config object
20286  */
20287
20288 Roo.bootstrap.TabPanel = function(config){
20289     Roo.bootstrap.TabPanel.superclass.constructor.call(this, config);
20290     this.addEvents({
20291         /**
20292              * @event changed
20293              * Fires when the active status changes
20294              * @param {Roo.bootstrap.TabPanel} this
20295              * @param {Boolean} state the new state
20296             
20297          */
20298         'changed': true,
20299         /**
20300              * @event beforedeactivate
20301              * Fires before a tab is de-activated - can be used to do validation on a form.
20302              * @param {Roo.bootstrap.TabPanel} this
20303              * @return {Boolean} false if there is an error
20304             
20305          */
20306         'beforedeactivate': true
20307      });
20308     
20309     this.tabId = this.tabId || Roo.id();
20310   
20311 };
20312
20313 Roo.extend(Roo.bootstrap.TabPanel, Roo.bootstrap.Component,  {
20314     
20315     active: false,
20316     html: false,
20317     tabId: false,
20318     navId : false,
20319     href : '',
20320     touchSlide : false,
20321     getAutoCreate : function(){
20322         
20323         
20324         var cfg = {
20325             tag: 'div',
20326             // item is needed for carousel - not sure if it has any effect otherwise
20327             cls: 'carousel-item tab-pane item' + ((this.href.length) ? ' clickable ' : ''),
20328             html: this.html || ''
20329         };
20330         
20331         if(this.active){
20332             cfg.cls += ' active';
20333         }
20334         
20335         if(this.tabId){
20336             cfg.tabId = this.tabId;
20337         }
20338         
20339         
20340         
20341         return cfg;
20342     },
20343     
20344     initEvents:  function()
20345     {
20346         var p = this.parent();
20347         
20348         this.navId = this.navId || p.navId;
20349         
20350         if (typeof(this.navId) != 'undefined') {
20351             // not really needed.. but just in case.. parent should be a NavGroup.
20352             var tg = Roo.bootstrap.TabGroup.get(this.navId);
20353             
20354             tg.register(this);
20355             
20356             var i = tg.tabs.length - 1;
20357             
20358             if(this.active && tg.bullets > 0 && i < tg.bullets){
20359                 tg.setActiveBullet(i);
20360             }
20361         }
20362         
20363         this.el.on('click', this.onClick, this);
20364         
20365         if(Roo.isTouch && this.touchSlide){
20366             this.el.on("touchstart", this.onTouchStart, this);
20367             this.el.on("touchmove", this.onTouchMove, this);
20368             this.el.on("touchend", this.onTouchEnd, this);
20369         }
20370         
20371     },
20372     
20373     onRender : function(ct, position)
20374     {
20375         Roo.bootstrap.TabPanel.superclass.onRender.call(this, ct, position);
20376     },
20377     
20378     setActive : function(state)
20379     {
20380         Roo.log("panel - set active " + this.tabId + "=" + state);
20381         
20382         this.active = state;
20383         if (!state) {
20384             this.el.removeClass('active');
20385             
20386         } else  if (!this.el.hasClass('active')) {
20387             this.el.addClass('active');
20388         }
20389         
20390         this.fireEvent('changed', this, state);
20391     },
20392     
20393     onClick : function(e)
20394     {
20395         e.preventDefault();
20396         
20397         if(!this.href.length){
20398             return;
20399         }
20400         
20401         window.location.href = this.href;
20402     },
20403     
20404     startX : 0,
20405     startY : 0,
20406     endX : 0,
20407     endY : 0,
20408     swiping : false,
20409     
20410     onTouchStart : function(e)
20411     {
20412         this.swiping = false;
20413         
20414         this.startX = e.browserEvent.touches[0].clientX;
20415         this.startY = e.browserEvent.touches[0].clientY;
20416     },
20417     
20418     onTouchMove : function(e)
20419     {
20420         this.swiping = true;
20421         
20422         this.endX = e.browserEvent.touches[0].clientX;
20423         this.endY = e.browserEvent.touches[0].clientY;
20424     },
20425     
20426     onTouchEnd : function(e)
20427     {
20428         if(!this.swiping){
20429             this.onClick(e);
20430             return;
20431         }
20432         
20433         var tabGroup = this.parent();
20434         
20435         if(this.endX > this.startX){ // swiping right
20436             tabGroup.showPanelPrev();
20437             return;
20438         }
20439         
20440         if(this.startX > this.endX){ // swiping left
20441             tabGroup.showPanelNext();
20442             return;
20443         }
20444     }
20445     
20446     
20447 });
20448  
20449
20450  
20451
20452  /*
20453  * - LGPL
20454  *
20455  * DateField
20456  * 
20457  */
20458
20459 /**
20460  * @class Roo.bootstrap.DateField
20461  * @extends Roo.bootstrap.Input
20462  * Bootstrap DateField class
20463  * @cfg {Number} weekStart default 0
20464  * @cfg {String} viewMode default empty, (months|years)
20465  * @cfg {String} minViewMode default empty, (months|years)
20466  * @cfg {Number} startDate default -Infinity
20467  * @cfg {Number} endDate default Infinity
20468  * @cfg {Boolean} todayHighlight default false
20469  * @cfg {Boolean} todayBtn default false
20470  * @cfg {Boolean} calendarWeeks default false
20471  * @cfg {Object} daysOfWeekDisabled default empty
20472  * @cfg {Boolean} singleMode default false (true | false)
20473  * 
20474  * @cfg {Boolean} keyboardNavigation default true
20475  * @cfg {String} language default en
20476  * 
20477  * @constructor
20478  * Create a new DateField
20479  * @param {Object} config The config object
20480  */
20481
20482 Roo.bootstrap.DateField = function(config){
20483     Roo.bootstrap.DateField.superclass.constructor.call(this, config);
20484      this.addEvents({
20485             /**
20486              * @event show
20487              * Fires when this field show.
20488              * @param {Roo.bootstrap.DateField} this
20489              * @param {Mixed} date The date value
20490              */
20491             show : true,
20492             /**
20493              * @event show
20494              * Fires when this field hide.
20495              * @param {Roo.bootstrap.DateField} this
20496              * @param {Mixed} date The date value
20497              */
20498             hide : true,
20499             /**
20500              * @event select
20501              * Fires when select a date.
20502              * @param {Roo.bootstrap.DateField} this
20503              * @param {Mixed} date The date value
20504              */
20505             select : true,
20506             /**
20507              * @event beforeselect
20508              * Fires when before select a date.
20509              * @param {Roo.bootstrap.DateField} this
20510              * @param {Mixed} date The date value
20511              */
20512             beforeselect : true
20513         });
20514 };
20515
20516 Roo.extend(Roo.bootstrap.DateField, Roo.bootstrap.Input,  {
20517     
20518     /**
20519      * @cfg {String} format
20520      * The default date format string which can be overriden for localization support.  The format must be
20521      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
20522      */
20523     format : "m/d/y",
20524     /**
20525      * @cfg {String} altFormats
20526      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
20527      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
20528      */
20529     altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
20530     
20531     weekStart : 0,
20532     
20533     viewMode : '',
20534     
20535     minViewMode : '',
20536     
20537     todayHighlight : false,
20538     
20539     todayBtn: false,
20540     
20541     language: 'en',
20542     
20543     keyboardNavigation: true,
20544     
20545     calendarWeeks: false,
20546     
20547     startDate: -Infinity,
20548     
20549     endDate: Infinity,
20550     
20551     daysOfWeekDisabled: [],
20552     
20553     _events: [],
20554     
20555     singleMode : false,
20556     
20557     UTCDate: function()
20558     {
20559         return new Date(Date.UTC.apply(Date, arguments));
20560     },
20561     
20562     UTCToday: function()
20563     {
20564         var today = new Date();
20565         return this.UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
20566     },
20567     
20568     getDate: function() {
20569             var d = this.getUTCDate();
20570             return new Date(d.getTime() + (d.getTimezoneOffset()*60000));
20571     },
20572     
20573     getUTCDate: function() {
20574             return this.date;
20575     },
20576     
20577     setDate: function(d) {
20578             this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000)));
20579     },
20580     
20581     setUTCDate: function(d) {
20582             this.date = d;
20583             this.setValue(this.formatDate(this.date));
20584     },
20585         
20586     onRender: function(ct, position)
20587     {
20588         
20589         Roo.bootstrap.DateField.superclass.onRender.call(this, ct, position);
20590         
20591         this.language = this.language || 'en';
20592         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : this.language.split('-')[0];
20593         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : "en";
20594         
20595         this.isRTL = Roo.bootstrap.DateField.dates[this.language].rtl || false;
20596         this.format = this.format || 'm/d/y';
20597         this.isInline = false;
20598         this.isInput = true;
20599         this.component = this.el.select('.add-on', true).first() || false;
20600         this.component = (this.component && this.component.length === 0) ? false : this.component;
20601         this.hasInput = this.component && this.inputEl().length;
20602         
20603         if (typeof(this.minViewMode === 'string')) {
20604             switch (this.minViewMode) {
20605                 case 'months':
20606                     this.minViewMode = 1;
20607                     break;
20608                 case 'years':
20609                     this.minViewMode = 2;
20610                     break;
20611                 default:
20612                     this.minViewMode = 0;
20613                     break;
20614             }
20615         }
20616         
20617         if (typeof(this.viewMode === 'string')) {
20618             switch (this.viewMode) {
20619                 case 'months':
20620                     this.viewMode = 1;
20621                     break;
20622                 case 'years':
20623                     this.viewMode = 2;
20624                     break;
20625                 default:
20626                     this.viewMode = 0;
20627                     break;
20628             }
20629         }
20630                 
20631         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.DateField.template);
20632         
20633 //        this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.DateField.template);
20634         
20635         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
20636         
20637         this.picker().on('mousedown', this.onMousedown, this);
20638         this.picker().on('click', this.onClick, this);
20639         
20640         this.picker().addClass('datepicker-dropdown');
20641         
20642         this.startViewMode = this.viewMode;
20643         
20644         if(this.singleMode){
20645             Roo.each(this.picker().select('thead > tr > th', true).elements, function(v){
20646                 v.setVisibilityMode(Roo.Element.DISPLAY);
20647                 v.hide();
20648             });
20649             
20650             Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
20651                 v.setStyle('width', '189px');
20652             });
20653         }
20654         
20655         Roo.each(this.picker().select('tfoot th.today', true).elements, function(v){
20656             if(!this.calendarWeeks){
20657                 v.remove();
20658                 return;
20659             }
20660             
20661             v.dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
20662             v.attr('colspan', function(i, val){
20663                 return parseInt(val) + 1;
20664             });
20665         });
20666                         
20667         
20668         this.weekEnd = this.weekStart === 0 ? 6 : this.weekStart - 1;
20669         
20670         this.setStartDate(this.startDate);
20671         this.setEndDate(this.endDate);
20672         
20673         this.setDaysOfWeekDisabled(this.daysOfWeekDisabled);
20674         
20675         this.fillDow();
20676         this.fillMonths();
20677         this.update();
20678         this.showMode();
20679         
20680         if(this.isInline) {
20681             this.showPopup();
20682         }
20683     },
20684     
20685     picker : function()
20686     {
20687         return this.pickerEl;
20688 //        return this.el.select('.datepicker', true).first();
20689     },
20690     
20691     fillDow: function()
20692     {
20693         var dowCnt = this.weekStart;
20694         
20695         var dow = {
20696             tag: 'tr',
20697             cn: [
20698                 
20699             ]
20700         };
20701         
20702         if(this.calendarWeeks){
20703             dow.cn.push({
20704                 tag: 'th',
20705                 cls: 'cw',
20706                 html: '&nbsp;'
20707             })
20708         }
20709         
20710         while (dowCnt < this.weekStart + 7) {
20711             dow.cn.push({
20712                 tag: 'th',
20713                 cls: 'dow',
20714                 html: Roo.bootstrap.DateField.dates[this.language].daysMin[(dowCnt++)%7]
20715             });
20716         }
20717         
20718         this.picker().select('>.datepicker-days thead', true).first().createChild(dow);
20719     },
20720     
20721     fillMonths: function()
20722     {    
20723         var i = 0;
20724         var months = this.picker().select('>.datepicker-months td', true).first();
20725         
20726         months.dom.innerHTML = '';
20727         
20728         while (i < 12) {
20729             var month = {
20730                 tag: 'span',
20731                 cls: 'month',
20732                 html: Roo.bootstrap.DateField.dates[this.language].monthsShort[i++]
20733             };
20734             
20735             months.createChild(month);
20736         }
20737         
20738     },
20739     
20740     update: function()
20741     {
20742         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;
20743         
20744         if (this.date < this.startDate) {
20745             this.viewDate = new Date(this.startDate);
20746         } else if (this.date > this.endDate) {
20747             this.viewDate = new Date(this.endDate);
20748         } else {
20749             this.viewDate = new Date(this.date);
20750         }
20751         
20752         this.fill();
20753     },
20754     
20755     fill: function() 
20756     {
20757         var d = new Date(this.viewDate),
20758                 year = d.getUTCFullYear(),
20759                 month = d.getUTCMonth(),
20760                 startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity,
20761                 startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
20762                 endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
20763                 endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
20764                 currentDate = this.date && this.date.valueOf(),
20765                 today = this.UTCToday();
20766         
20767         this.picker().select('>.datepicker-days thead th.switch', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].months[month]+' '+year;
20768         
20769 //        this.picker().select('>tfoot th.today', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
20770         
20771 //        this.picker.select('>tfoot th.today').
20772 //                                              .text(dates[this.language].today)
20773 //                                              .toggle(this.todayBtn !== false);
20774     
20775         this.updateNavArrows();
20776         this.fillMonths();
20777                                                 
20778         var prevMonth = this.UTCDate(year, month-1, 28,0,0,0,0),
20779         
20780         day = prevMonth.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
20781          
20782         prevMonth.setUTCDate(day);
20783         
20784         prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7);
20785         
20786         var nextMonth = new Date(prevMonth);
20787         
20788         nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
20789         
20790         nextMonth = nextMonth.valueOf();
20791         
20792         var fillMonths = false;
20793         
20794         this.picker().select('>.datepicker-days tbody',true).first().dom.innerHTML = '';
20795         
20796         while(prevMonth.valueOf() <= nextMonth) {
20797             var clsName = '';
20798             
20799             if (prevMonth.getUTCDay() === this.weekStart) {
20800                 if(fillMonths){
20801                     this.picker().select('>.datepicker-days tbody',true).first().createChild(fillMonths);
20802                 }
20803                     
20804                 fillMonths = {
20805                     tag: 'tr',
20806                     cn: []
20807                 };
20808                 
20809                 if(this.calendarWeeks){
20810                     // ISO 8601: First week contains first thursday.
20811                     // ISO also states week starts on Monday, but we can be more abstract here.
20812                     var
20813                     // Start of current week: based on weekstart/current date
20814                     ws = new Date(+prevMonth + (this.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5),
20815                     // Thursday of this week
20816                     th = new Date(+ws + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
20817                     // First Thursday of year, year from thursday
20818                     yth = new Date(+(yth = this.UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5),
20819                     // Calendar week: ms between thursdays, div ms per day, div 7 days
20820                     calWeek =  (th - yth) / 864e5 / 7 + 1;
20821                     
20822                     fillMonths.cn.push({
20823                         tag: 'td',
20824                         cls: 'cw',
20825                         html: calWeek
20826                     });
20827                 }
20828             }
20829             
20830             if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
20831                 clsName += ' old';
20832             } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
20833                 clsName += ' new';
20834             }
20835             if (this.todayHighlight &&
20836                 prevMonth.getUTCFullYear() == today.getFullYear() &&
20837                 prevMonth.getUTCMonth() == today.getMonth() &&
20838                 prevMonth.getUTCDate() == today.getDate()) {
20839                 clsName += ' today';
20840             }
20841             
20842             if (currentDate && prevMonth.valueOf() === currentDate) {
20843                 clsName += ' active';
20844             }
20845             
20846             if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate ||
20847                     this.daysOfWeekDisabled.indexOf(prevMonth.getUTCDay()) !== -1) {
20848                     clsName += ' disabled';
20849             }
20850             
20851             fillMonths.cn.push({
20852                 tag: 'td',
20853                 cls: 'day ' + clsName,
20854                 html: prevMonth.getDate()
20855             });
20856             
20857             prevMonth.setDate(prevMonth.getDate()+1);
20858         }
20859           
20860         var currentYear = this.date && this.date.getUTCFullYear();
20861         var currentMonth = this.date && this.date.getUTCMonth();
20862         
20863         this.picker().select('>.datepicker-months th.switch',true).first().dom.innerHTML = year;
20864         
20865         Roo.each(this.picker().select('>.datepicker-months tbody span',true).elements, function(v,k){
20866             v.removeClass('active');
20867             
20868             if(currentYear === year && k === currentMonth){
20869                 v.addClass('active');
20870             }
20871             
20872             if (year < startYear || year > endYear || (year == startYear && k < startMonth) || (year == endYear && k > endMonth)) {
20873                 v.addClass('disabled');
20874             }
20875             
20876         });
20877         
20878         
20879         year = parseInt(year/10, 10) * 10;
20880         
20881         this.picker().select('>.datepicker-years th.switch', true).first().dom.innerHTML = year + '-' + (year + 9);
20882         
20883         this.picker().select('>.datepicker-years tbody td',true).first().dom.innerHTML = '';
20884         
20885         year -= 1;
20886         for (var i = -1; i < 11; i++) {
20887             this.picker().select('>.datepicker-years tbody td',true).first().createChild({
20888                 tag: 'span',
20889                 cls: 'year' + (i === -1 || i === 10 ? ' old' : '') + (currentYear === year ? ' active' : '') + (year < startYear || year > endYear ? ' disabled' : ''),
20890                 html: year
20891             });
20892             
20893             year += 1;
20894         }
20895     },
20896     
20897     showMode: function(dir) 
20898     {
20899         if (dir) {
20900             this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir));
20901         }
20902         
20903         Roo.each(this.picker().select('>div',true).elements, function(v){
20904             v.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
20905             v.hide();
20906         });
20907         this.picker().select('>.datepicker-'+Roo.bootstrap.DateField.modes[this.viewMode].clsName, true).first().show();
20908     },
20909     
20910     place: function()
20911     {
20912         if(this.isInline) {
20913             return;
20914         }
20915         
20916         this.picker().removeClass(['bottom', 'top']);
20917         
20918         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
20919             /*
20920              * place to the top of element!
20921              *
20922              */
20923             
20924             this.picker().addClass('top');
20925             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
20926             
20927             return;
20928         }
20929         
20930         this.picker().addClass('bottom');
20931         
20932         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
20933     },
20934     
20935     parseDate : function(value)
20936     {
20937         if(!value || value instanceof Date){
20938             return value;
20939         }
20940         var v = Date.parseDate(value, this.format);
20941         if (!v && (this.useIso || value.match(/^(\d{4})-0?(\d+)-0?(\d+)/))) {
20942             v = Date.parseDate(value, 'Y-m-d');
20943         }
20944         if(!v && this.altFormats){
20945             if(!this.altFormatsArray){
20946                 this.altFormatsArray = this.altFormats.split("|");
20947             }
20948             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
20949                 v = Date.parseDate(value, this.altFormatsArray[i]);
20950             }
20951         }
20952         return v;
20953     },
20954     
20955     formatDate : function(date, fmt)
20956     {   
20957         return (!date || !(date instanceof Date)) ?
20958         date : date.dateFormat(fmt || this.format);
20959     },
20960     
20961     onFocus : function()
20962     {
20963         Roo.bootstrap.DateField.superclass.onFocus.call(this);
20964         this.showPopup();
20965     },
20966     
20967     onBlur : function()
20968     {
20969         Roo.bootstrap.DateField.superclass.onBlur.call(this);
20970         
20971         var d = this.inputEl().getValue();
20972         
20973         this.setValue(d);
20974                 
20975         this.hidePopup();
20976     },
20977     
20978     showPopup : function()
20979     {
20980         this.picker().show();
20981         this.update();
20982         this.place();
20983         
20984         this.fireEvent('showpopup', this, this.date);
20985     },
20986     
20987     hidePopup : function()
20988     {
20989         if(this.isInline) {
20990             return;
20991         }
20992         this.picker().hide();
20993         this.viewMode = this.startViewMode;
20994         this.showMode();
20995         
20996         this.fireEvent('hidepopup', this, this.date);
20997         
20998     },
20999     
21000     onMousedown: function(e)
21001     {
21002         e.stopPropagation();
21003         e.preventDefault();
21004     },
21005     
21006     keyup: function(e)
21007     {
21008         Roo.bootstrap.DateField.superclass.keyup.call(this);
21009         this.update();
21010     },
21011
21012     setValue: function(v)
21013     {
21014         if(this.fireEvent('beforeselect', this, v) !== false){
21015             var d = new Date(this.parseDate(v) ).clearTime();
21016         
21017             if(isNaN(d.getTime())){
21018                 this.date = this.viewDate = '';
21019                 Roo.bootstrap.DateField.superclass.setValue.call(this, '');
21020                 return;
21021             }
21022
21023             v = this.formatDate(d);
21024
21025             Roo.bootstrap.DateField.superclass.setValue.call(this, v);
21026
21027             this.date = new Date(d.getTime() - d.getTimezoneOffset()*60000);
21028
21029             this.update();
21030
21031             this.fireEvent('select', this, this.date);
21032         }
21033     },
21034     
21035     getValue: function()
21036     {
21037         return this.formatDate(this.date);
21038     },
21039     
21040     fireKey: function(e)
21041     {
21042         if (!this.picker().isVisible()){
21043             if (e.keyCode == 27) { // allow escape to hide and re-show picker
21044                 this.showPopup();
21045             }
21046             return;
21047         }
21048         
21049         var dateChanged = false,
21050         dir, day, month,
21051         newDate, newViewDate;
21052         
21053         switch(e.keyCode){
21054             case 27: // escape
21055                 this.hidePopup();
21056                 e.preventDefault();
21057                 break;
21058             case 37: // left
21059             case 39: // right
21060                 if (!this.keyboardNavigation) {
21061                     break;
21062                 }
21063                 dir = e.keyCode == 37 ? -1 : 1;
21064                 
21065                 if (e.ctrlKey){
21066                     newDate = this.moveYear(this.date, dir);
21067                     newViewDate = this.moveYear(this.viewDate, dir);
21068                 } else if (e.shiftKey){
21069                     newDate = this.moveMonth(this.date, dir);
21070                     newViewDate = this.moveMonth(this.viewDate, dir);
21071                 } else {
21072                     newDate = new Date(this.date);
21073                     newDate.setUTCDate(this.date.getUTCDate() + dir);
21074                     newViewDate = new Date(this.viewDate);
21075                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
21076                 }
21077                 if (this.dateWithinRange(newDate)){
21078                     this.date = newDate;
21079                     this.viewDate = newViewDate;
21080                     this.setValue(this.formatDate(this.date));
21081 //                    this.update();
21082                     e.preventDefault();
21083                     dateChanged = true;
21084                 }
21085                 break;
21086             case 38: // up
21087             case 40: // down
21088                 if (!this.keyboardNavigation) {
21089                     break;
21090                 }
21091                 dir = e.keyCode == 38 ? -1 : 1;
21092                 if (e.ctrlKey){
21093                     newDate = this.moveYear(this.date, dir);
21094                     newViewDate = this.moveYear(this.viewDate, dir);
21095                 } else if (e.shiftKey){
21096                     newDate = this.moveMonth(this.date, dir);
21097                     newViewDate = this.moveMonth(this.viewDate, dir);
21098                 } else {
21099                     newDate = new Date(this.date);
21100                     newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
21101                     newViewDate = new Date(this.viewDate);
21102                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
21103                 }
21104                 if (this.dateWithinRange(newDate)){
21105                     this.date = newDate;
21106                     this.viewDate = newViewDate;
21107                     this.setValue(this.formatDate(this.date));
21108 //                    this.update();
21109                     e.preventDefault();
21110                     dateChanged = true;
21111                 }
21112                 break;
21113             case 13: // enter
21114                 this.setValue(this.formatDate(this.date));
21115                 this.hidePopup();
21116                 e.preventDefault();
21117                 break;
21118             case 9: // tab
21119                 this.setValue(this.formatDate(this.date));
21120                 this.hidePopup();
21121                 break;
21122             case 16: // shift
21123             case 17: // ctrl
21124             case 18: // alt
21125                 break;
21126             default :
21127                 this.hidePopup();
21128                 
21129         }
21130     },
21131     
21132     
21133     onClick: function(e) 
21134     {
21135         e.stopPropagation();
21136         e.preventDefault();
21137         
21138         var target = e.getTarget();
21139         
21140         if(target.nodeName.toLowerCase() === 'i'){
21141             target = Roo.get(target).dom.parentNode;
21142         }
21143         
21144         var nodeName = target.nodeName;
21145         var className = target.className;
21146         var html = target.innerHTML;
21147         //Roo.log(nodeName);
21148         
21149         switch(nodeName.toLowerCase()) {
21150             case 'th':
21151                 switch(className) {
21152                     case 'switch':
21153                         this.showMode(1);
21154                         break;
21155                     case 'prev':
21156                     case 'next':
21157                         var dir = Roo.bootstrap.DateField.modes[this.viewMode].navStep * (className == 'prev' ? -1 : 1);
21158                         switch(this.viewMode){
21159                                 case 0:
21160                                         this.viewDate = this.moveMonth(this.viewDate, dir);
21161                                         break;
21162                                 case 1:
21163                                 case 2:
21164                                         this.viewDate = this.moveYear(this.viewDate, dir);
21165                                         break;
21166                         }
21167                         this.fill();
21168                         break;
21169                     case 'today':
21170                         var date = new Date();
21171                         this.date = this.UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
21172 //                        this.fill()
21173                         this.setValue(this.formatDate(this.date));
21174                         
21175                         this.hidePopup();
21176                         break;
21177                 }
21178                 break;
21179             case 'span':
21180                 if (className.indexOf('disabled') < 0) {
21181                     this.viewDate.setUTCDate(1);
21182                     if (className.indexOf('month') > -1) {
21183                         this.viewDate.setUTCMonth(Roo.bootstrap.DateField.dates[this.language].monthsShort.indexOf(html));
21184                     } else {
21185                         var year = parseInt(html, 10) || 0;
21186                         this.viewDate.setUTCFullYear(year);
21187                         
21188                     }
21189                     
21190                     if(this.singleMode){
21191                         this.setValue(this.formatDate(this.viewDate));
21192                         this.hidePopup();
21193                         return;
21194                     }
21195                     
21196                     this.showMode(-1);
21197                     this.fill();
21198                 }
21199                 break;
21200                 
21201             case 'td':
21202                 //Roo.log(className);
21203                 if (className.indexOf('day') > -1 && className.indexOf('disabled') < 0 ){
21204                     var day = parseInt(html, 10) || 1;
21205                     var year = this.viewDate.getUTCFullYear(),
21206                         month = this.viewDate.getUTCMonth();
21207
21208                     if (className.indexOf('old') > -1) {
21209                         if(month === 0 ){
21210                             month = 11;
21211                             year -= 1;
21212                         }else{
21213                             month -= 1;
21214                         }
21215                     } else if (className.indexOf('new') > -1) {
21216                         if (month == 11) {
21217                             month = 0;
21218                             year += 1;
21219                         } else {
21220                             month += 1;
21221                         }
21222                     }
21223                     //Roo.log([year,month,day]);
21224                     this.date = this.UTCDate(year, month, day,0,0,0,0);
21225                     this.viewDate = this.UTCDate(year, month, Math.min(28, day),0,0,0,0);
21226 //                    this.fill();
21227                     //Roo.log(this.formatDate(this.date));
21228                     this.setValue(this.formatDate(this.date));
21229                     this.hidePopup();
21230                 }
21231                 break;
21232         }
21233     },
21234     
21235     setStartDate: function(startDate)
21236     {
21237         this.startDate = startDate || -Infinity;
21238         if (this.startDate !== -Infinity) {
21239             this.startDate = this.parseDate(this.startDate);
21240         }
21241         this.update();
21242         this.updateNavArrows();
21243     },
21244
21245     setEndDate: function(endDate)
21246     {
21247         this.endDate = endDate || Infinity;
21248         if (this.endDate !== Infinity) {
21249             this.endDate = this.parseDate(this.endDate);
21250         }
21251         this.update();
21252         this.updateNavArrows();
21253     },
21254     
21255     setDaysOfWeekDisabled: function(daysOfWeekDisabled)
21256     {
21257         this.daysOfWeekDisabled = daysOfWeekDisabled || [];
21258         if (typeof(this.daysOfWeekDisabled) !== 'object') {
21259             this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
21260         }
21261         this.daysOfWeekDisabled = this.daysOfWeekDisabled.map(function (d) {
21262             return parseInt(d, 10);
21263         });
21264         this.update();
21265         this.updateNavArrows();
21266     },
21267     
21268     updateNavArrows: function() 
21269     {
21270         if(this.singleMode){
21271             return;
21272         }
21273         
21274         var d = new Date(this.viewDate),
21275         year = d.getUTCFullYear(),
21276         month = d.getUTCMonth();
21277         
21278         Roo.each(this.picker().select('.prev', true).elements, function(v){
21279             v.show();
21280             switch (this.viewMode) {
21281                 case 0:
21282
21283                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
21284                         v.hide();
21285                     }
21286                     break;
21287                 case 1:
21288                 case 2:
21289                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
21290                         v.hide();
21291                     }
21292                     break;
21293             }
21294         });
21295         
21296         Roo.each(this.picker().select('.next', true).elements, function(v){
21297             v.show();
21298             switch (this.viewMode) {
21299                 case 0:
21300
21301                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
21302                         v.hide();
21303                     }
21304                     break;
21305                 case 1:
21306                 case 2:
21307                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
21308                         v.hide();
21309                     }
21310                     break;
21311             }
21312         })
21313     },
21314     
21315     moveMonth: function(date, dir)
21316     {
21317         if (!dir) {
21318             return date;
21319         }
21320         var new_date = new Date(date.valueOf()),
21321         day = new_date.getUTCDate(),
21322         month = new_date.getUTCMonth(),
21323         mag = Math.abs(dir),
21324         new_month, test;
21325         dir = dir > 0 ? 1 : -1;
21326         if (mag == 1){
21327             test = dir == -1
21328             // If going back one month, make sure month is not current month
21329             // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
21330             ? function(){
21331                 return new_date.getUTCMonth() == month;
21332             }
21333             // If going forward one month, make sure month is as expected
21334             // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
21335             : function(){
21336                 return new_date.getUTCMonth() != new_month;
21337             };
21338             new_month = month + dir;
21339             new_date.setUTCMonth(new_month);
21340             // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
21341             if (new_month < 0 || new_month > 11) {
21342                 new_month = (new_month + 12) % 12;
21343             }
21344         } else {
21345             // For magnitudes >1, move one month at a time...
21346             for (var i=0; i<mag; i++) {
21347                 // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
21348                 new_date = this.moveMonth(new_date, dir);
21349             }
21350             // ...then reset the day, keeping it in the new month
21351             new_month = new_date.getUTCMonth();
21352             new_date.setUTCDate(day);
21353             test = function(){
21354                 return new_month != new_date.getUTCMonth();
21355             };
21356         }
21357         // Common date-resetting loop -- if date is beyond end of month, make it
21358         // end of month
21359         while (test()){
21360             new_date.setUTCDate(--day);
21361             new_date.setUTCMonth(new_month);
21362         }
21363         return new_date;
21364     },
21365
21366     moveYear: function(date, dir)
21367     {
21368         return this.moveMonth(date, dir*12);
21369     },
21370
21371     dateWithinRange: function(date)
21372     {
21373         return date >= this.startDate && date <= this.endDate;
21374     },
21375
21376     
21377     remove: function() 
21378     {
21379         this.picker().remove();
21380     },
21381     
21382     validateValue : function(value)
21383     {
21384         if(this.getVisibilityEl().hasClass('hidden')){
21385             return true;
21386         }
21387         
21388         if(value.length < 1)  {
21389             if(this.allowBlank){
21390                 return true;
21391             }
21392             return false;
21393         }
21394         
21395         if(value.length < this.minLength){
21396             return false;
21397         }
21398         if(value.length > this.maxLength){
21399             return false;
21400         }
21401         if(this.vtype){
21402             var vt = Roo.form.VTypes;
21403             if(!vt[this.vtype](value, this)){
21404                 return false;
21405             }
21406         }
21407         if(typeof this.validator == "function"){
21408             var msg = this.validator(value);
21409             if(msg !== true){
21410                 return false;
21411             }
21412         }
21413         
21414         if(this.regex && !this.regex.test(value)){
21415             return false;
21416         }
21417         
21418         if(typeof(this.parseDate(value)) == 'undefined'){
21419             return false;
21420         }
21421         
21422         if (this.endDate !== Infinity && this.parseDate(value).getTime() > this.endDate.getTime()) {
21423             return false;
21424         }      
21425         
21426         if (this.startDate !== -Infinity && this.parseDate(value).getTime() < this.startDate.getTime()) {
21427             return false;
21428         } 
21429         
21430         
21431         return true;
21432     },
21433     
21434     reset : function()
21435     {
21436         this.date = this.viewDate = '';
21437         
21438         Roo.bootstrap.DateField.superclass.setValue.call(this, '');
21439     }
21440    
21441 });
21442
21443 Roo.apply(Roo.bootstrap.DateField,  {
21444     
21445     head : {
21446         tag: 'thead',
21447         cn: [
21448         {
21449             tag: 'tr',
21450             cn: [
21451             {
21452                 tag: 'th',
21453                 cls: 'prev',
21454                 html: '<i class="fa fa-arrow-left"/>'
21455             },
21456             {
21457                 tag: 'th',
21458                 cls: 'switch',
21459                 colspan: '5'
21460             },
21461             {
21462                 tag: 'th',
21463                 cls: 'next',
21464                 html: '<i class="fa fa-arrow-right"/>'
21465             }
21466
21467             ]
21468         }
21469         ]
21470     },
21471     
21472     content : {
21473         tag: 'tbody',
21474         cn: [
21475         {
21476             tag: 'tr',
21477             cn: [
21478             {
21479                 tag: 'td',
21480                 colspan: '7'
21481             }
21482             ]
21483         }
21484         ]
21485     },
21486     
21487     footer : {
21488         tag: 'tfoot',
21489         cn: [
21490         {
21491             tag: 'tr',
21492             cn: [
21493             {
21494                 tag: 'th',
21495                 colspan: '7',
21496                 cls: 'today'
21497             }
21498                     
21499             ]
21500         }
21501         ]
21502     },
21503     
21504     dates:{
21505         en: {
21506             days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
21507             daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
21508             daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
21509             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
21510             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
21511             today: "Today"
21512         }
21513     },
21514     
21515     modes: [
21516     {
21517         clsName: 'days',
21518         navFnc: 'Month',
21519         navStep: 1
21520     },
21521     {
21522         clsName: 'months',
21523         navFnc: 'FullYear',
21524         navStep: 1
21525     },
21526     {
21527         clsName: 'years',
21528         navFnc: 'FullYear',
21529         navStep: 10
21530     }]
21531 });
21532
21533 Roo.apply(Roo.bootstrap.DateField,  {
21534   
21535     template : {
21536         tag: 'div',
21537         cls: 'datepicker dropdown-menu roo-dynamic',
21538         cn: [
21539         {
21540             tag: 'div',
21541             cls: 'datepicker-days',
21542             cn: [
21543             {
21544                 tag: 'table',
21545                 cls: 'table-condensed',
21546                 cn:[
21547                 Roo.bootstrap.DateField.head,
21548                 {
21549                     tag: 'tbody'
21550                 },
21551                 Roo.bootstrap.DateField.footer
21552                 ]
21553             }
21554             ]
21555         },
21556         {
21557             tag: 'div',
21558             cls: 'datepicker-months',
21559             cn: [
21560             {
21561                 tag: 'table',
21562                 cls: 'table-condensed',
21563                 cn:[
21564                 Roo.bootstrap.DateField.head,
21565                 Roo.bootstrap.DateField.content,
21566                 Roo.bootstrap.DateField.footer
21567                 ]
21568             }
21569             ]
21570         },
21571         {
21572             tag: 'div',
21573             cls: 'datepicker-years',
21574             cn: [
21575             {
21576                 tag: 'table',
21577                 cls: 'table-condensed',
21578                 cn:[
21579                 Roo.bootstrap.DateField.head,
21580                 Roo.bootstrap.DateField.content,
21581                 Roo.bootstrap.DateField.footer
21582                 ]
21583             }
21584             ]
21585         }
21586         ]
21587     }
21588 });
21589
21590  
21591
21592  /*
21593  * - LGPL
21594  *
21595  * TimeField
21596  * 
21597  */
21598
21599 /**
21600  * @class Roo.bootstrap.TimeField
21601  * @extends Roo.bootstrap.Input
21602  * Bootstrap DateField class
21603  * 
21604  * 
21605  * @constructor
21606  * Create a new TimeField
21607  * @param {Object} config The config object
21608  */
21609
21610 Roo.bootstrap.TimeField = function(config){
21611     Roo.bootstrap.TimeField.superclass.constructor.call(this, config);
21612     this.addEvents({
21613             /**
21614              * @event show
21615              * Fires when this field show.
21616              * @param {Roo.bootstrap.DateField} thisthis
21617              * @param {Mixed} date The date value
21618              */
21619             show : true,
21620             /**
21621              * @event show
21622              * Fires when this field hide.
21623              * @param {Roo.bootstrap.DateField} this
21624              * @param {Mixed} date The date value
21625              */
21626             hide : true,
21627             /**
21628              * @event select
21629              * Fires when select a date.
21630              * @param {Roo.bootstrap.DateField} this
21631              * @param {Mixed} date The date value
21632              */
21633             select : true
21634         });
21635 };
21636
21637 Roo.extend(Roo.bootstrap.TimeField, Roo.bootstrap.Input,  {
21638     
21639     /**
21640      * @cfg {String} format
21641      * The default time format string which can be overriden for localization support.  The format must be
21642      * valid according to {@link Date#parseDate} (defaults to 'H:i').
21643      */
21644     format : "H:i",
21645        
21646     onRender: function(ct, position)
21647     {
21648         
21649         Roo.bootstrap.TimeField.superclass.onRender.call(this, ct, position);
21650                 
21651         this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.TimeField.template);
21652         
21653         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
21654         
21655         this.pop = this.picker().select('>.datepicker-time',true).first();
21656         this.pop.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
21657         
21658         this.picker().on('mousedown', this.onMousedown, this);
21659         this.picker().on('click', this.onClick, this);
21660         
21661         this.picker().addClass('datepicker-dropdown');
21662     
21663         this.fillTime();
21664         this.update();
21665             
21666         this.pop.select('span.hours-up', true).first().on('click', this.onIncrementHours, this);
21667         this.pop.select('span.hours-down', true).first().on('click', this.onDecrementHours, this);
21668         this.pop.select('span.minutes-up', true).first().on('click', this.onIncrementMinutes, this);
21669         this.pop.select('span.minutes-down', true).first().on('click', this.onDecrementMinutes, this);
21670         this.pop.select('button.period', true).first().on('click', this.onTogglePeriod, this);
21671         this.pop.select('button.ok', true).first().on('click', this.setTime, this);
21672
21673     },
21674     
21675     fireKey: function(e){
21676         if (!this.picker().isVisible()){
21677             if (e.keyCode == 27) { // allow escape to hide and re-show picker
21678                 this.show();
21679             }
21680             return;
21681         }
21682
21683         e.preventDefault();
21684         
21685         switch(e.keyCode){
21686             case 27: // escape
21687                 this.hide();
21688                 break;
21689             case 37: // left
21690             case 39: // right
21691                 this.onTogglePeriod();
21692                 break;
21693             case 38: // up
21694                 this.onIncrementMinutes();
21695                 break;
21696             case 40: // down
21697                 this.onDecrementMinutes();
21698                 break;
21699             case 13: // enter
21700             case 9: // tab
21701                 this.setTime();
21702                 break;
21703         }
21704     },
21705     
21706     onClick: function(e) {
21707         e.stopPropagation();
21708         e.preventDefault();
21709     },
21710     
21711     picker : function()
21712     {
21713         return this.el.select('.datepicker', true).first();
21714     },
21715     
21716     fillTime: function()
21717     {    
21718         var time = this.pop.select('tbody', true).first();
21719         
21720         time.dom.innerHTML = '';
21721         
21722         time.createChild({
21723             tag: 'tr',
21724             cn: [
21725                 {
21726                     tag: 'td',
21727                     cn: [
21728                         {
21729                             tag: 'a',
21730                             href: '#',
21731                             cls: 'btn',
21732                             cn: [
21733                                 {
21734                                     tag: 'span',
21735                                     cls: 'hours-up glyphicon glyphicon-chevron-up'
21736                                 }
21737                             ]
21738                         } 
21739                     ]
21740                 },
21741                 {
21742                     tag: 'td',
21743                     cls: 'separator'
21744                 },
21745                 {
21746                     tag: 'td',
21747                     cn: [
21748                         {
21749                             tag: 'a',
21750                             href: '#',
21751                             cls: 'btn',
21752                             cn: [
21753                                 {
21754                                     tag: 'span',
21755                                     cls: 'minutes-up glyphicon glyphicon-chevron-up'
21756                                 }
21757                             ]
21758                         }
21759                     ]
21760                 },
21761                 {
21762                     tag: 'td',
21763                     cls: 'separator'
21764                 }
21765             ]
21766         });
21767         
21768         time.createChild({
21769             tag: 'tr',
21770             cn: [
21771                 {
21772                     tag: 'td',
21773                     cn: [
21774                         {
21775                             tag: 'span',
21776                             cls: 'timepicker-hour',
21777                             html: '00'
21778                         }  
21779                     ]
21780                 },
21781                 {
21782                     tag: 'td',
21783                     cls: 'separator',
21784                     html: ':'
21785                 },
21786                 {
21787                     tag: 'td',
21788                     cn: [
21789                         {
21790                             tag: 'span',
21791                             cls: 'timepicker-minute',
21792                             html: '00'
21793                         }  
21794                     ]
21795                 },
21796                 {
21797                     tag: 'td',
21798                     cls: 'separator'
21799                 },
21800                 {
21801                     tag: 'td',
21802                     cn: [
21803                         {
21804                             tag: 'button',
21805                             type: 'button',
21806                             cls: 'btn btn-primary period',
21807                             html: 'AM'
21808                             
21809                         }
21810                     ]
21811                 }
21812             ]
21813         });
21814         
21815         time.createChild({
21816             tag: 'tr',
21817             cn: [
21818                 {
21819                     tag: 'td',
21820                     cn: [
21821                         {
21822                             tag: 'a',
21823                             href: '#',
21824                             cls: 'btn',
21825                             cn: [
21826                                 {
21827                                     tag: 'span',
21828                                     cls: 'hours-down glyphicon glyphicon-chevron-down'
21829                                 }
21830                             ]
21831                         }
21832                     ]
21833                 },
21834                 {
21835                     tag: 'td',
21836                     cls: 'separator'
21837                 },
21838                 {
21839                     tag: 'td',
21840                     cn: [
21841                         {
21842                             tag: 'a',
21843                             href: '#',
21844                             cls: 'btn',
21845                             cn: [
21846                                 {
21847                                     tag: 'span',
21848                                     cls: 'minutes-down glyphicon glyphicon-chevron-down'
21849                                 }
21850                             ]
21851                         }
21852                     ]
21853                 },
21854                 {
21855                     tag: 'td',
21856                     cls: 'separator'
21857                 }
21858             ]
21859         });
21860         
21861     },
21862     
21863     update: function()
21864     {
21865         
21866         this.time = (typeof(this.time) === 'undefined') ? new Date() : this.time;
21867         
21868         this.fill();
21869     },
21870     
21871     fill: function() 
21872     {
21873         var hours = this.time.getHours();
21874         var minutes = this.time.getMinutes();
21875         var period = 'AM';
21876         
21877         if(hours > 11){
21878             period = 'PM';
21879         }
21880         
21881         if(hours == 0){
21882             hours = 12;
21883         }
21884         
21885         
21886         if(hours > 12){
21887             hours = hours - 12;
21888         }
21889         
21890         if(hours < 10){
21891             hours = '0' + hours;
21892         }
21893         
21894         if(minutes < 10){
21895             minutes = '0' + minutes;
21896         }
21897         
21898         this.pop.select('.timepicker-hour', true).first().dom.innerHTML = hours;
21899         this.pop.select('.timepicker-minute', true).first().dom.innerHTML = minutes;
21900         this.pop.select('button', true).first().dom.innerHTML = period;
21901         
21902     },
21903     
21904     place: function()
21905     {   
21906         this.picker().removeClass(['bottom-left', 'bottom-right', 'top-left', 'top-right']);
21907         
21908         var cls = ['bottom'];
21909         
21910         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){ // top
21911             cls.pop();
21912             cls.push('top');
21913         }
21914         
21915         cls.push('right');
21916         
21917         if((Roo.lib.Dom.getViewWidth() + Roo.get(document.body).getScroll().left) - (this.inputEl().getLeft() + this.picker().getWidth()) < 0){ // left
21918             cls.pop();
21919             cls.push('left');
21920         }
21921         
21922         this.picker().addClass(cls.join('-'));
21923         
21924         var _this = this;
21925         
21926         Roo.each(cls, function(c){
21927             if(c == 'bottom'){
21928                 _this.picker().setTop(_this.inputEl().getHeight());
21929                 return;
21930             }
21931             if(c == 'top'){
21932                 _this.picker().setTop(0 - _this.picker().getHeight());
21933                 return;
21934             }
21935             
21936             if(c == 'left'){
21937                 _this.picker().setLeft(_this.inputEl().getLeft() + _this.inputEl().getWidth() - _this.el.getLeft() - _this.picker().getWidth());
21938                 return;
21939             }
21940             if(c == 'right'){
21941                 _this.picker().setLeft(_this.inputEl().getLeft() - _this.el.getLeft());
21942                 return;
21943             }
21944         });
21945         
21946     },
21947   
21948     onFocus : function()
21949     {
21950         Roo.bootstrap.TimeField.superclass.onFocus.call(this);
21951         this.show();
21952     },
21953     
21954     onBlur : function()
21955     {
21956         Roo.bootstrap.TimeField.superclass.onBlur.call(this);
21957         this.hide();
21958     },
21959     
21960     show : function()
21961     {
21962         this.picker().show();
21963         this.pop.show();
21964         this.update();
21965         this.place();
21966         
21967         this.fireEvent('show', this, this.date);
21968     },
21969     
21970     hide : function()
21971     {
21972         this.picker().hide();
21973         this.pop.hide();
21974         
21975         this.fireEvent('hide', this, this.date);
21976     },
21977     
21978     setTime : function()
21979     {
21980         this.hide();
21981         this.setValue(this.time.format(this.format));
21982         
21983         this.fireEvent('select', this, this.date);
21984         
21985         
21986     },
21987     
21988     onMousedown: function(e){
21989         e.stopPropagation();
21990         e.preventDefault();
21991     },
21992     
21993     onIncrementHours: function()
21994     {
21995         Roo.log('onIncrementHours');
21996         this.time = this.time.add(Date.HOUR, 1);
21997         this.update();
21998         
21999     },
22000     
22001     onDecrementHours: function()
22002     {
22003         Roo.log('onDecrementHours');
22004         this.time = this.time.add(Date.HOUR, -1);
22005         this.update();
22006     },
22007     
22008     onIncrementMinutes: function()
22009     {
22010         Roo.log('onIncrementMinutes');
22011         this.time = this.time.add(Date.MINUTE, 1);
22012         this.update();
22013     },
22014     
22015     onDecrementMinutes: function()
22016     {
22017         Roo.log('onDecrementMinutes');
22018         this.time = this.time.add(Date.MINUTE, -1);
22019         this.update();
22020     },
22021     
22022     onTogglePeriod: function()
22023     {
22024         Roo.log('onTogglePeriod');
22025         this.time = this.time.add(Date.HOUR, 12);
22026         this.update();
22027     }
22028     
22029    
22030 });
22031
22032 Roo.apply(Roo.bootstrap.TimeField,  {
22033     
22034     content : {
22035         tag: 'tbody',
22036         cn: [
22037             {
22038                 tag: 'tr',
22039                 cn: [
22040                 {
22041                     tag: 'td',
22042                     colspan: '7'
22043                 }
22044                 ]
22045             }
22046         ]
22047     },
22048     
22049     footer : {
22050         tag: 'tfoot',
22051         cn: [
22052             {
22053                 tag: 'tr',
22054                 cn: [
22055                 {
22056                     tag: 'th',
22057                     colspan: '7',
22058                     cls: '',
22059                     cn: [
22060                         {
22061                             tag: 'button',
22062                             cls: 'btn btn-info ok',
22063                             html: 'OK'
22064                         }
22065                     ]
22066                 }
22067
22068                 ]
22069             }
22070         ]
22071     }
22072 });
22073
22074 Roo.apply(Roo.bootstrap.TimeField,  {
22075   
22076     template : {
22077         tag: 'div',
22078         cls: 'datepicker dropdown-menu',
22079         cn: [
22080             {
22081                 tag: 'div',
22082                 cls: 'datepicker-time',
22083                 cn: [
22084                 {
22085                     tag: 'table',
22086                     cls: 'table-condensed',
22087                     cn:[
22088                     Roo.bootstrap.TimeField.content,
22089                     Roo.bootstrap.TimeField.footer
22090                     ]
22091                 }
22092                 ]
22093             }
22094         ]
22095     }
22096 });
22097
22098  
22099
22100  /*
22101  * - LGPL
22102  *
22103  * MonthField
22104  * 
22105  */
22106
22107 /**
22108  * @class Roo.bootstrap.MonthField
22109  * @extends Roo.bootstrap.Input
22110  * Bootstrap MonthField class
22111  * 
22112  * @cfg {String} language default en
22113  * 
22114  * @constructor
22115  * Create a new MonthField
22116  * @param {Object} config The config object
22117  */
22118
22119 Roo.bootstrap.MonthField = function(config){
22120     Roo.bootstrap.MonthField.superclass.constructor.call(this, config);
22121     
22122     this.addEvents({
22123         /**
22124          * @event show
22125          * Fires when this field show.
22126          * @param {Roo.bootstrap.MonthField} this
22127          * @param {Mixed} date The date value
22128          */
22129         show : true,
22130         /**
22131          * @event show
22132          * Fires when this field hide.
22133          * @param {Roo.bootstrap.MonthField} this
22134          * @param {Mixed} date The date value
22135          */
22136         hide : true,
22137         /**
22138          * @event select
22139          * Fires when select a date.
22140          * @param {Roo.bootstrap.MonthField} this
22141          * @param {String} oldvalue The old value
22142          * @param {String} newvalue The new value
22143          */
22144         select : true
22145     });
22146 };
22147
22148 Roo.extend(Roo.bootstrap.MonthField, Roo.bootstrap.Input,  {
22149     
22150     onRender: function(ct, position)
22151     {
22152         
22153         Roo.bootstrap.MonthField.superclass.onRender.call(this, ct, position);
22154         
22155         this.language = this.language || 'en';
22156         this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : this.language.split('-')[0];
22157         this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : "en";
22158         
22159         this.isRTL = Roo.bootstrap.MonthField.dates[this.language].rtl || false;
22160         this.isInline = false;
22161         this.isInput = true;
22162         this.component = this.el.select('.add-on', true).first() || false;
22163         this.component = (this.component && this.component.length === 0) ? false : this.component;
22164         this.hasInput = this.component && this.inputEL().length;
22165         
22166         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.MonthField.template);
22167         
22168         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
22169         
22170         this.picker().on('mousedown', this.onMousedown, this);
22171         this.picker().on('click', this.onClick, this);
22172         
22173         this.picker().addClass('datepicker-dropdown');
22174         
22175         Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
22176             v.setStyle('width', '189px');
22177         });
22178         
22179         this.fillMonths();
22180         
22181         this.update();
22182         
22183         if(this.isInline) {
22184             this.show();
22185         }
22186         
22187     },
22188     
22189     setValue: function(v, suppressEvent)
22190     {   
22191         var o = this.getValue();
22192         
22193         Roo.bootstrap.MonthField.superclass.setValue.call(this, v);
22194         
22195         this.update();
22196
22197         if(suppressEvent !== true){
22198             this.fireEvent('select', this, o, v);
22199         }
22200         
22201     },
22202     
22203     getValue: function()
22204     {
22205         return this.value;
22206     },
22207     
22208     onClick: function(e) 
22209     {
22210         e.stopPropagation();
22211         e.preventDefault();
22212         
22213         var target = e.getTarget();
22214         
22215         if(target.nodeName.toLowerCase() === 'i'){
22216             target = Roo.get(target).dom.parentNode;
22217         }
22218         
22219         var nodeName = target.nodeName;
22220         var className = target.className;
22221         var html = target.innerHTML;
22222         
22223         if(nodeName.toLowerCase() != 'span' || className.indexOf('disabled') > -1 || className.indexOf('month') == -1){
22224             return;
22225         }
22226         
22227         this.vIndex = Roo.bootstrap.MonthField.dates[this.language].monthsShort.indexOf(html);
22228         
22229         this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
22230         
22231         this.hide();
22232                         
22233     },
22234     
22235     picker : function()
22236     {
22237         return this.pickerEl;
22238     },
22239     
22240     fillMonths: function()
22241     {    
22242         var i = 0;
22243         var months = this.picker().select('>.datepicker-months td', true).first();
22244         
22245         months.dom.innerHTML = '';
22246         
22247         while (i < 12) {
22248             var month = {
22249                 tag: 'span',
22250                 cls: 'month',
22251                 html: Roo.bootstrap.MonthField.dates[this.language].monthsShort[i++]
22252             };
22253             
22254             months.createChild(month);
22255         }
22256         
22257     },
22258     
22259     update: function()
22260     {
22261         var _this = this;
22262         
22263         if(typeof(this.vIndex) == 'undefined' && this.value.length){
22264             this.vIndex = Roo.bootstrap.MonthField.dates[this.language].months.indexOf(this.value);
22265         }
22266         
22267         Roo.each(this.pickerEl.select('> .datepicker-months tbody > tr > td > span', true).elements, function(e, k){
22268             e.removeClass('active');
22269             
22270             if(typeof(_this.vIndex) != 'undefined' && k == _this.vIndex){
22271                 e.addClass('active');
22272             }
22273         })
22274     },
22275     
22276     place: function()
22277     {
22278         if(this.isInline) {
22279             return;
22280         }
22281         
22282         this.picker().removeClass(['bottom', 'top']);
22283         
22284         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
22285             /*
22286              * place to the top of element!
22287              *
22288              */
22289             
22290             this.picker().addClass('top');
22291             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
22292             
22293             return;
22294         }
22295         
22296         this.picker().addClass('bottom');
22297         
22298         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
22299     },
22300     
22301     onFocus : function()
22302     {
22303         Roo.bootstrap.MonthField.superclass.onFocus.call(this);
22304         this.show();
22305     },
22306     
22307     onBlur : function()
22308     {
22309         Roo.bootstrap.MonthField.superclass.onBlur.call(this);
22310         
22311         var d = this.inputEl().getValue();
22312         
22313         this.setValue(d);
22314                 
22315         this.hide();
22316     },
22317     
22318     show : function()
22319     {
22320         this.picker().show();
22321         this.picker().select('>.datepicker-months', true).first().show();
22322         this.update();
22323         this.place();
22324         
22325         this.fireEvent('show', this, this.date);
22326     },
22327     
22328     hide : function()
22329     {
22330         if(this.isInline) {
22331             return;
22332         }
22333         this.picker().hide();
22334         this.fireEvent('hide', this, this.date);
22335         
22336     },
22337     
22338     onMousedown: function(e)
22339     {
22340         e.stopPropagation();
22341         e.preventDefault();
22342     },
22343     
22344     keyup: function(e)
22345     {
22346         Roo.bootstrap.MonthField.superclass.keyup.call(this);
22347         this.update();
22348     },
22349
22350     fireKey: function(e)
22351     {
22352         if (!this.picker().isVisible()){
22353             if (e.keyCode == 27)   {// allow escape to hide and re-show picker
22354                 this.show();
22355             }
22356             return;
22357         }
22358         
22359         var dir;
22360         
22361         switch(e.keyCode){
22362             case 27: // escape
22363                 this.hide();
22364                 e.preventDefault();
22365                 break;
22366             case 37: // left
22367             case 39: // right
22368                 dir = e.keyCode == 37 ? -1 : 1;
22369                 
22370                 this.vIndex = this.vIndex + dir;
22371                 
22372                 if(this.vIndex < 0){
22373                     this.vIndex = 0;
22374                 }
22375                 
22376                 if(this.vIndex > 11){
22377                     this.vIndex = 11;
22378                 }
22379                 
22380                 if(isNaN(this.vIndex)){
22381                     this.vIndex = 0;
22382                 }
22383                 
22384                 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
22385                 
22386                 break;
22387             case 38: // up
22388             case 40: // down
22389                 
22390                 dir = e.keyCode == 38 ? -1 : 1;
22391                 
22392                 this.vIndex = this.vIndex + dir * 4;
22393                 
22394                 if(this.vIndex < 0){
22395                     this.vIndex = 0;
22396                 }
22397                 
22398                 if(this.vIndex > 11){
22399                     this.vIndex = 11;
22400                 }
22401                 
22402                 if(isNaN(this.vIndex)){
22403                     this.vIndex = 0;
22404                 }
22405                 
22406                 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
22407                 break;
22408                 
22409             case 13: // enter
22410                 
22411                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
22412                     this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
22413                 }
22414                 
22415                 this.hide();
22416                 e.preventDefault();
22417                 break;
22418             case 9: // tab
22419                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
22420                     this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
22421                 }
22422                 this.hide();
22423                 break;
22424             case 16: // shift
22425             case 17: // ctrl
22426             case 18: // alt
22427                 break;
22428             default :
22429                 this.hide();
22430                 
22431         }
22432     },
22433     
22434     remove: function() 
22435     {
22436         this.picker().remove();
22437     }
22438    
22439 });
22440
22441 Roo.apply(Roo.bootstrap.MonthField,  {
22442     
22443     content : {
22444         tag: 'tbody',
22445         cn: [
22446         {
22447             tag: 'tr',
22448             cn: [
22449             {
22450                 tag: 'td',
22451                 colspan: '7'
22452             }
22453             ]
22454         }
22455         ]
22456     },
22457     
22458     dates:{
22459         en: {
22460             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
22461             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
22462         }
22463     }
22464 });
22465
22466 Roo.apply(Roo.bootstrap.MonthField,  {
22467   
22468     template : {
22469         tag: 'div',
22470         cls: 'datepicker dropdown-menu roo-dynamic',
22471         cn: [
22472             {
22473                 tag: 'div',
22474                 cls: 'datepicker-months',
22475                 cn: [
22476                 {
22477                     tag: 'table',
22478                     cls: 'table-condensed',
22479                     cn:[
22480                         Roo.bootstrap.DateField.content
22481                     ]
22482                 }
22483                 ]
22484             }
22485         ]
22486     }
22487 });
22488
22489  
22490
22491  
22492  /*
22493  * - LGPL
22494  *
22495  * CheckBox
22496  * 
22497  */
22498
22499 /**
22500  * @class Roo.bootstrap.CheckBox
22501  * @extends Roo.bootstrap.Input
22502  * Bootstrap CheckBox class
22503  * 
22504  * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
22505  * @cfg {String} inputValue The value that should go into the generated input element's value when checked.
22506  * @cfg {String} boxLabel The text that appears beside the checkbox
22507  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the checkbox
22508  * @cfg {Boolean} checked initnal the element
22509  * @cfg {Boolean} inline inline the element (default false)
22510  * @cfg {String} groupId the checkbox group id // normal just use for checkbox
22511  * @cfg {String} tooltip label tooltip
22512  * 
22513  * @constructor
22514  * Create a new CheckBox
22515  * @param {Object} config The config object
22516  */
22517
22518 Roo.bootstrap.CheckBox = function(config){
22519     Roo.bootstrap.CheckBox.superclass.constructor.call(this, config);
22520    
22521     this.addEvents({
22522         /**
22523         * @event check
22524         * Fires when the element is checked or unchecked.
22525         * @param {Roo.bootstrap.CheckBox} this This input
22526         * @param {Boolean} checked The new checked value
22527         */
22528        check : true,
22529        /**
22530         * @event click
22531         * Fires when the element is click.
22532         * @param {Roo.bootstrap.CheckBox} this This input
22533         */
22534        click : true
22535     });
22536     
22537 };
22538
22539 Roo.extend(Roo.bootstrap.CheckBox, Roo.bootstrap.Input,  {
22540   
22541     inputType: 'checkbox',
22542     inputValue: 1,
22543     valueOff: 0,
22544     boxLabel: false,
22545     checked: false,
22546     weight : false,
22547     inline: false,
22548     tooltip : '',
22549     
22550     // checkbox success does not make any sense really.. 
22551     invalidClass : "",
22552     validClass : "",
22553     
22554     
22555     getAutoCreate : function()
22556     {
22557         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
22558         
22559         var id = Roo.id();
22560         
22561         var cfg = {};
22562         
22563         cfg.cls = 'form-group form-check ' + this.inputType; //input-group
22564         
22565         if(this.inline){
22566             cfg.cls += ' ' + this.inputType + '-inline  form-check-inline';
22567         }
22568         
22569         var input =  {
22570             tag: 'input',
22571             id : id,
22572             type : this.inputType,
22573             value : this.inputValue,
22574             cls : 'roo-' + this.inputType, //'form-box',
22575             placeholder : this.placeholder || ''
22576             
22577         };
22578         
22579         if(this.inputType != 'radio'){
22580             var hidden =  {
22581                 tag: 'input',
22582                 type : 'hidden',
22583                 cls : 'roo-hidden-value',
22584                 value : this.checked ? this.inputValue : this.valueOff
22585             };
22586         }
22587         
22588             
22589         if (this.weight) { // Validity check?
22590             cfg.cls += " " + this.inputType + "-" + this.weight;
22591         }
22592         
22593         if (this.disabled) {
22594             input.disabled=true;
22595         }
22596         
22597         if(this.checked){
22598             input.checked = this.checked;
22599         }
22600         
22601         if (this.name) {
22602             
22603             input.name = this.name;
22604             
22605             if(this.inputType != 'radio'){
22606                 hidden.name = this.name;
22607                 input.name = '_hidden_' + this.name;
22608             }
22609         }
22610         
22611         if (this.size) {
22612             input.cls += ' input-' + this.size;
22613         }
22614         
22615         var settings=this;
22616         
22617         ['xs','sm','md','lg'].map(function(size){
22618             if (settings[size]) {
22619                 cfg.cls += ' col-' + size + '-' + settings[size];
22620             }
22621         });
22622         
22623         var inputblock = input;
22624          
22625         if (this.before || this.after) {
22626             
22627             inputblock = {
22628                 cls : 'input-group',
22629                 cn :  [] 
22630             };
22631             
22632             if (this.before) {
22633                 inputblock.cn.push({
22634                     tag :'span',
22635                     cls : 'input-group-addon',
22636                     html : this.before
22637                 });
22638             }
22639             
22640             inputblock.cn.push(input);
22641             
22642             if(this.inputType != 'radio'){
22643                 inputblock.cn.push(hidden);
22644             }
22645             
22646             if (this.after) {
22647                 inputblock.cn.push({
22648                     tag :'span',
22649                     cls : 'input-group-addon',
22650                     html : this.after
22651                 });
22652             }
22653             
22654         }
22655         var boxLabelCfg = false;
22656         
22657         if(this.boxLabel){
22658            
22659             boxLabelCfg = {
22660                 tag: 'label',
22661                 //'for': id, // box label is handled by onclick - so no for...
22662                 cls: 'box-label',
22663                 html: this.boxLabel
22664             };
22665             if(this.tooltip){
22666                 boxLabelCfg.tooltip = this.tooltip;
22667             }
22668              
22669         }
22670         
22671         
22672         if (align ==='left' && this.fieldLabel.length) {
22673 //                Roo.log("left and has label");
22674             cfg.cn = [
22675                 {
22676                     tag: 'label',
22677                     'for' :  id,
22678                     cls : 'control-label',
22679                     html : this.fieldLabel
22680                 },
22681                 {
22682                     cls : "", 
22683                     cn: [
22684                         inputblock
22685                     ]
22686                 }
22687             ];
22688             
22689             if (boxLabelCfg) {
22690                 cfg.cn[1].cn.push(boxLabelCfg);
22691             }
22692             
22693             if(this.labelWidth > 12){
22694                 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
22695             }
22696             
22697             if(this.labelWidth < 13 && this.labelmd == 0){
22698                 this.labelmd = this.labelWidth;
22699             }
22700             
22701             if(this.labellg > 0){
22702                 cfg.cn[0].cls += ' col-lg-' + this.labellg;
22703                 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
22704             }
22705             
22706             if(this.labelmd > 0){
22707                 cfg.cn[0].cls += ' col-md-' + this.labelmd;
22708                 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
22709             }
22710             
22711             if(this.labelsm > 0){
22712                 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
22713                 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
22714             }
22715             
22716             if(this.labelxs > 0){
22717                 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
22718                 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
22719             }
22720             
22721         } else if ( this.fieldLabel.length) {
22722 //                Roo.log(" label");
22723                 cfg.cn = [
22724                    
22725                     {
22726                         tag: this.boxLabel ? 'span' : 'label',
22727                         'for': id,
22728                         cls: 'control-label box-input-label',
22729                         //cls : 'input-group-addon',
22730                         html : this.fieldLabel
22731                     },
22732                     
22733                     inputblock
22734                     
22735                 ];
22736                 if (boxLabelCfg) {
22737                     cfg.cn.push(boxLabelCfg);
22738                 }
22739
22740         } else {
22741             
22742 //                Roo.log(" no label && no align");
22743                 cfg.cn = [  inputblock ] ;
22744                 if (boxLabelCfg) {
22745                     cfg.cn.push(boxLabelCfg);
22746                 }
22747
22748                 
22749         }
22750         
22751        
22752         
22753         if(this.inputType != 'radio'){
22754             cfg.cn.push(hidden);
22755         }
22756         
22757         return cfg;
22758         
22759     },
22760     
22761     /**
22762      * return the real input element.
22763      */
22764     inputEl: function ()
22765     {
22766         return this.el.select('input.roo-' + this.inputType,true).first();
22767     },
22768     hiddenEl: function ()
22769     {
22770         return this.el.select('input.roo-hidden-value',true).first();
22771     },
22772     
22773     labelEl: function()
22774     {
22775         return this.el.select('label.control-label',true).first();
22776     },
22777     /* depricated... */
22778     
22779     label: function()
22780     {
22781         return this.labelEl();
22782     },
22783     
22784     boxLabelEl: function()
22785     {
22786         return this.el.select('label.box-label',true).first();
22787     },
22788     
22789     initEvents : function()
22790     {
22791 //        Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
22792         
22793         this.inputEl().on('click', this.onClick,  this);
22794         
22795         if (this.boxLabel) { 
22796             this.el.select('label.box-label',true).first().on('click', this.onClick,  this);
22797         }
22798         
22799         this.startValue = this.getValue();
22800         
22801         if(this.groupId){
22802             Roo.bootstrap.CheckBox.register(this);
22803         }
22804     },
22805     
22806     onClick : function(e)
22807     {   
22808         if(this.fireEvent('click', this, e) !== false){
22809             this.setChecked(!this.checked);
22810         }
22811         
22812     },
22813     
22814     setChecked : function(state,suppressEvent)
22815     {
22816         this.startValue = this.getValue();
22817
22818         if(this.inputType == 'radio'){
22819             
22820             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
22821                 e.dom.checked = false;
22822             });
22823             
22824             this.inputEl().dom.checked = true;
22825             
22826             this.inputEl().dom.value = this.inputValue;
22827             
22828             if(suppressEvent !== true){
22829                 this.fireEvent('check', this, true);
22830             }
22831             
22832             this.validate();
22833             
22834             return;
22835         }
22836         
22837         this.checked = state;
22838         
22839         this.inputEl().dom.checked = state;
22840         
22841         
22842         this.hiddenEl().dom.value = state ? this.inputValue : this.valueOff;
22843         
22844         if(suppressEvent !== true){
22845             this.fireEvent('check', this, state);
22846         }
22847         
22848         this.validate();
22849     },
22850     
22851     getValue : function()
22852     {
22853         if(this.inputType == 'radio'){
22854             return this.getGroupValue();
22855         }
22856         
22857         return this.hiddenEl().dom.value;
22858         
22859     },
22860     
22861     getGroupValue : function()
22862     {
22863         if(typeof(this.el.up('form').child('input[name='+this.name+']:checked', true)) == 'undefined'){
22864             return '';
22865         }
22866         
22867         return this.el.up('form').child('input[name='+this.name+']:checked', true).value;
22868     },
22869     
22870     setValue : function(v,suppressEvent)
22871     {
22872         if(this.inputType == 'radio'){
22873             this.setGroupValue(v, suppressEvent);
22874             return;
22875         }
22876         
22877         this.setChecked(((typeof(v) == 'undefined') ? this.checked : (String(v) === String(this.inputValue))), suppressEvent);
22878         
22879         this.validate();
22880     },
22881     
22882     setGroupValue : function(v, suppressEvent)
22883     {
22884         this.startValue = this.getValue();
22885         
22886         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
22887             e.dom.checked = false;
22888             
22889             if(e.dom.value == v){
22890                 e.dom.checked = true;
22891             }
22892         });
22893         
22894         if(suppressEvent !== true){
22895             this.fireEvent('check', this, true);
22896         }
22897
22898         this.validate();
22899         
22900         return;
22901     },
22902     
22903     validate : function()
22904     {
22905         if(this.getVisibilityEl().hasClass('hidden')){
22906             return true;
22907         }
22908         
22909         if(
22910                 this.disabled || 
22911                 (this.inputType == 'radio' && this.validateRadio()) ||
22912                 (this.inputType == 'checkbox' && this.validateCheckbox())
22913         ){
22914             this.markValid();
22915             return true;
22916         }
22917         
22918         this.markInvalid();
22919         return false;
22920     },
22921     
22922     validateRadio : function()
22923     {
22924         if(this.getVisibilityEl().hasClass('hidden')){
22925             return true;
22926         }
22927         
22928         if(this.allowBlank){
22929             return true;
22930         }
22931         
22932         var valid = false;
22933         
22934         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
22935             if(!e.dom.checked){
22936                 return;
22937             }
22938             
22939             valid = true;
22940             
22941             return false;
22942         });
22943         
22944         return valid;
22945     },
22946     
22947     validateCheckbox : function()
22948     {
22949         if(!this.groupId){
22950             return (this.getValue() == this.inputValue || this.allowBlank) ? true : false;
22951             //return (this.getValue() == this.inputValue) ? true : false;
22952         }
22953         
22954         var group = Roo.bootstrap.CheckBox.get(this.groupId);
22955         
22956         if(!group){
22957             return false;
22958         }
22959         
22960         var r = false;
22961         
22962         for(var i in group){
22963             if(group[i].el.isVisible(true)){
22964                 r = false;
22965                 break;
22966             }
22967             
22968             r = true;
22969         }
22970         
22971         for(var i in group){
22972             if(r){
22973                 break;
22974             }
22975             
22976             r = (group[i].getValue() == group[i].inputValue) ? true : false;
22977         }
22978         
22979         return r;
22980     },
22981     
22982     /**
22983      * Mark this field as valid
22984      */
22985     markValid : function()
22986     {
22987         var _this = this;
22988         
22989         this.fireEvent('valid', this);
22990         
22991         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
22992         
22993         if(this.groupId){
22994             label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
22995         }
22996         
22997         if(label){
22998             label.markValid();
22999         }
23000
23001         if(this.inputType == 'radio'){
23002             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23003                 var fg = e.findParent('.form-group', false, true);
23004                 if (Roo.bootstrap.version == 3) {
23005                     fg.removeClass([_this.invalidClass, _this.validClass]);
23006                     fg.addClass(_this.validClass);
23007                 } else {
23008                     fg.removeClass(['is-valid', 'is-invalid']);
23009                     fg.addClass('is-valid');
23010                 }
23011             });
23012             
23013             return;
23014         }
23015
23016         if(!this.groupId){
23017             var fg = this.el.findParent('.form-group', false, true);
23018             if (Roo.bootstrap.version == 3) {
23019                 fg.removeClass([this.invalidClass, this.validClass]);
23020                 fg.addClass(this.validClass);
23021             } else {
23022                 fg.removeClass(['is-valid', 'is-invalid']);
23023                 fg.addClass('is-valid');
23024             }
23025             return;
23026         }
23027         
23028         var group = Roo.bootstrap.CheckBox.get(this.groupId);
23029         
23030         if(!group){
23031             return;
23032         }
23033         
23034         for(var i in group){
23035             var fg = group[i].el.findParent('.form-group', false, true);
23036             if (Roo.bootstrap.version == 3) {
23037                 fg.removeClass([this.invalidClass, this.validClass]);
23038                 fg.addClass(this.validClass);
23039             } else {
23040                 fg.removeClass(['is-valid', 'is-invalid']);
23041                 fg.addClass('is-valid');
23042             }
23043         }
23044     },
23045     
23046      /**
23047      * Mark this field as invalid
23048      * @param {String} msg The validation message
23049      */
23050     markInvalid : function(msg)
23051     {
23052         if(this.allowBlank){
23053             return;
23054         }
23055         
23056         var _this = this;
23057         
23058         this.fireEvent('invalid', this, msg);
23059         
23060         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
23061         
23062         if(this.groupId){
23063             label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
23064         }
23065         
23066         if(label){
23067             label.markInvalid();
23068         }
23069             
23070         if(this.inputType == 'radio'){
23071             
23072             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23073                 var fg = e.findParent('.form-group', false, true);
23074                 if (Roo.bootstrap.version == 3) {
23075                     fg.removeClass([_this.invalidClass, _this.validClass]);
23076                     fg.addClass(_this.invalidClass);
23077                 } else {
23078                     fg.removeClass(['is-invalid', 'is-valid']);
23079                     fg.addClass('is-invalid');
23080                 }
23081             });
23082             
23083             return;
23084         }
23085         
23086         if(!this.groupId){
23087             var fg = this.el.findParent('.form-group', false, true);
23088             if (Roo.bootstrap.version == 3) {
23089                 fg.removeClass([_this.invalidClass, _this.validClass]);
23090                 fg.addClass(_this.invalidClass);
23091             } else {
23092                 fg.removeClass(['is-invalid', 'is-valid']);
23093                 fg.addClass('is-invalid');
23094             }
23095             return;
23096         }
23097         
23098         var group = Roo.bootstrap.CheckBox.get(this.groupId);
23099         
23100         if(!group){
23101             return;
23102         }
23103         
23104         for(var i in group){
23105             var fg = group[i].el.findParent('.form-group', false, true);
23106             if (Roo.bootstrap.version == 3) {
23107                 fg.removeClass([_this.invalidClass, _this.validClass]);
23108                 fg.addClass(_this.invalidClass);
23109             } else {
23110                 fg.removeClass(['is-invalid', 'is-valid']);
23111                 fg.addClass('is-invalid');
23112             }
23113         }
23114         
23115     },
23116     
23117     clearInvalid : function()
23118     {
23119         Roo.bootstrap.Input.prototype.clearInvalid.call(this);
23120         
23121         // this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
23122         
23123         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
23124         
23125         if (label && label.iconEl) {
23126             label.iconEl.removeClass([ label.validClass, label.invalidClass ]);
23127             label.iconEl.removeClass(['is-invalid', 'is-valid']);
23128         }
23129     },
23130     
23131     disable : function()
23132     {
23133         if(this.inputType != 'radio'){
23134             Roo.bootstrap.CheckBox.superclass.disable.call(this);
23135             return;
23136         }
23137         
23138         var _this = this;
23139         
23140         if(this.rendered){
23141             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23142                 _this.getActionEl().addClass(this.disabledClass);
23143                 e.dom.disabled = true;
23144             });
23145         }
23146         
23147         this.disabled = true;
23148         this.fireEvent("disable", this);
23149         return this;
23150     },
23151
23152     enable : function()
23153     {
23154         if(this.inputType != 'radio'){
23155             Roo.bootstrap.CheckBox.superclass.enable.call(this);
23156             return;
23157         }
23158         
23159         var _this = this;
23160         
23161         if(this.rendered){
23162             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23163                 _this.getActionEl().removeClass(this.disabledClass);
23164                 e.dom.disabled = false;
23165             });
23166         }
23167         
23168         this.disabled = false;
23169         this.fireEvent("enable", this);
23170         return this;
23171     },
23172     
23173     setBoxLabel : function(v)
23174     {
23175         this.boxLabel = v;
23176         
23177         if(this.rendered){
23178             this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
23179         }
23180     }
23181
23182 });
23183
23184 Roo.apply(Roo.bootstrap.CheckBox, {
23185     
23186     groups: {},
23187     
23188      /**
23189     * register a CheckBox Group
23190     * @param {Roo.bootstrap.CheckBox} the CheckBox to add
23191     */
23192     register : function(checkbox)
23193     {
23194         if(typeof(this.groups[checkbox.groupId]) == 'undefined'){
23195             this.groups[checkbox.groupId] = {};
23196         }
23197         
23198         if(this.groups[checkbox.groupId].hasOwnProperty(checkbox.name)){
23199             return;
23200         }
23201         
23202         this.groups[checkbox.groupId][checkbox.name] = checkbox;
23203         
23204     },
23205     /**
23206     * fetch a CheckBox Group based on the group ID
23207     * @param {string} the group ID
23208     * @returns {Roo.bootstrap.CheckBox} the CheckBox group
23209     */
23210     get: function(groupId) {
23211         if (typeof(this.groups[groupId]) == 'undefined') {
23212             return false;
23213         }
23214         
23215         return this.groups[groupId] ;
23216     }
23217     
23218     
23219 });
23220 /*
23221  * - LGPL
23222  *
23223  * RadioItem
23224  * 
23225  */
23226
23227 /**
23228  * @class Roo.bootstrap.Radio
23229  * @extends Roo.bootstrap.Component
23230  * Bootstrap Radio class
23231  * @cfg {String} boxLabel - the label associated
23232  * @cfg {String} value - the value of radio
23233  * 
23234  * @constructor
23235  * Create a new Radio
23236  * @param {Object} config The config object
23237  */
23238 Roo.bootstrap.Radio = function(config){
23239     Roo.bootstrap.Radio.superclass.constructor.call(this, config);
23240     
23241 };
23242
23243 Roo.extend(Roo.bootstrap.Radio, Roo.bootstrap.Component, {
23244     
23245     boxLabel : '',
23246     
23247     value : '',
23248     
23249     getAutoCreate : function()
23250     {
23251         var cfg = {
23252             tag : 'div',
23253             cls : 'form-group radio',
23254             cn : [
23255                 {
23256                     tag : 'label',
23257                     cls : 'box-label',
23258                     html : this.boxLabel
23259                 }
23260             ]
23261         };
23262         
23263         return cfg;
23264     },
23265     
23266     initEvents : function() 
23267     {
23268         this.parent().register(this);
23269         
23270         this.el.on('click', this.onClick, this);
23271         
23272     },
23273     
23274     onClick : function(e)
23275     {
23276         if(this.parent().fireEvent('click', this.parent(), this, e) !== false){
23277             this.setChecked(true);
23278         }
23279     },
23280     
23281     setChecked : function(state, suppressEvent)
23282     {
23283         this.parent().setValue(this.value, suppressEvent);
23284         
23285     },
23286     
23287     setBoxLabel : function(v)
23288     {
23289         this.boxLabel = v;
23290         
23291         if(this.rendered){
23292             this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
23293         }
23294     }
23295     
23296 });
23297  
23298
23299  /*
23300  * - LGPL
23301  *
23302  * Input
23303  * 
23304  */
23305
23306 /**
23307  * @class Roo.bootstrap.SecurePass
23308  * @extends Roo.bootstrap.Input
23309  * Bootstrap SecurePass class
23310  *
23311  * 
23312  * @constructor
23313  * Create a new SecurePass
23314  * @param {Object} config The config object
23315  */
23316  
23317 Roo.bootstrap.SecurePass = function (config) {
23318     // these go here, so the translation tool can replace them..
23319     this.errors = {
23320         PwdEmpty: "Please type a password, and then retype it to confirm.",
23321         PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
23322         PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
23323         PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
23324         IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
23325         FNInPwd: "Your password can't contain your first name. Please type a different password.",
23326         LNInPwd: "Your password can't contain your last name. Please type a different password.",
23327         TooWeak: "Your password is Too Weak."
23328     },
23329     this.meterLabel = "Password strength:";
23330     this.pwdStrengths = ["Too Weak", "Weak", "Medium", "Strong"];
23331     this.meterClass = [
23332         "roo-password-meter-tooweak", 
23333         "roo-password-meter-weak", 
23334         "roo-password-meter-medium", 
23335         "roo-password-meter-strong", 
23336         "roo-password-meter-grey"
23337     ];
23338     
23339     this.errors = {};
23340     
23341     Roo.bootstrap.SecurePass.superclass.constructor.call(this, config);
23342 }
23343
23344 Roo.extend(Roo.bootstrap.SecurePass, Roo.bootstrap.Input, {
23345     /**
23346      * @cfg {String/Object} errors A Error spec, or true for a default spec (defaults to
23347      * {
23348      *  PwdEmpty: "Please type a password, and then retype it to confirm.",
23349      *  PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
23350      *  PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
23351      *  PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
23352      *  IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
23353      *  FNInPwd: "Your password can't contain your first name. Please type a different password.",
23354      *  LNInPwd: "Your password can't contain your last name. Please type a different password."
23355      * })
23356      */
23357     // private
23358     
23359     meterWidth: 300,
23360     errorMsg :'',    
23361     errors: false,
23362     imageRoot: '/',
23363     /**
23364      * @cfg {String/Object} Label for the strength meter (defaults to
23365      * 'Password strength:')
23366      */
23367     // private
23368     meterLabel: '',
23369     /**
23370      * @cfg {String/Object} pwdStrengths A pwdStrengths spec, or true for a default spec (defaults to
23371      * ['Weak', 'Medium', 'Strong'])
23372      */
23373     // private    
23374     pwdStrengths: false,    
23375     // private
23376     strength: 0,
23377     // private
23378     _lastPwd: null,
23379     // private
23380     kCapitalLetter: 0,
23381     kSmallLetter: 1,
23382     kDigit: 2,
23383     kPunctuation: 3,
23384     
23385     insecure: false,
23386     // private
23387     initEvents: function ()
23388     {
23389         Roo.bootstrap.SecurePass.superclass.initEvents.call(this);
23390
23391         if (this.el.is('input[type=password]') && Roo.isSafari) {
23392             this.el.on('keydown', this.SafariOnKeyDown, this);
23393         }
23394
23395         this.el.on('keyup', this.checkStrength, this, {buffer: 50});
23396     },
23397     // private
23398     onRender: function (ct, position)
23399     {
23400         Roo.bootstrap.SecurePass.superclass.onRender.call(this, ct, position);
23401         this.wrap = this.el.wrap({cls: 'x-form-field-wrap'});
23402         this.trigger = this.wrap.createChild({tag: 'div', cls: 'StrengthMeter ' + this.triggerClass});
23403
23404         this.trigger.createChild({
23405                    cn: [
23406                     {
23407                     //id: 'PwdMeter',
23408                     tag: 'div',
23409                     cls: 'roo-password-meter-grey col-xs-12',
23410                     style: {
23411                         //width: 0,
23412                         //width: this.meterWidth + 'px'                                                
23413                         }
23414                     },
23415                     {                            
23416                          cls: 'roo-password-meter-text'                          
23417                     }
23418                 ]            
23419         });
23420
23421          
23422         if (this.hideTrigger) {
23423             this.trigger.setDisplayed(false);
23424         }
23425         this.setSize(this.width || '', this.height || '');
23426     },
23427     // private
23428     onDestroy: function ()
23429     {
23430         if (this.trigger) {
23431             this.trigger.removeAllListeners();
23432             this.trigger.remove();
23433         }
23434         if (this.wrap) {
23435             this.wrap.remove();
23436         }
23437         Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
23438     },
23439     // private
23440     checkStrength: function ()
23441     {
23442         var pwd = this.inputEl().getValue();
23443         if (pwd == this._lastPwd) {
23444             return;
23445         }
23446
23447         var strength;
23448         if (this.ClientSideStrongPassword(pwd)) {
23449             strength = 3;
23450         } else if (this.ClientSideMediumPassword(pwd)) {
23451             strength = 2;
23452         } else if (this.ClientSideWeakPassword(pwd)) {
23453             strength = 1;
23454         } else {
23455             strength = 0;
23456         }
23457         
23458         Roo.log('strength1: ' + strength);
23459         
23460         //var pm = this.trigger.child('div/div/div').dom;
23461         var pm = this.trigger.child('div/div');
23462         pm.removeClass(this.meterClass);
23463         pm.addClass(this.meterClass[strength]);
23464                 
23465         
23466         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
23467                 
23468         pt.innerHTML = this.meterLabel + '&nbsp;' + this.pwdStrengths[strength];
23469         
23470         this._lastPwd = pwd;
23471     },
23472     reset: function ()
23473     {
23474         Roo.bootstrap.SecurePass.superclass.reset.call(this);
23475         
23476         this._lastPwd = '';
23477         
23478         var pm = this.trigger.child('div/div');
23479         pm.removeClass(this.meterClass);
23480         pm.addClass('roo-password-meter-grey');        
23481         
23482         
23483         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
23484         
23485         pt.innerHTML = '';
23486         this.inputEl().dom.type='password';
23487     },
23488     // private
23489     validateValue: function (value)
23490     {
23491         if (!Roo.bootstrap.SecurePass.superclass.validateValue.call(this, value)) {
23492             return false;
23493         }
23494         if (value.length == 0) {
23495             if (this.allowBlank) {
23496                 this.clearInvalid();
23497                 return true;
23498             }
23499
23500             this.markInvalid(this.errors.PwdEmpty);
23501             this.errorMsg = this.errors.PwdEmpty;
23502             return false;
23503         }
23504         
23505         if(this.insecure){
23506             return true;
23507         }
23508         
23509         if (!value.match(/[\x21-\x7e]+/)) {
23510             this.markInvalid(this.errors.PwdBadChar);
23511             this.errorMsg = this.errors.PwdBadChar;
23512             return false;
23513         }
23514         if (value.length < 6) {
23515             this.markInvalid(this.errors.PwdShort);
23516             this.errorMsg = this.errors.PwdShort;
23517             return false;
23518         }
23519         if (value.length > 16) {
23520             this.markInvalid(this.errors.PwdLong);
23521             this.errorMsg = this.errors.PwdLong;
23522             return false;
23523         }
23524         var strength;
23525         if (this.ClientSideStrongPassword(value)) {
23526             strength = 3;
23527         } else if (this.ClientSideMediumPassword(value)) {
23528             strength = 2;
23529         } else if (this.ClientSideWeakPassword(value)) {
23530             strength = 1;
23531         } else {
23532             strength = 0;
23533         }
23534
23535         
23536         if (strength < 2) {
23537             //this.markInvalid(this.errors.TooWeak);
23538             this.errorMsg = this.errors.TooWeak;
23539             //return false;
23540         }
23541         
23542         
23543         console.log('strength2: ' + strength);
23544         
23545         //var pm = this.trigger.child('div/div/div').dom;
23546         
23547         var pm = this.trigger.child('div/div');
23548         pm.removeClass(this.meterClass);
23549         pm.addClass(this.meterClass[strength]);
23550                 
23551         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
23552                 
23553         pt.innerHTML = this.meterLabel + '&nbsp;' + this.pwdStrengths[strength];
23554         
23555         this.errorMsg = ''; 
23556         return true;
23557     },
23558     // private
23559     CharacterSetChecks: function (type)
23560     {
23561         this.type = type;
23562         this.fResult = false;
23563     },
23564     // private
23565     isctype: function (character, type)
23566     {
23567         switch (type) {  
23568             case this.kCapitalLetter:
23569                 if (character >= 'A' && character <= 'Z') {
23570                     return true;
23571                 }
23572                 break;
23573             
23574             case this.kSmallLetter:
23575                 if (character >= 'a' && character <= 'z') {
23576                     return true;
23577                 }
23578                 break;
23579             
23580             case this.kDigit:
23581                 if (character >= '0' && character <= '9') {
23582                     return true;
23583                 }
23584                 break;
23585             
23586             case this.kPunctuation:
23587                 if ('!@#$%^&*()_+-=\'";:[{]}|.>,</?`~'.indexOf(character) >= 0) {
23588                     return true;
23589                 }
23590                 break;
23591             
23592             default:
23593                 return false;
23594         }
23595
23596     },
23597     // private
23598     IsLongEnough: function (pwd, size)
23599     {
23600         return !(pwd == null || isNaN(size) || pwd.length < size);
23601     },
23602     // private
23603     SpansEnoughCharacterSets: function (word, nb)
23604     {
23605         if (!this.IsLongEnough(word, nb))
23606         {
23607             return false;
23608         }
23609
23610         var characterSetChecks = new Array(
23611             new this.CharacterSetChecks(this.kCapitalLetter), new this.CharacterSetChecks(this.kSmallLetter),
23612             new this.CharacterSetChecks(this.kDigit), new this.CharacterSetChecks(this.kPunctuation)
23613         );
23614         
23615         for (var index = 0; index < word.length; ++index) {
23616             for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
23617                 if (!characterSetChecks[nCharSet].fResult && this.isctype(word.charAt(index), characterSetChecks[nCharSet].type)) {
23618                     characterSetChecks[nCharSet].fResult = true;
23619                     break;
23620                 }
23621             }
23622         }
23623
23624         var nCharSets = 0;
23625         for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
23626             if (characterSetChecks[nCharSet].fResult) {
23627                 ++nCharSets;
23628             }
23629         }
23630
23631         if (nCharSets < nb) {
23632             return false;
23633         }
23634         return true;
23635     },
23636     // private
23637     ClientSideStrongPassword: function (pwd)
23638     {
23639         return this.IsLongEnough(pwd, 8) && this.SpansEnoughCharacterSets(pwd, 3);
23640     },
23641     // private
23642     ClientSideMediumPassword: function (pwd)
23643     {
23644         return this.IsLongEnough(pwd, 7) && this.SpansEnoughCharacterSets(pwd, 2);
23645     },
23646     // private
23647     ClientSideWeakPassword: function (pwd)
23648     {
23649         return this.IsLongEnough(pwd, 6) || !this.IsLongEnough(pwd, 0);
23650     }
23651           
23652 })//<script type="text/javascript">
23653
23654 /*
23655  * Based  Ext JS Library 1.1.1
23656  * Copyright(c) 2006-2007, Ext JS, LLC.
23657  * LGPL
23658  *
23659  */
23660  
23661 /**
23662  * @class Roo.HtmlEditorCore
23663  * @extends Roo.Component
23664  * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
23665  *
23666  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
23667  */
23668
23669 Roo.HtmlEditorCore = function(config){
23670     
23671     
23672     Roo.HtmlEditorCore.superclass.constructor.call(this, config);
23673     
23674     
23675     this.addEvents({
23676         /**
23677          * @event initialize
23678          * Fires when the editor is fully initialized (including the iframe)
23679          * @param {Roo.HtmlEditorCore} this
23680          */
23681         initialize: true,
23682         /**
23683          * @event activate
23684          * Fires when the editor is first receives the focus. Any insertion must wait
23685          * until after this event.
23686          * @param {Roo.HtmlEditorCore} this
23687          */
23688         activate: true,
23689          /**
23690          * @event beforesync
23691          * Fires before the textarea is updated with content from the editor iframe. Return false
23692          * to cancel the sync.
23693          * @param {Roo.HtmlEditorCore} this
23694          * @param {String} html
23695          */
23696         beforesync: true,
23697          /**
23698          * @event beforepush
23699          * Fires before the iframe editor is updated with content from the textarea. Return false
23700          * to cancel the push.
23701          * @param {Roo.HtmlEditorCore} this
23702          * @param {String} html
23703          */
23704         beforepush: true,
23705          /**
23706          * @event sync
23707          * Fires when the textarea is updated with content from the editor iframe.
23708          * @param {Roo.HtmlEditorCore} this
23709          * @param {String} html
23710          */
23711         sync: true,
23712          /**
23713          * @event push
23714          * Fires when the iframe editor is updated with content from the textarea.
23715          * @param {Roo.HtmlEditorCore} this
23716          * @param {String} html
23717          */
23718         push: true,
23719         
23720         /**
23721          * @event editorevent
23722          * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
23723          * @param {Roo.HtmlEditorCore} this
23724          */
23725         editorevent: true
23726         
23727     });
23728     
23729     // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
23730     
23731     // defaults : white / black...
23732     this.applyBlacklists();
23733     
23734     
23735     
23736 };
23737
23738
23739 Roo.extend(Roo.HtmlEditorCore, Roo.Component,  {
23740
23741
23742      /**
23743      * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field 
23744      */
23745     
23746     owner : false,
23747     
23748      /**
23749      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
23750      *                        Roo.resizable.
23751      */
23752     resizable : false,
23753      /**
23754      * @cfg {Number} height (in pixels)
23755      */   
23756     height: 300,
23757    /**
23758      * @cfg {Number} width (in pixels)
23759      */   
23760     width: 500,
23761     
23762     /**
23763      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
23764      * 
23765      */
23766     stylesheets: false,
23767     
23768     // id of frame..
23769     frameId: false,
23770     
23771     // private properties
23772     validationEvent : false,
23773     deferHeight: true,
23774     initialized : false,
23775     activated : false,
23776     sourceEditMode : false,
23777     onFocus : Roo.emptyFn,
23778     iframePad:3,
23779     hideMode:'offsets',
23780     
23781     clearUp: true,
23782     
23783     // blacklist + whitelisted elements..
23784     black: false,
23785     white: false,
23786      
23787     bodyCls : '',
23788
23789     /**
23790      * Protected method that will not generally be called directly. It
23791      * is called when the editor initializes the iframe with HTML contents. Override this method if you
23792      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
23793      */
23794     getDocMarkup : function(){
23795         // body styles..
23796         var st = '';
23797         
23798         // inherit styels from page...?? 
23799         if (this.stylesheets === false) {
23800             
23801             Roo.get(document.head).select('style').each(function(node) {
23802                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
23803             });
23804             
23805             Roo.get(document.head).select('link').each(function(node) { 
23806                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
23807             });
23808             
23809         } else if (!this.stylesheets.length) {
23810                 // simple..
23811                 st = '<style type="text/css">' +
23812                     'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
23813                    '</style>';
23814         } else {
23815             for (var i in this.stylesheets) { 
23816                 st += '<link rel="stylesheet" href="' + this.stylesheets[i] +'" type="text/css">';
23817             }
23818             
23819         }
23820         
23821         st +=  '<style type="text/css">' +
23822             'IMG { cursor: pointer } ' +
23823         '</style>';
23824
23825         var cls = 'roo-htmleditor-body';
23826         
23827         if(this.bodyCls.length){
23828             cls += ' ' + this.bodyCls;
23829         }
23830         
23831         return '<html><head>' + st  +
23832             //<style type="text/css">' +
23833             //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
23834             //'</style>' +
23835             ' </head><body contenteditable="true" data-enable-grammerly="true" class="' +  cls + '"></body></html>';
23836     },
23837
23838     // private
23839     onRender : function(ct, position)
23840     {
23841         var _t = this;
23842         //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
23843         this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
23844         
23845         
23846         this.el.dom.style.border = '0 none';
23847         this.el.dom.setAttribute('tabIndex', -1);
23848         this.el.addClass('x-hidden hide');
23849         
23850         
23851         
23852         if(Roo.isIE){ // fix IE 1px bogus margin
23853             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
23854         }
23855        
23856         
23857         this.frameId = Roo.id();
23858         
23859          
23860         
23861         var iframe = this.owner.wrap.createChild({
23862             tag: 'iframe',
23863             cls: 'form-control', // bootstrap..
23864             id: this.frameId,
23865             name: this.frameId,
23866             frameBorder : 'no',
23867             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
23868         }, this.el
23869         );
23870         
23871         
23872         this.iframe = iframe.dom;
23873
23874          this.assignDocWin();
23875         
23876         this.doc.designMode = 'on';
23877        
23878         this.doc.open();
23879         this.doc.write(this.getDocMarkup());
23880         this.doc.close();
23881
23882         
23883         var task = { // must defer to wait for browser to be ready
23884             run : function(){
23885                 //console.log("run task?" + this.doc.readyState);
23886                 this.assignDocWin();
23887                 if(this.doc.body || this.doc.readyState == 'complete'){
23888                     try {
23889                         this.doc.designMode="on";
23890                     } catch (e) {
23891                         return;
23892                     }
23893                     Roo.TaskMgr.stop(task);
23894                     this.initEditor.defer(10, this);
23895                 }
23896             },
23897             interval : 10,
23898             duration: 10000,
23899             scope: this
23900         };
23901         Roo.TaskMgr.start(task);
23902
23903     },
23904
23905     // private
23906     onResize : function(w, h)
23907     {
23908          Roo.log('resize: ' +w + ',' + h );
23909         //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
23910         if(!this.iframe){
23911             return;
23912         }
23913         if(typeof w == 'number'){
23914             
23915             this.iframe.style.width = w + 'px';
23916         }
23917         if(typeof h == 'number'){
23918             
23919             this.iframe.style.height = h + 'px';
23920             if(this.doc){
23921                 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
23922             }
23923         }
23924         
23925     },
23926
23927     /**
23928      * Toggles the editor between standard and source edit mode.
23929      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
23930      */
23931     toggleSourceEdit : function(sourceEditMode){
23932         
23933         this.sourceEditMode = sourceEditMode === true;
23934         
23935         if(this.sourceEditMode){
23936  
23937             Roo.get(this.iframe).addClass(['x-hidden','hide']);     //FIXME - what's the BS styles for these
23938             
23939         }else{
23940             Roo.get(this.iframe).removeClass(['x-hidden','hide']);
23941             //this.iframe.className = '';
23942             this.deferFocus();
23943         }
23944         //this.setSize(this.owner.wrap.getSize());
23945         //this.fireEvent('editmodechange', this, this.sourceEditMode);
23946     },
23947
23948     
23949   
23950
23951     /**
23952      * Protected method that will not generally be called directly. If you need/want
23953      * custom HTML cleanup, this is the method you should override.
23954      * @param {String} html The HTML to be cleaned
23955      * return {String} The cleaned HTML
23956      */
23957     cleanHtml : function(html){
23958         html = String(html);
23959         if(html.length > 5){
23960             if(Roo.isSafari){ // strip safari nonsense
23961                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
23962             }
23963         }
23964         if(html == '&nbsp;'){
23965             html = '';
23966         }
23967         return html;
23968     },
23969
23970     /**
23971      * HTML Editor -> Textarea
23972      * Protected method that will not generally be called directly. Syncs the contents
23973      * of the editor iframe with the textarea.
23974      */
23975     syncValue : function(){
23976         if(this.initialized){
23977             var bd = (this.doc.body || this.doc.documentElement);
23978             //this.cleanUpPaste(); -- this is done else where and causes havoc..
23979             var html = bd.innerHTML;
23980             if(Roo.isSafari){
23981                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
23982                 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
23983                 if(m && m[1]){
23984                     html = '<div style="'+m[0]+'">' + html + '</div>';
23985                 }
23986             }
23987             html = this.cleanHtml(html);
23988             // fix up the special chars.. normaly like back quotes in word...
23989             // however we do not want to do this with chinese..
23990             html = html.replace(/[\uD800-\uDBFF][\uDC00-\uDFFF]|[\u0080-\uFFFF]/g, function(match) {
23991                 
23992                 var cc = match.charCodeAt();
23993
23994                 // Get the character value, handling surrogate pairs
23995                 if (match.length == 2) {
23996                     // It's a surrogate pair, calculate the Unicode code point
23997                     var high = match.charCodeAt(0) - 0xD800;
23998                     var low  = match.charCodeAt(1) - 0xDC00;
23999                     cc = (high * 0x400) + low + 0x10000;
24000                 }  else if (
24001                     (cc >= 0x4E00 && cc < 0xA000 ) ||
24002                     (cc >= 0x3400 && cc < 0x4E00 ) ||
24003                     (cc >= 0xf900 && cc < 0xfb00 )
24004                 ) {
24005                         return match;
24006                 }  
24007          
24008                 // No, use a numeric entity. Here we brazenly (and possibly mistakenly)
24009                 return "&#" + cc + ";";
24010                 
24011                 
24012             });
24013             
24014             
24015              
24016             if(this.owner.fireEvent('beforesync', this, html) !== false){
24017                 this.el.dom.value = html;
24018                 this.owner.fireEvent('sync', this, html);
24019             }
24020         }
24021     },
24022
24023     /**
24024      * Protected method that will not generally be called directly. Pushes the value of the textarea
24025      * into the iframe editor.
24026      */
24027     pushValue : function(){
24028         if(this.initialized){
24029             var v = this.el.dom.value.trim();
24030             
24031 //            if(v.length < 1){
24032 //                v = '&#160;';
24033 //            }
24034             
24035             if(this.owner.fireEvent('beforepush', this, v) !== false){
24036                 var d = (this.doc.body || this.doc.documentElement);
24037                 d.innerHTML = v;
24038                 this.cleanUpPaste();
24039                 this.el.dom.value = d.innerHTML;
24040                 this.owner.fireEvent('push', this, v);
24041             }
24042         }
24043     },
24044
24045     // private
24046     deferFocus : function(){
24047         this.focus.defer(10, this);
24048     },
24049
24050     // doc'ed in Field
24051     focus : function(){
24052         if(this.win && !this.sourceEditMode){
24053             this.win.focus();
24054         }else{
24055             this.el.focus();
24056         }
24057     },
24058     
24059     assignDocWin: function()
24060     {
24061         var iframe = this.iframe;
24062         
24063          if(Roo.isIE){
24064             this.doc = iframe.contentWindow.document;
24065             this.win = iframe.contentWindow;
24066         } else {
24067 //            if (!Roo.get(this.frameId)) {
24068 //                return;
24069 //            }
24070 //            this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
24071 //            this.win = Roo.get(this.frameId).dom.contentWindow;
24072             
24073             if (!Roo.get(this.frameId) && !iframe.contentDocument) {
24074                 return;
24075             }
24076             
24077             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
24078             this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
24079         }
24080     },
24081     
24082     // private
24083     initEditor : function(){
24084         //console.log("INIT EDITOR");
24085         this.assignDocWin();
24086         
24087         
24088         
24089         this.doc.designMode="on";
24090         this.doc.open();
24091         this.doc.write(this.getDocMarkup());
24092         this.doc.close();
24093         
24094         var dbody = (this.doc.body || this.doc.documentElement);
24095         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
24096         // this copies styles from the containing element into thsi one..
24097         // not sure why we need all of this..
24098         //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
24099         
24100         //var ss = this.el.getStyles( 'background-image', 'background-repeat');
24101         //ss['background-attachment'] = 'fixed'; // w3c
24102         dbody.bgProperties = 'fixed'; // ie
24103         //Roo.DomHelper.applyStyles(dbody, ss);
24104         Roo.EventManager.on(this.doc, {
24105             //'mousedown': this.onEditorEvent,
24106             'mouseup': this.onEditorEvent,
24107             'dblclick': this.onEditorEvent,
24108             'click': this.onEditorEvent,
24109             'keyup': this.onEditorEvent,
24110             buffer:100,
24111             scope: this
24112         });
24113         if(Roo.isGecko){
24114             Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
24115         }
24116         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
24117             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
24118         }
24119         this.initialized = true;
24120
24121         this.owner.fireEvent('initialize', this);
24122         this.pushValue();
24123     },
24124
24125     // private
24126     onDestroy : function(){
24127         
24128         
24129         
24130         if(this.rendered){
24131             
24132             //for (var i =0; i < this.toolbars.length;i++) {
24133             //    // fixme - ask toolbars for heights?
24134             //    this.toolbars[i].onDestroy();
24135            // }
24136             
24137             //this.wrap.dom.innerHTML = '';
24138             //this.wrap.remove();
24139         }
24140     },
24141
24142     // private
24143     onFirstFocus : function(){
24144         
24145         this.assignDocWin();
24146         
24147         
24148         this.activated = true;
24149          
24150     
24151         if(Roo.isGecko){ // prevent silly gecko errors
24152             this.win.focus();
24153             var s = this.win.getSelection();
24154             if(!s.focusNode || s.focusNode.nodeType != 3){
24155                 var r = s.getRangeAt(0);
24156                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
24157                 r.collapse(true);
24158                 this.deferFocus();
24159             }
24160             try{
24161                 this.execCmd('useCSS', true);
24162                 this.execCmd('styleWithCSS', false);
24163             }catch(e){}
24164         }
24165         this.owner.fireEvent('activate', this);
24166     },
24167
24168     // private
24169     adjustFont: function(btn){
24170         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
24171         //if(Roo.isSafari){ // safari
24172         //    adjust *= 2;
24173        // }
24174         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
24175         if(Roo.isSafari){ // safari
24176             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
24177             v =  (v < 10) ? 10 : v;
24178             v =  (v > 48) ? 48 : v;
24179             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
24180             
24181         }
24182         
24183         
24184         v = Math.max(1, v+adjust);
24185         
24186         this.execCmd('FontSize', v  );
24187     },
24188
24189     onEditorEvent : function(e)
24190     {
24191         this.owner.fireEvent('editorevent', this, e);
24192       //  this.updateToolbar();
24193         this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
24194     },
24195
24196     insertTag : function(tg)
24197     {
24198         // could be a bit smarter... -> wrap the current selected tRoo..
24199         if (tg.toLowerCase() == 'span' ||
24200             tg.toLowerCase() == 'code' ||
24201             tg.toLowerCase() == 'sup' ||
24202             tg.toLowerCase() == 'sub' 
24203             ) {
24204             
24205             range = this.createRange(this.getSelection());
24206             var wrappingNode = this.doc.createElement(tg.toLowerCase());
24207             wrappingNode.appendChild(range.extractContents());
24208             range.insertNode(wrappingNode);
24209
24210             return;
24211             
24212             
24213             
24214         }
24215         this.execCmd("formatblock",   tg);
24216         
24217     },
24218     
24219     insertText : function(txt)
24220     {
24221         
24222         
24223         var range = this.createRange();
24224         range.deleteContents();
24225                //alert(Sender.getAttribute('label'));
24226                
24227         range.insertNode(this.doc.createTextNode(txt));
24228     } ,
24229     
24230      
24231
24232     /**
24233      * Executes a Midas editor command on the editor document and performs necessary focus and
24234      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
24235      * @param {String} cmd The Midas command
24236      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
24237      */
24238     relayCmd : function(cmd, value){
24239         this.win.focus();
24240         this.execCmd(cmd, value);
24241         this.owner.fireEvent('editorevent', this);
24242         //this.updateToolbar();
24243         this.owner.deferFocus();
24244     },
24245
24246     /**
24247      * Executes a Midas editor command directly on the editor document.
24248      * For visual commands, you should use {@link #relayCmd} instead.
24249      * <b>This should only be called after the editor is initialized.</b>
24250      * @param {String} cmd The Midas command
24251      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
24252      */
24253     execCmd : function(cmd, value){
24254         this.doc.execCommand(cmd, false, value === undefined ? null : value);
24255         this.syncValue();
24256     },
24257  
24258  
24259    
24260     /**
24261      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
24262      * to insert tRoo.
24263      * @param {String} text | dom node.. 
24264      */
24265     insertAtCursor : function(text)
24266     {
24267         
24268         if(!this.activated){
24269             return;
24270         }
24271         /*
24272         if(Roo.isIE){
24273             this.win.focus();
24274             var r = this.doc.selection.createRange();
24275             if(r){
24276                 r.collapse(true);
24277                 r.pasteHTML(text);
24278                 this.syncValue();
24279                 this.deferFocus();
24280             
24281             }
24282             return;
24283         }
24284         */
24285         if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
24286             this.win.focus();
24287             
24288             
24289             // from jquery ui (MIT licenced)
24290             var range, node;
24291             var win = this.win;
24292             
24293             if (win.getSelection && win.getSelection().getRangeAt) {
24294                 range = win.getSelection().getRangeAt(0);
24295                 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
24296                 range.insertNode(node);
24297             } else if (win.document.selection && win.document.selection.createRange) {
24298                 // no firefox support
24299                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
24300                 win.document.selection.createRange().pasteHTML(txt);
24301             } else {
24302                 // no firefox support
24303                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
24304                 this.execCmd('InsertHTML', txt);
24305             } 
24306             
24307             this.syncValue();
24308             
24309             this.deferFocus();
24310         }
24311     },
24312  // private
24313     mozKeyPress : function(e){
24314         if(e.ctrlKey){
24315             var c = e.getCharCode(), cmd;
24316           
24317             if(c > 0){
24318                 c = String.fromCharCode(c).toLowerCase();
24319                 switch(c){
24320                     case 'b':
24321                         cmd = 'bold';
24322                         break;
24323                     case 'i':
24324                         cmd = 'italic';
24325                         break;
24326                     
24327                     case 'u':
24328                         cmd = 'underline';
24329                         break;
24330                     
24331                     case 'v':
24332                         this.cleanUpPaste.defer(100, this);
24333                         return;
24334                         
24335                 }
24336                 if(cmd){
24337                     this.win.focus();
24338                     this.execCmd(cmd);
24339                     this.deferFocus();
24340                     e.preventDefault();
24341                 }
24342                 
24343             }
24344         }
24345     },
24346
24347     // private
24348     fixKeys : function(){ // load time branching for fastest keydown performance
24349         if(Roo.isIE){
24350             return function(e){
24351                 var k = e.getKey(), r;
24352                 if(k == e.TAB){
24353                     e.stopEvent();
24354                     r = this.doc.selection.createRange();
24355                     if(r){
24356                         r.collapse(true);
24357                         r.pasteHTML('&#160;&#160;&#160;&#160;');
24358                         this.deferFocus();
24359                     }
24360                     return;
24361                 }
24362                 
24363                 if(k == e.ENTER){
24364                     r = this.doc.selection.createRange();
24365                     if(r){
24366                         var target = r.parentElement();
24367                         if(!target || target.tagName.toLowerCase() != 'li'){
24368                             e.stopEvent();
24369                             r.pasteHTML('<br />');
24370                             r.collapse(false);
24371                             r.select();
24372                         }
24373                     }
24374                 }
24375                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
24376                     this.cleanUpPaste.defer(100, this);
24377                     return;
24378                 }
24379                 
24380                 
24381             };
24382         }else if(Roo.isOpera){
24383             return function(e){
24384                 var k = e.getKey();
24385                 if(k == e.TAB){
24386                     e.stopEvent();
24387                     this.win.focus();
24388                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
24389                     this.deferFocus();
24390                 }
24391                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
24392                     this.cleanUpPaste.defer(100, this);
24393                     return;
24394                 }
24395                 
24396             };
24397         }else if(Roo.isSafari){
24398             return function(e){
24399                 var k = e.getKey();
24400                 
24401                 if(k == e.TAB){
24402                     e.stopEvent();
24403                     this.execCmd('InsertText','\t');
24404                     this.deferFocus();
24405                     return;
24406                 }
24407                if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
24408                     this.cleanUpPaste.defer(100, this);
24409                     return;
24410                 }
24411                 
24412              };
24413         }
24414     }(),
24415     
24416     getAllAncestors: function()
24417     {
24418         var p = this.getSelectedNode();
24419         var a = [];
24420         if (!p) {
24421             a.push(p); // push blank onto stack..
24422             p = this.getParentElement();
24423         }
24424         
24425         
24426         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
24427             a.push(p);
24428             p = p.parentNode;
24429         }
24430         a.push(this.doc.body);
24431         return a;
24432     },
24433     lastSel : false,
24434     lastSelNode : false,
24435     
24436     
24437     getSelection : function() 
24438     {
24439         this.assignDocWin();
24440         return Roo.isIE ? this.doc.selection : this.win.getSelection();
24441     },
24442     
24443     getSelectedNode: function() 
24444     {
24445         // this may only work on Gecko!!!
24446         
24447         // should we cache this!!!!
24448         
24449         
24450         
24451          
24452         var range = this.createRange(this.getSelection()).cloneRange();
24453         
24454         if (Roo.isIE) {
24455             var parent = range.parentElement();
24456             while (true) {
24457                 var testRange = range.duplicate();
24458                 testRange.moveToElementText(parent);
24459                 if (testRange.inRange(range)) {
24460                     break;
24461                 }
24462                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
24463                     break;
24464                 }
24465                 parent = parent.parentElement;
24466             }
24467             return parent;
24468         }
24469         
24470         // is ancestor a text element.
24471         var ac =  range.commonAncestorContainer;
24472         if (ac.nodeType == 3) {
24473             ac = ac.parentNode;
24474         }
24475         
24476         var ar = ac.childNodes;
24477          
24478         var nodes = [];
24479         var other_nodes = [];
24480         var has_other_nodes = false;
24481         for (var i=0;i<ar.length;i++) {
24482             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
24483                 continue;
24484             }
24485             // fullly contained node.
24486             
24487             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
24488                 nodes.push(ar[i]);
24489                 continue;
24490             }
24491             
24492             // probably selected..
24493             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
24494                 other_nodes.push(ar[i]);
24495                 continue;
24496             }
24497             // outer..
24498             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
24499                 continue;
24500             }
24501             
24502             
24503             has_other_nodes = true;
24504         }
24505         if (!nodes.length && other_nodes.length) {
24506             nodes= other_nodes;
24507         }
24508         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
24509             return false;
24510         }
24511         
24512         return nodes[0];
24513     },
24514     createRange: function(sel)
24515     {
24516         // this has strange effects when using with 
24517         // top toolbar - not sure if it's a great idea.
24518         //this.editor.contentWindow.focus();
24519         if (typeof sel != "undefined") {
24520             try {
24521                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
24522             } catch(e) {
24523                 return this.doc.createRange();
24524             }
24525         } else {
24526             return this.doc.createRange();
24527         }
24528     },
24529     getParentElement: function()
24530     {
24531         
24532         this.assignDocWin();
24533         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
24534         
24535         var range = this.createRange(sel);
24536          
24537         try {
24538             var p = range.commonAncestorContainer;
24539             while (p.nodeType == 3) { // text node
24540                 p = p.parentNode;
24541             }
24542             return p;
24543         } catch (e) {
24544             return null;
24545         }
24546     
24547     },
24548     /***
24549      *
24550      * Range intersection.. the hard stuff...
24551      *  '-1' = before
24552      *  '0' = hits..
24553      *  '1' = after.
24554      *         [ -- selected range --- ]
24555      *   [fail]                        [fail]
24556      *
24557      *    basically..
24558      *      if end is before start or  hits it. fail.
24559      *      if start is after end or hits it fail.
24560      *
24561      *   if either hits (but other is outside. - then it's not 
24562      *   
24563      *    
24564      **/
24565     
24566     
24567     // @see http://www.thismuchiknow.co.uk/?p=64.
24568     rangeIntersectsNode : function(range, node)
24569     {
24570         var nodeRange = node.ownerDocument.createRange();
24571         try {
24572             nodeRange.selectNode(node);
24573         } catch (e) {
24574             nodeRange.selectNodeContents(node);
24575         }
24576     
24577         var rangeStartRange = range.cloneRange();
24578         rangeStartRange.collapse(true);
24579     
24580         var rangeEndRange = range.cloneRange();
24581         rangeEndRange.collapse(false);
24582     
24583         var nodeStartRange = nodeRange.cloneRange();
24584         nodeStartRange.collapse(true);
24585     
24586         var nodeEndRange = nodeRange.cloneRange();
24587         nodeEndRange.collapse(false);
24588     
24589         return rangeStartRange.compareBoundaryPoints(
24590                  Range.START_TO_START, nodeEndRange) == -1 &&
24591                rangeEndRange.compareBoundaryPoints(
24592                  Range.START_TO_START, nodeStartRange) == 1;
24593         
24594          
24595     },
24596     rangeCompareNode : function(range, node)
24597     {
24598         var nodeRange = node.ownerDocument.createRange();
24599         try {
24600             nodeRange.selectNode(node);
24601         } catch (e) {
24602             nodeRange.selectNodeContents(node);
24603         }
24604         
24605         
24606         range.collapse(true);
24607     
24608         nodeRange.collapse(true);
24609      
24610         var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
24611         var ee = range.compareBoundaryPoints(  Range.END_TO_END, nodeRange);
24612          
24613         //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
24614         
24615         var nodeIsBefore   =  ss == 1;
24616         var nodeIsAfter    = ee == -1;
24617         
24618         if (nodeIsBefore && nodeIsAfter) {
24619             return 0; // outer
24620         }
24621         if (!nodeIsBefore && nodeIsAfter) {
24622             return 1; //right trailed.
24623         }
24624         
24625         if (nodeIsBefore && !nodeIsAfter) {
24626             return 2;  // left trailed.
24627         }
24628         // fully contined.
24629         return 3;
24630     },
24631
24632     // private? - in a new class?
24633     cleanUpPaste :  function()
24634     {
24635         // cleans up the whole document..
24636         Roo.log('cleanuppaste');
24637         
24638         this.cleanUpChildren(this.doc.body);
24639         var clean = this.cleanWordChars(this.doc.body.innerHTML);
24640         if (clean != this.doc.body.innerHTML) {
24641             this.doc.body.innerHTML = clean;
24642         }
24643         
24644     },
24645     
24646     cleanWordChars : function(input) {// change the chars to hex code
24647         var he = Roo.HtmlEditorCore;
24648         
24649         var output = input;
24650         Roo.each(he.swapCodes, function(sw) { 
24651             var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
24652             
24653             output = output.replace(swapper, sw[1]);
24654         });
24655         
24656         return output;
24657     },
24658     
24659     
24660     cleanUpChildren : function (n)
24661     {
24662         if (!n.childNodes.length) {
24663             return;
24664         }
24665         for (var i = n.childNodes.length-1; i > -1 ; i--) {
24666            this.cleanUpChild(n.childNodes[i]);
24667         }
24668     },
24669     
24670     
24671         
24672     
24673     cleanUpChild : function (node)
24674     {
24675         var ed = this;
24676         //console.log(node);
24677         if (node.nodeName == "#text") {
24678             // clean up silly Windows -- stuff?
24679             return; 
24680         }
24681         if (node.nodeName == "#comment") {
24682             node.parentNode.removeChild(node);
24683             // clean up silly Windows -- stuff?
24684             return; 
24685         }
24686         var lcname = node.tagName.toLowerCase();
24687         // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
24688         // whitelist of tags..
24689         
24690         if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
24691             // remove node.
24692             node.parentNode.removeChild(node);
24693             return;
24694             
24695         }
24696         
24697         var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
24698         
24699         // spans with no attributes - just remove them..
24700         if ((!node.attributes || !node.attributes.length) && lcname == 'span') { 
24701             remove_keep_children = true;
24702         }
24703         
24704         // remove <a name=....> as rendering on yahoo mailer is borked with this.
24705         // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
24706         
24707         //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
24708         //    remove_keep_children = true;
24709         //}
24710         
24711         if (remove_keep_children) {
24712             this.cleanUpChildren(node);
24713             // inserts everything just before this node...
24714             while (node.childNodes.length) {
24715                 var cn = node.childNodes[0];
24716                 node.removeChild(cn);
24717                 node.parentNode.insertBefore(cn, node);
24718             }
24719             node.parentNode.removeChild(node);
24720             return;
24721         }
24722         
24723         if (!node.attributes || !node.attributes.length) {
24724             
24725           
24726             
24727             
24728             this.cleanUpChildren(node);
24729             return;
24730         }
24731         
24732         function cleanAttr(n,v)
24733         {
24734             
24735             if (v.match(/^\./) || v.match(/^\//)) {
24736                 return;
24737             }
24738             if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/) || v.match(/^ftp:/)) {
24739                 return;
24740             }
24741             if (v.match(/^#/)) {
24742                 return;
24743             }
24744             if (v.match(/^\{/)) { // allow template editing.
24745                 return;
24746             }
24747 //            Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
24748             node.removeAttribute(n);
24749             
24750         }
24751         
24752         var cwhite = this.cwhite;
24753         var cblack = this.cblack;
24754             
24755         function cleanStyle(n,v)
24756         {
24757             if (v.match(/expression/)) { //XSS?? should we even bother..
24758                 node.removeAttribute(n);
24759                 return;
24760             }
24761             
24762             var parts = v.split(/;/);
24763             var clean = [];
24764             
24765             Roo.each(parts, function(p) {
24766                 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
24767                 if (!p.length) {
24768                     return true;
24769                 }
24770                 var l = p.split(':').shift().replace(/\s+/g,'');
24771                 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
24772                 
24773                 if ( cwhite.length && cblack.indexOf(l) > -1) {
24774 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
24775                     //node.removeAttribute(n);
24776                     return true;
24777                 }
24778                 //Roo.log()
24779                 // only allow 'c whitelisted system attributes'
24780                 if ( cwhite.length &&  cwhite.indexOf(l) < 0) {
24781 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
24782                     //node.removeAttribute(n);
24783                     return true;
24784                 }
24785                 
24786                 
24787                  
24788                 
24789                 clean.push(p);
24790                 return true;
24791             });
24792             if (clean.length) { 
24793                 node.setAttribute(n, clean.join(';'));
24794             } else {
24795                 node.removeAttribute(n);
24796             }
24797             
24798         }
24799         
24800         
24801         for (var i = node.attributes.length-1; i > -1 ; i--) {
24802             var a = node.attributes[i];
24803             //console.log(a);
24804             
24805             if (a.name.toLowerCase().substr(0,2)=='on')  {
24806                 node.removeAttribute(a.name);
24807                 continue;
24808             }
24809             if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
24810                 node.removeAttribute(a.name);
24811                 continue;
24812             }
24813             if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
24814                 cleanAttr(a.name,a.value); // fixme..
24815                 continue;
24816             }
24817             if (a.name == 'style') {
24818                 cleanStyle(a.name,a.value);
24819                 continue;
24820             }
24821             /// clean up MS crap..
24822             // tecnically this should be a list of valid class'es..
24823             
24824             
24825             if (a.name == 'class') {
24826                 if (a.value.match(/^Mso/)) {
24827                     node.removeAttribute('class');
24828                 }
24829                 
24830                 if (a.value.match(/^body$/)) {
24831                     node.removeAttribute('class');
24832                 }
24833                 continue;
24834             }
24835             
24836             // style cleanup!?
24837             // class cleanup?
24838             
24839         }
24840         
24841         
24842         this.cleanUpChildren(node);
24843         
24844         
24845     },
24846     
24847     /**
24848      * Clean up MS wordisms...
24849      */
24850     cleanWord : function(node)
24851     {
24852         if (!node) {
24853             this.cleanWord(this.doc.body);
24854             return;
24855         }
24856         
24857         if(
24858                 node.nodeName == 'SPAN' &&
24859                 !node.hasAttributes() &&
24860                 node.childNodes.length == 1 &&
24861                 node.firstChild.nodeName == "#text"  
24862         ) {
24863             var textNode = node.firstChild;
24864             node.removeChild(textNode);
24865             if (node.getAttribute('lang') != 'zh-CN') {   // do not space pad on chinese characters..
24866                 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" "), node);
24867             }
24868             node.parentNode.insertBefore(textNode, node);
24869             if (node.getAttribute('lang') != 'zh-CN') {   // do not space pad on chinese characters..
24870                 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" ") , node);
24871             }
24872             node.parentNode.removeChild(node);
24873         }
24874         
24875         if (node.nodeName == "#text") {
24876             // clean up silly Windows -- stuff?
24877             return; 
24878         }
24879         if (node.nodeName == "#comment") {
24880             node.parentNode.removeChild(node);
24881             // clean up silly Windows -- stuff?
24882             return; 
24883         }
24884         
24885         if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
24886             node.parentNode.removeChild(node);
24887             return;
24888         }
24889         //Roo.log(node.tagName);
24890         // remove - but keep children..
24891         if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|v:|font)/)) {
24892             //Roo.log('-- removed');
24893             while (node.childNodes.length) {
24894                 var cn = node.childNodes[0];
24895                 node.removeChild(cn);
24896                 node.parentNode.insertBefore(cn, node);
24897                 // move node to parent - and clean it..
24898                 this.cleanWord(cn);
24899             }
24900             node.parentNode.removeChild(node);
24901             /// no need to iterate chidlren = it's got none..
24902             //this.iterateChildren(node, this.cleanWord);
24903             return;
24904         }
24905         // clean styles
24906         if (node.className.length) {
24907             
24908             var cn = node.className.split(/\W+/);
24909             var cna = [];
24910             Roo.each(cn, function(cls) {
24911                 if (cls.match(/Mso[a-zA-Z]+/)) {
24912                     return;
24913                 }
24914                 cna.push(cls);
24915             });
24916             node.className = cna.length ? cna.join(' ') : '';
24917             if (!cna.length) {
24918                 node.removeAttribute("class");
24919             }
24920         }
24921         
24922         if (node.hasAttribute("lang")) {
24923             node.removeAttribute("lang");
24924         }
24925         
24926         if (node.hasAttribute("style")) {
24927             
24928             var styles = node.getAttribute("style").split(";");
24929             var nstyle = [];
24930             Roo.each(styles, function(s) {
24931                 if (!s.match(/:/)) {
24932                     return;
24933                 }
24934                 var kv = s.split(":");
24935                 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
24936                     return;
24937                 }
24938                 // what ever is left... we allow.
24939                 nstyle.push(s);
24940             });
24941             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
24942             if (!nstyle.length) {
24943                 node.removeAttribute('style');
24944             }
24945         }
24946         this.iterateChildren(node, this.cleanWord);
24947         
24948         
24949         
24950     },
24951     /**
24952      * iterateChildren of a Node, calling fn each time, using this as the scole..
24953      * @param {DomNode} node node to iterate children of.
24954      * @param {Function} fn method of this class to call on each item.
24955      */
24956     iterateChildren : function(node, fn)
24957     {
24958         if (!node.childNodes.length) {
24959                 return;
24960         }
24961         for (var i = node.childNodes.length-1; i > -1 ; i--) {
24962            fn.call(this, node.childNodes[i])
24963         }
24964     },
24965     
24966     
24967     /**
24968      * cleanTableWidths.
24969      *
24970      * Quite often pasting from word etc.. results in tables with column and widths.
24971      * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
24972      *
24973      */
24974     cleanTableWidths : function(node)
24975     {
24976          
24977          
24978         if (!node) {
24979             this.cleanTableWidths(this.doc.body);
24980             return;
24981         }
24982         
24983         // ignore list...
24984         if (node.nodeName == "#text" || node.nodeName == "#comment") {
24985             return; 
24986         }
24987         Roo.log(node.tagName);
24988         if (!node.tagName.toLowerCase().match(/^(table|td|tr)$/)) {
24989             this.iterateChildren(node, this.cleanTableWidths);
24990             return;
24991         }
24992         if (node.hasAttribute('width')) {
24993             node.removeAttribute('width');
24994         }
24995         
24996          
24997         if (node.hasAttribute("style")) {
24998             // pretty basic...
24999             
25000             var styles = node.getAttribute("style").split(";");
25001             var nstyle = [];
25002             Roo.each(styles, function(s) {
25003                 if (!s.match(/:/)) {
25004                     return;
25005                 }
25006                 var kv = s.split(":");
25007                 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
25008                     return;
25009                 }
25010                 // what ever is left... we allow.
25011                 nstyle.push(s);
25012             });
25013             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
25014             if (!nstyle.length) {
25015                 node.removeAttribute('style');
25016             }
25017         }
25018         
25019         this.iterateChildren(node, this.cleanTableWidths);
25020         
25021         
25022     },
25023     
25024     
25025     
25026     
25027     domToHTML : function(currentElement, depth, nopadtext) {
25028         
25029         depth = depth || 0;
25030         nopadtext = nopadtext || false;
25031     
25032         if (!currentElement) {
25033             return this.domToHTML(this.doc.body);
25034         }
25035         
25036         //Roo.log(currentElement);
25037         var j;
25038         var allText = false;
25039         var nodeName = currentElement.nodeName;
25040         var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
25041         
25042         if  (nodeName == '#text') {
25043             
25044             return nopadtext ? currentElement.nodeValue : currentElement.nodeValue.trim();
25045         }
25046         
25047         
25048         var ret = '';
25049         if (nodeName != 'BODY') {
25050              
25051             var i = 0;
25052             // Prints the node tagName, such as <A>, <IMG>, etc
25053             if (tagName) {
25054                 var attr = [];
25055                 for(i = 0; i < currentElement.attributes.length;i++) {
25056                     // quoting?
25057                     var aname = currentElement.attributes.item(i).name;
25058                     if (!currentElement.attributes.item(i).value.length) {
25059                         continue;
25060                     }
25061                     attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
25062                 }
25063                 
25064                 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
25065             } 
25066             else {
25067                 
25068                 // eack
25069             }
25070         } else {
25071             tagName = false;
25072         }
25073         if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
25074             return ret;
25075         }
25076         if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
25077             nopadtext = true;
25078         }
25079         
25080         
25081         // Traverse the tree
25082         i = 0;
25083         var currentElementChild = currentElement.childNodes.item(i);
25084         var allText = true;
25085         var innerHTML  = '';
25086         lastnode = '';
25087         while (currentElementChild) {
25088             // Formatting code (indent the tree so it looks nice on the screen)
25089             var nopad = nopadtext;
25090             if (lastnode == 'SPAN') {
25091                 nopad  = true;
25092             }
25093             // text
25094             if  (currentElementChild.nodeName == '#text') {
25095                 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
25096                 toadd = nopadtext ? toadd : toadd.trim();
25097                 if (!nopad && toadd.length > 80) {
25098                     innerHTML  += "\n" + (new Array( depth + 1 )).join( "  "  );
25099                 }
25100                 innerHTML  += toadd;
25101                 
25102                 i++;
25103                 currentElementChild = currentElement.childNodes.item(i);
25104                 lastNode = '';
25105                 continue;
25106             }
25107             allText = false;
25108             
25109             innerHTML  += nopad ? '' : "\n" + (new Array( depth + 1 )).join( "  "  );
25110                 
25111             // Recursively traverse the tree structure of the child node
25112             innerHTML   += this.domToHTML(currentElementChild, depth+1, nopadtext);
25113             lastnode = currentElementChild.nodeName;
25114             i++;
25115             currentElementChild=currentElement.childNodes.item(i);
25116         }
25117         
25118         ret += innerHTML;
25119         
25120         if (!allText) {
25121                 // The remaining code is mostly for formatting the tree
25122             ret+= nopadtext ? '' : "\n" + (new Array( depth  )).join( "  "  );
25123         }
25124         
25125         
25126         if (tagName) {
25127             ret+= "</"+tagName+">";
25128         }
25129         return ret;
25130         
25131     },
25132         
25133     applyBlacklists : function()
25134     {
25135         var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white  : [];
25136         var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black :  [];
25137         
25138         this.white = [];
25139         this.black = [];
25140         Roo.each(Roo.HtmlEditorCore.white, function(tag) {
25141             if (b.indexOf(tag) > -1) {
25142                 return;
25143             }
25144             this.white.push(tag);
25145             
25146         }, this);
25147         
25148         Roo.each(w, function(tag) {
25149             if (b.indexOf(tag) > -1) {
25150                 return;
25151             }
25152             if (this.white.indexOf(tag) > -1) {
25153                 return;
25154             }
25155             this.white.push(tag);
25156             
25157         }, this);
25158         
25159         
25160         Roo.each(Roo.HtmlEditorCore.black, function(tag) {
25161             if (w.indexOf(tag) > -1) {
25162                 return;
25163             }
25164             this.black.push(tag);
25165             
25166         }, this);
25167         
25168         Roo.each(b, function(tag) {
25169             if (w.indexOf(tag) > -1) {
25170                 return;
25171             }
25172             if (this.black.indexOf(tag) > -1) {
25173                 return;
25174             }
25175             this.black.push(tag);
25176             
25177         }, this);
25178         
25179         
25180         w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite  : [];
25181         b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack :  [];
25182         
25183         this.cwhite = [];
25184         this.cblack = [];
25185         Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
25186             if (b.indexOf(tag) > -1) {
25187                 return;
25188             }
25189             this.cwhite.push(tag);
25190             
25191         }, this);
25192         
25193         Roo.each(w, function(tag) {
25194             if (b.indexOf(tag) > -1) {
25195                 return;
25196             }
25197             if (this.cwhite.indexOf(tag) > -1) {
25198                 return;
25199             }
25200             this.cwhite.push(tag);
25201             
25202         }, this);
25203         
25204         
25205         Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
25206             if (w.indexOf(tag) > -1) {
25207                 return;
25208             }
25209             this.cblack.push(tag);
25210             
25211         }, this);
25212         
25213         Roo.each(b, function(tag) {
25214             if (w.indexOf(tag) > -1) {
25215                 return;
25216             }
25217             if (this.cblack.indexOf(tag) > -1) {
25218                 return;
25219             }
25220             this.cblack.push(tag);
25221             
25222         }, this);
25223     },
25224     
25225     setStylesheets : function(stylesheets)
25226     {
25227         if(typeof(stylesheets) == 'string'){
25228             Roo.get(this.iframe.contentDocument.head).createChild({
25229                 tag : 'link',
25230                 rel : 'stylesheet',
25231                 type : 'text/css',
25232                 href : stylesheets
25233             });
25234             
25235             return;
25236         }
25237         var _this = this;
25238      
25239         Roo.each(stylesheets, function(s) {
25240             if(!s.length){
25241                 return;
25242             }
25243             
25244             Roo.get(_this.iframe.contentDocument.head).createChild({
25245                 tag : 'link',
25246                 rel : 'stylesheet',
25247                 type : 'text/css',
25248                 href : s
25249             });
25250         });
25251
25252         
25253     },
25254     
25255     removeStylesheets : function()
25256     {
25257         var _this = this;
25258         
25259         Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
25260             s.remove();
25261         });
25262     },
25263     
25264     setStyle : function(style)
25265     {
25266         Roo.get(this.iframe.contentDocument.head).createChild({
25267             tag : 'style',
25268             type : 'text/css',
25269             html : style
25270         });
25271
25272         return;
25273     }
25274     
25275     // hide stuff that is not compatible
25276     /**
25277      * @event blur
25278      * @hide
25279      */
25280     /**
25281      * @event change
25282      * @hide
25283      */
25284     /**
25285      * @event focus
25286      * @hide
25287      */
25288     /**
25289      * @event specialkey
25290      * @hide
25291      */
25292     /**
25293      * @cfg {String} fieldClass @hide
25294      */
25295     /**
25296      * @cfg {String} focusClass @hide
25297      */
25298     /**
25299      * @cfg {String} autoCreate @hide
25300      */
25301     /**
25302      * @cfg {String} inputType @hide
25303      */
25304     /**
25305      * @cfg {String} invalidClass @hide
25306      */
25307     /**
25308      * @cfg {String} invalidText @hide
25309      */
25310     /**
25311      * @cfg {String} msgFx @hide
25312      */
25313     /**
25314      * @cfg {String} validateOnBlur @hide
25315      */
25316 });
25317
25318 Roo.HtmlEditorCore.white = [
25319         'area', 'br', 'img', 'input', 'hr', 'wbr',
25320         
25321        'address', 'blockquote', 'center', 'dd',      'dir',       'div', 
25322        'dl',      'dt',         'h1',     'h2',      'h3',        'h4', 
25323        'h5',      'h6',         'hr',     'isindex', 'listing',   'marquee', 
25324        'menu',    'multicol',   'ol',     'p',       'plaintext', 'pre', 
25325        'table',   'ul',         'xmp', 
25326        
25327        'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th', 
25328       'thead',   'tr', 
25329      
25330       'dir', 'menu', 'ol', 'ul', 'dl',
25331        
25332       'embed',  'object'
25333 ];
25334
25335
25336 Roo.HtmlEditorCore.black = [
25337     //    'embed',  'object', // enable - backend responsiblity to clean thiese
25338         'applet', // 
25339         'base',   'basefont', 'bgsound', 'blink',  'body', 
25340         'frame',  'frameset', 'head',    'html',   'ilayer', 
25341         'iframe', 'layer',  'link',     'meta',    'object',   
25342         'script', 'style' ,'title',  'xml' // clean later..
25343 ];
25344 Roo.HtmlEditorCore.clean = [
25345     'script', 'style', 'title', 'xml'
25346 ];
25347 Roo.HtmlEditorCore.remove = [
25348     'font'
25349 ];
25350 // attributes..
25351
25352 Roo.HtmlEditorCore.ablack = [
25353     'on'
25354 ];
25355     
25356 Roo.HtmlEditorCore.aclean = [ 
25357     'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc' 
25358 ];
25359
25360 // protocols..
25361 Roo.HtmlEditorCore.pwhite= [
25362         'http',  'https',  'mailto'
25363 ];
25364
25365 // white listed style attributes.
25366 Roo.HtmlEditorCore.cwhite= [
25367       //  'text-align', /// default is to allow most things..
25368       
25369          
25370 //        'font-size'//??
25371 ];
25372
25373 // black listed style attributes.
25374 Roo.HtmlEditorCore.cblack= [
25375       //  'font-size' -- this can be set by the project 
25376 ];
25377
25378
25379 Roo.HtmlEditorCore.swapCodes   =[ 
25380     [    8211, "--" ], 
25381     [    8212, "--" ], 
25382     [    8216,  "'" ],  
25383     [    8217, "'" ],  
25384     [    8220, '"' ],  
25385     [    8221, '"' ],  
25386     [    8226, "*" ],  
25387     [    8230, "..." ]
25388 ]; 
25389
25390     /*
25391  * - LGPL
25392  *
25393  * HtmlEditor
25394  * 
25395  */
25396
25397 /**
25398  * @class Roo.bootstrap.HtmlEditor
25399  * @extends Roo.bootstrap.TextArea
25400  * Bootstrap HtmlEditor class
25401
25402  * @constructor
25403  * Create a new HtmlEditor
25404  * @param {Object} config The config object
25405  */
25406
25407 Roo.bootstrap.HtmlEditor = function(config){
25408     Roo.bootstrap.HtmlEditor.superclass.constructor.call(this, config);
25409     if (!this.toolbars) {
25410         this.toolbars = [];
25411     }
25412     
25413     this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
25414     this.addEvents({
25415             /**
25416              * @event initialize
25417              * Fires when the editor is fully initialized (including the iframe)
25418              * @param {HtmlEditor} this
25419              */
25420             initialize: true,
25421             /**
25422              * @event activate
25423              * Fires when the editor is first receives the focus. Any insertion must wait
25424              * until after this event.
25425              * @param {HtmlEditor} this
25426              */
25427             activate: true,
25428              /**
25429              * @event beforesync
25430              * Fires before the textarea is updated with content from the editor iframe. Return false
25431              * to cancel the sync.
25432              * @param {HtmlEditor} this
25433              * @param {String} html
25434              */
25435             beforesync: true,
25436              /**
25437              * @event beforepush
25438              * Fires before the iframe editor is updated with content from the textarea. Return false
25439              * to cancel the push.
25440              * @param {HtmlEditor} this
25441              * @param {String} html
25442              */
25443             beforepush: true,
25444              /**
25445              * @event sync
25446              * Fires when the textarea is updated with content from the editor iframe.
25447              * @param {HtmlEditor} this
25448              * @param {String} html
25449              */
25450             sync: true,
25451              /**
25452              * @event push
25453              * Fires when the iframe editor is updated with content from the textarea.
25454              * @param {HtmlEditor} this
25455              * @param {String} html
25456              */
25457             push: true,
25458              /**
25459              * @event editmodechange
25460              * Fires when the editor switches edit modes
25461              * @param {HtmlEditor} this
25462              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
25463              */
25464             editmodechange: true,
25465             /**
25466              * @event editorevent
25467              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
25468              * @param {HtmlEditor} this
25469              */
25470             editorevent: true,
25471             /**
25472              * @event firstfocus
25473              * Fires when on first focus - needed by toolbars..
25474              * @param {HtmlEditor} this
25475              */
25476             firstfocus: true,
25477             /**
25478              * @event autosave
25479              * Auto save the htmlEditor value as a file into Events
25480              * @param {HtmlEditor} this
25481              */
25482             autosave: true,
25483             /**
25484              * @event savedpreview
25485              * preview the saved version of htmlEditor
25486              * @param {HtmlEditor} this
25487              */
25488             savedpreview: true
25489         });
25490 };
25491
25492
25493 Roo.extend(Roo.bootstrap.HtmlEditor, Roo.bootstrap.TextArea,  {
25494     
25495     
25496       /**
25497      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
25498      */
25499     toolbars : false,
25500     
25501      /**
25502     * @cfg {Array} buttons Array of toolbar's buttons. - defaults to empty
25503     */
25504     btns : [],
25505    
25506      /**
25507      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
25508      *                        Roo.resizable.
25509      */
25510     resizable : false,
25511      /**
25512      * @cfg {Number} height (in pixels)
25513      */   
25514     height: 300,
25515    /**
25516      * @cfg {Number} width (in pixels)
25517      */   
25518     width: false,
25519     
25520     /**
25521      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
25522      * 
25523      */
25524     stylesheets: false,
25525     
25526     // id of frame..
25527     frameId: false,
25528     
25529     // private properties
25530     validationEvent : false,
25531     deferHeight: true,
25532     initialized : false,
25533     activated : false,
25534     
25535     onFocus : Roo.emptyFn,
25536     iframePad:3,
25537     hideMode:'offsets',
25538     
25539     tbContainer : false,
25540     
25541     bodyCls : '',
25542     
25543     toolbarContainer :function() {
25544         return this.wrap.select('.x-html-editor-tb',true).first();
25545     },
25546
25547     /**
25548      * Protected method that will not generally be called directly. It
25549      * is called when the editor creates its toolbar. Override this method if you need to
25550      * add custom toolbar buttons.
25551      * @param {HtmlEditor} editor
25552      */
25553     createToolbar : function(){
25554         Roo.log('renewing');
25555         Roo.log("create toolbars");
25556         
25557         this.toolbars = [ new Roo.bootstrap.htmleditor.ToolbarStandard({editor: this} ) ];
25558         this.toolbars[0].render(this.toolbarContainer());
25559         
25560         return;
25561         
25562 //        if (!editor.toolbars || !editor.toolbars.length) {
25563 //            editor.toolbars = [ new Roo.bootstrap.HtmlEditor.ToolbarStandard() ]; // can be empty?
25564 //        }
25565 //        
25566 //        for (var i =0 ; i < editor.toolbars.length;i++) {
25567 //            editor.toolbars[i] = Roo.factory(
25568 //                    typeof(editor.toolbars[i]) == 'string' ?
25569 //                        { xtype: editor.toolbars[i]} : editor.toolbars[i],
25570 //                Roo.bootstrap.HtmlEditor);
25571 //            editor.toolbars[i].init(editor);
25572 //        }
25573     },
25574
25575      
25576     // private
25577     onRender : function(ct, position)
25578     {
25579        // Roo.log("Call onRender: " + this.xtype);
25580         var _t = this;
25581         Roo.bootstrap.HtmlEditor.superclass.onRender.call(this, ct, position);
25582       
25583         this.wrap = this.inputEl().wrap({
25584             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
25585         });
25586         
25587         this.editorcore.onRender(ct, position);
25588          
25589         if (this.resizable) {
25590             this.resizeEl = new Roo.Resizable(this.wrap, {
25591                 pinned : true,
25592                 wrap: true,
25593                 dynamic : true,
25594                 minHeight : this.height,
25595                 height: this.height,
25596                 handles : this.resizable,
25597                 width: this.width,
25598                 listeners : {
25599                     resize : function(r, w, h) {
25600                         _t.onResize(w,h); // -something
25601                     }
25602                 }
25603             });
25604             
25605         }
25606         this.createToolbar(this);
25607        
25608         
25609         if(!this.width && this.resizable){
25610             this.setSize(this.wrap.getSize());
25611         }
25612         if (this.resizeEl) {
25613             this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
25614             // should trigger onReize..
25615         }
25616         
25617     },
25618
25619     // private
25620     onResize : function(w, h)
25621     {
25622         Roo.log('resize: ' +w + ',' + h );
25623         Roo.bootstrap.HtmlEditor.superclass.onResize.apply(this, arguments);
25624         var ew = false;
25625         var eh = false;
25626         
25627         if(this.inputEl() ){
25628             if(typeof w == 'number'){
25629                 var aw = w - this.wrap.getFrameWidth('lr');
25630                 this.inputEl().setWidth(this.adjustWidth('textarea', aw));
25631                 ew = aw;
25632             }
25633             if(typeof h == 'number'){
25634                  var tbh = -11;  // fixme it needs to tool bar size!
25635                 for (var i =0; i < this.toolbars.length;i++) {
25636                     // fixme - ask toolbars for heights?
25637                     tbh += this.toolbars[i].el.getHeight();
25638                     //if (this.toolbars[i].footer) {
25639                     //    tbh += this.toolbars[i].footer.el.getHeight();
25640                     //}
25641                 }
25642               
25643                 
25644                 
25645                 
25646                 
25647                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
25648                 ah -= 5; // knock a few pixes off for look..
25649                 this.inputEl().setHeight(this.adjustWidth('textarea', ah));
25650                 var eh = ah;
25651             }
25652         }
25653         Roo.log('onResize:' + [w,h,ew,eh].join(',') );
25654         this.editorcore.onResize(ew,eh);
25655         
25656     },
25657
25658     /**
25659      * Toggles the editor between standard and source edit mode.
25660      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
25661      */
25662     toggleSourceEdit : function(sourceEditMode)
25663     {
25664         this.editorcore.toggleSourceEdit(sourceEditMode);
25665         
25666         if(this.editorcore.sourceEditMode){
25667             Roo.log('editor - showing textarea');
25668             
25669 //            Roo.log('in');
25670 //            Roo.log(this.syncValue());
25671             this.syncValue();
25672             this.inputEl().removeClass(['hide', 'x-hidden']);
25673             this.inputEl().dom.removeAttribute('tabIndex');
25674             this.inputEl().focus();
25675         }else{
25676             Roo.log('editor - hiding textarea');
25677 //            Roo.log('out')
25678 //            Roo.log(this.pushValue()); 
25679             this.pushValue();
25680             
25681             this.inputEl().addClass(['hide', 'x-hidden']);
25682             this.inputEl().dom.setAttribute('tabIndex', -1);
25683             //this.deferFocus();
25684         }
25685          
25686         if(this.resizable){
25687             this.setSize(this.wrap.getSize());
25688         }
25689         
25690         this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
25691     },
25692  
25693     // private (for BoxComponent)
25694     adjustSize : Roo.BoxComponent.prototype.adjustSize,
25695
25696     // private (for BoxComponent)
25697     getResizeEl : function(){
25698         return this.wrap;
25699     },
25700
25701     // private (for BoxComponent)
25702     getPositionEl : function(){
25703         return this.wrap;
25704     },
25705
25706     // private
25707     initEvents : function(){
25708         this.originalValue = this.getValue();
25709     },
25710
25711 //    /**
25712 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
25713 //     * @method
25714 //     */
25715 //    markInvalid : Roo.emptyFn,
25716 //    /**
25717 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
25718 //     * @method
25719 //     */
25720 //    clearInvalid : Roo.emptyFn,
25721
25722     setValue : function(v){
25723         Roo.bootstrap.HtmlEditor.superclass.setValue.call(this, v);
25724         this.editorcore.pushValue();
25725     },
25726
25727      
25728     // private
25729     deferFocus : function(){
25730         this.focus.defer(10, this);
25731     },
25732
25733     // doc'ed in Field
25734     focus : function(){
25735         this.editorcore.focus();
25736         
25737     },
25738       
25739
25740     // private
25741     onDestroy : function(){
25742         
25743         
25744         
25745         if(this.rendered){
25746             
25747             for (var i =0; i < this.toolbars.length;i++) {
25748                 // fixme - ask toolbars for heights?
25749                 this.toolbars[i].onDestroy();
25750             }
25751             
25752             this.wrap.dom.innerHTML = '';
25753             this.wrap.remove();
25754         }
25755     },
25756
25757     // private
25758     onFirstFocus : function(){
25759         //Roo.log("onFirstFocus");
25760         this.editorcore.onFirstFocus();
25761          for (var i =0; i < this.toolbars.length;i++) {
25762             this.toolbars[i].onFirstFocus();
25763         }
25764         
25765     },
25766     
25767     // private
25768     syncValue : function()
25769     {   
25770         this.editorcore.syncValue();
25771     },
25772     
25773     pushValue : function()
25774     {   
25775         this.editorcore.pushValue();
25776     }
25777      
25778     
25779     // hide stuff that is not compatible
25780     /**
25781      * @event blur
25782      * @hide
25783      */
25784     /**
25785      * @event change
25786      * @hide
25787      */
25788     /**
25789      * @event focus
25790      * @hide
25791      */
25792     /**
25793      * @event specialkey
25794      * @hide
25795      */
25796     /**
25797      * @cfg {String} fieldClass @hide
25798      */
25799     /**
25800      * @cfg {String} focusClass @hide
25801      */
25802     /**
25803      * @cfg {String} autoCreate @hide
25804      */
25805     /**
25806      * @cfg {String} inputType @hide
25807      */
25808      
25809     /**
25810      * @cfg {String} invalidText @hide
25811      */
25812     /**
25813      * @cfg {String} msgFx @hide
25814      */
25815     /**
25816      * @cfg {String} validateOnBlur @hide
25817      */
25818 });
25819  
25820     
25821    
25822    
25823    
25824       
25825 Roo.namespace('Roo.bootstrap.htmleditor');
25826 /**
25827  * @class Roo.bootstrap.HtmlEditorToolbar1
25828  * Basic Toolbar
25829  * 
25830  * @example
25831  * Usage:
25832  *
25833  new Roo.bootstrap.HtmlEditor({
25834     ....
25835     toolbars : [
25836         new Roo.bootstrap.HtmlEditorToolbar1({
25837             disable : { fonts: 1 , format: 1, ..., ... , ...],
25838             btns : [ .... ]
25839         })
25840     }
25841      
25842  * 
25843  * @cfg {Object} disable List of elements to disable..
25844  * @cfg {Array} btns List of additional buttons.
25845  * 
25846  * 
25847  * NEEDS Extra CSS? 
25848  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
25849  */
25850  
25851 Roo.bootstrap.htmleditor.ToolbarStandard = function(config)
25852 {
25853     
25854     Roo.apply(this, config);
25855     
25856     // default disabled, based on 'good practice'..
25857     this.disable = this.disable || {};
25858     Roo.applyIf(this.disable, {
25859         fontSize : true,
25860         colors : true,
25861         specialElements : true
25862     });
25863     Roo.bootstrap.htmleditor.ToolbarStandard.superclass.constructor.call(this, config);
25864     
25865     this.editor = config.editor;
25866     this.editorcore = config.editor.editorcore;
25867     
25868     this.buttons   = new Roo.util.MixedCollection(false, function(o) { return o.cmd; });
25869     
25870     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
25871     // dont call parent... till later.
25872 }
25873 Roo.extend(Roo.bootstrap.htmleditor.ToolbarStandard, Roo.bootstrap.NavSimplebar,  {
25874      
25875     bar : true,
25876     
25877     editor : false,
25878     editorcore : false,
25879     
25880     
25881     formats : [
25882         "p" ,  
25883         "h1","h2","h3","h4","h5","h6", 
25884         "pre", "code", 
25885         "abbr", "acronym", "address", "cite", "samp", "var",
25886         'div','span'
25887     ],
25888     
25889     onRender : function(ct, position)
25890     {
25891        // Roo.log("Call onRender: " + this.xtype);
25892         
25893        Roo.bootstrap.htmleditor.ToolbarStandard.superclass.onRender.call(this, ct, position);
25894        Roo.log(this.el);
25895        this.el.dom.style.marginBottom = '0';
25896        var _this = this;
25897        var editorcore = this.editorcore;
25898        var editor= this.editor;
25899        
25900        var children = [];
25901        var btn = function(id,cmd , toggle, handler, html){
25902        
25903             var  event = toggle ? 'toggle' : 'click';
25904        
25905             var a = {
25906                 size : 'sm',
25907                 xtype: 'Button',
25908                 xns: Roo.bootstrap,
25909                 //glyphicon : id,
25910                 fa: id,
25911                 cmd : id || cmd,
25912                 enableToggle:toggle !== false,
25913                 html : html || '',
25914                 pressed : toggle ? false : null,
25915                 listeners : {}
25916             };
25917             a.listeners[toggle ? 'toggle' : 'click'] = function() {
25918                 handler ? handler.call(_this,this) :_this.onBtnClick.call(_this, cmd ||  id);
25919             };
25920             children.push(a);
25921             return a;
25922        }
25923        
25924     //    var cb_box = function...
25925         
25926         var style = {
25927                 xtype: 'Button',
25928                 size : 'sm',
25929                 xns: Roo.bootstrap,
25930                 fa : 'font',
25931                 //html : 'submit'
25932                 menu : {
25933                     xtype: 'Menu',
25934                     xns: Roo.bootstrap,
25935                     items:  []
25936                 }
25937         };
25938         Roo.each(this.formats, function(f) {
25939             style.menu.items.push({
25940                 xtype :'MenuItem',
25941                 xns: Roo.bootstrap,
25942                 html : '<'+ f+' style="margin:2px">'+f +'</'+ f+'>',
25943                 tagname : f,
25944                 listeners : {
25945                     click : function()
25946                     {
25947                         editorcore.insertTag(this.tagname);
25948                         editor.focus();
25949                     }
25950                 }
25951                 
25952             });
25953         });
25954         children.push(style);   
25955         
25956         btn('bold',false,true);
25957         btn('italic',false,true);
25958         btn('align-left', 'justifyleft',true);
25959         btn('align-center', 'justifycenter',true);
25960         btn('align-right' , 'justifyright',true);
25961         btn('link', false, false, function(btn) {
25962             //Roo.log("create link?");
25963             var url = prompt(this.createLinkText, this.defaultLinkValue);
25964             if(url && url != 'http:/'+'/'){
25965                 this.editorcore.relayCmd('createlink', url);
25966             }
25967         }),
25968         btn('list','insertunorderedlist',true);
25969         btn('pencil', false,true, function(btn){
25970                 Roo.log(this);
25971                 this.toggleSourceEdit(btn.pressed);
25972         });
25973         
25974         if (this.editor.btns.length > 0) {
25975             for (var i = 0; i<this.editor.btns.length; i++) {
25976                 children.push(this.editor.btns[i]);
25977             }
25978         }
25979         
25980         /*
25981         var cog = {
25982                 xtype: 'Button',
25983                 size : 'sm',
25984                 xns: Roo.bootstrap,
25985                 glyphicon : 'cog',
25986                 //html : 'submit'
25987                 menu : {
25988                     xtype: 'Menu',
25989                     xns: Roo.bootstrap,
25990                     items:  []
25991                 }
25992         };
25993         
25994         cog.menu.items.push({
25995             xtype :'MenuItem',
25996             xns: Roo.bootstrap,
25997             html : Clean styles,
25998             tagname : f,
25999             listeners : {
26000                 click : function()
26001                 {
26002                     editorcore.insertTag(this.tagname);
26003                     editor.focus();
26004                 }
26005             }
26006             
26007         });
26008        */
26009         
26010          
26011        this.xtype = 'NavSimplebar';
26012         
26013         for(var i=0;i< children.length;i++) {
26014             
26015             this.buttons.add(this.addxtypeChild(children[i]));
26016             
26017         }
26018         
26019         editor.on('editorevent', this.updateToolbar, this);
26020     },
26021     onBtnClick : function(id)
26022     {
26023        this.editorcore.relayCmd(id);
26024        this.editorcore.focus();
26025     },
26026     
26027     /**
26028      * Protected method that will not generally be called directly. It triggers
26029      * a toolbar update by reading the markup state of the current selection in the editor.
26030      */
26031     updateToolbar: function(){
26032
26033         if(!this.editorcore.activated){
26034             this.editor.onFirstFocus(); // is this neeed?
26035             return;
26036         }
26037
26038         var btns = this.buttons; 
26039         var doc = this.editorcore.doc;
26040         btns.get('bold').setActive(doc.queryCommandState('bold'));
26041         btns.get('italic').setActive(doc.queryCommandState('italic'));
26042         //btns.get('underline').setActive(doc.queryCommandState('underline'));
26043         
26044         btns.get('align-left').setActive(doc.queryCommandState('justifyleft'));
26045         btns.get('align-center').setActive(doc.queryCommandState('justifycenter'));
26046         btns.get('align-right').setActive(doc.queryCommandState('justifyright'));
26047         
26048         //btns[frameId + '-insertorderedlist').setActive(doc.queryCommandState('insertorderedlist'));
26049         btns.get('list').setActive(doc.queryCommandState('insertunorderedlist'));
26050          /*
26051         
26052         var ans = this.editorcore.getAllAncestors();
26053         if (this.formatCombo) {
26054             
26055             
26056             var store = this.formatCombo.store;
26057             this.formatCombo.setValue("");
26058             for (var i =0; i < ans.length;i++) {
26059                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
26060                     // select it..
26061                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
26062                     break;
26063                 }
26064             }
26065         }
26066         
26067         
26068         
26069         // hides menus... - so this cant be on a menu...
26070         Roo.bootstrap.MenuMgr.hideAll();
26071         */
26072         Roo.bootstrap.MenuMgr.hideAll();
26073         //this.editorsyncValue();
26074     },
26075     onFirstFocus: function() {
26076         this.buttons.each(function(item){
26077            item.enable();
26078         });
26079     },
26080     toggleSourceEdit : function(sourceEditMode){
26081         
26082           
26083         if(sourceEditMode){
26084             Roo.log("disabling buttons");
26085            this.buttons.each( function(item){
26086                 if(item.cmd != 'pencil'){
26087                     item.disable();
26088                 }
26089             });
26090           
26091         }else{
26092             Roo.log("enabling buttons");
26093             if(this.editorcore.initialized){
26094                 this.buttons.each( function(item){
26095                     item.enable();
26096                 });
26097             }
26098             
26099         }
26100         Roo.log("calling toggole on editor");
26101         // tell the editor that it's been pressed..
26102         this.editor.toggleSourceEdit(sourceEditMode);
26103        
26104     }
26105 });
26106
26107
26108
26109
26110  
26111 /*
26112  * - LGPL
26113  */
26114
26115 /**
26116  * @class Roo.bootstrap.Markdown
26117  * @extends Roo.bootstrap.TextArea
26118  * Bootstrap Showdown editable area
26119  * @cfg {string} content
26120  * 
26121  * @constructor
26122  * Create a new Showdown
26123  */
26124
26125 Roo.bootstrap.Markdown = function(config){
26126     Roo.bootstrap.Markdown.superclass.constructor.call(this, config);
26127    
26128 };
26129
26130 Roo.extend(Roo.bootstrap.Markdown, Roo.bootstrap.TextArea,  {
26131     
26132     editing :false,
26133     
26134     initEvents : function()
26135     {
26136         
26137         Roo.bootstrap.TextArea.prototype.initEvents.call(this);
26138         this.markdownEl = this.el.createChild({
26139             cls : 'roo-markdown-area'
26140         });
26141         this.inputEl().addClass('d-none');
26142         this.markdownEl.dom.innerHTML = Roo.Markdown.toHtml(Roo.util.Format.htmlEncode(this.getValue()));
26143         this.markdownEl.on('click', this.toggleTextEdit, this);
26144         this.on('blur', this.toggleTextEdit, this);
26145         this.on('specialkey', this.resizeTextArea, this);
26146     },
26147     
26148     toggleTextEdit : function()
26149     {
26150         var sh = this.markdownEl.getHeight();
26151         this.inputEl().addClass('d-none');
26152         this.markdownEl.addClass('d-none');
26153         if (!this.editing) {
26154             // show editor?
26155             this.inputEl().setHeight(Math.min(500, Math.max(sh,(this.getValue().split("\n").length+1) * 30)));
26156             this.inputEl().removeClass('d-none');
26157             this.inputEl().focus();
26158             this.editing = true;
26159             return;
26160         }
26161         // show showdown...
26162         this.updateMarkdown();
26163         this.markdownEl.removeClass('d-none');
26164         this.editing = false;
26165         return;
26166     },
26167     updateMarkdown : function()
26168     {
26169         if (this.getValue() == '') {
26170             this.markdownEl.dom.innerHTML = String.format('<span class="roo-placeholder">{0}</span>', this.placeholder);
26171             return;
26172         }
26173         this.markdownEl.dom.innerHTML = Roo.Markdown.toHtml(Roo.util.Format.htmlEncode(this.getValue()));
26174     },
26175     
26176     resizeTextArea: function () {
26177         
26178         var sh = 100;
26179         Roo.log([sh, this.getValue().split("\n").length * 30]);
26180         this.inputEl().setHeight(Math.min(500, Math.max(sh, (this.getValue().split("\n").length +1) * 30)));
26181     },
26182     setValue : function(val)
26183     {
26184         Roo.bootstrap.TextArea.prototype.setValue.call(this,val);
26185         if (!this.editing) {
26186             this.updateMarkdown();
26187         }
26188         
26189     },
26190     focus : function()
26191     {
26192         if (!this.editing) {
26193             this.toggleTextEdit();
26194         }
26195         
26196     }
26197
26198
26199 });
26200 /**
26201  * @class Roo.bootstrap.Table.AbstractSelectionModel
26202  * @extends Roo.util.Observable
26203  * Abstract base class for grid SelectionModels.  It provides the interface that should be
26204  * implemented by descendant classes.  This class should not be directly instantiated.
26205  * @constructor
26206  */
26207 Roo.bootstrap.Table.AbstractSelectionModel = function(){
26208     this.locked = false;
26209     Roo.bootstrap.Table.AbstractSelectionModel.superclass.constructor.call(this);
26210 };
26211
26212
26213 Roo.extend(Roo.bootstrap.Table.AbstractSelectionModel, Roo.util.Observable,  {
26214     /** @ignore Called by the grid automatically. Do not call directly. */
26215     init : function(grid){
26216         this.grid = grid;
26217         this.initEvents();
26218     },
26219
26220     /**
26221      * Locks the selections.
26222      */
26223     lock : function(){
26224         this.locked = true;
26225     },
26226
26227     /**
26228      * Unlocks the selections.
26229      */
26230     unlock : function(){
26231         this.locked = false;
26232     },
26233
26234     /**
26235      * Returns true if the selections are locked.
26236      * @return {Boolean}
26237      */
26238     isLocked : function(){
26239         return this.locked;
26240     },
26241     
26242     
26243     initEvents : function ()
26244     {
26245         
26246     }
26247 });
26248 /**
26249  * @extends Roo.bootstrap.Table.AbstractSelectionModel
26250  * @class Roo.bootstrap.Table.RowSelectionModel
26251  * The default SelectionModel used by {@link Roo.bootstrap.Table}.
26252  * It supports multiple selections and keyboard selection/navigation. 
26253  * @constructor
26254  * @param {Object} config
26255  */
26256
26257 Roo.bootstrap.Table.RowSelectionModel = function(config){
26258     Roo.apply(this, config);
26259     this.selections = new Roo.util.MixedCollection(false, function(o){
26260         return o.id;
26261     });
26262
26263     this.last = false;
26264     this.lastActive = false;
26265
26266     this.addEvents({
26267         /**
26268              * @event selectionchange
26269              * Fires when the selection changes
26270              * @param {SelectionModel} this
26271              */
26272             "selectionchange" : true,
26273         /**
26274              * @event afterselectionchange
26275              * Fires after the selection changes (eg. by key press or clicking)
26276              * @param {SelectionModel} this
26277              */
26278             "afterselectionchange" : true,
26279         /**
26280              * @event beforerowselect
26281              * Fires when a row is selected being selected, return false to cancel.
26282              * @param {SelectionModel} this
26283              * @param {Number} rowIndex The selected index
26284              * @param {Boolean} keepExisting False if other selections will be cleared
26285              */
26286             "beforerowselect" : true,
26287         /**
26288              * @event rowselect
26289              * Fires when a row is selected.
26290              * @param {SelectionModel} this
26291              * @param {Number} rowIndex The selected index
26292              * @param {Roo.data.Record} r The record
26293              */
26294             "rowselect" : true,
26295         /**
26296              * @event rowdeselect
26297              * Fires when a row is deselected.
26298              * @param {SelectionModel} this
26299              * @param {Number} rowIndex The selected index
26300              */
26301         "rowdeselect" : true
26302     });
26303     Roo.bootstrap.Table.RowSelectionModel.superclass.constructor.call(this);
26304     this.locked = false;
26305  };
26306
26307 Roo.extend(Roo.bootstrap.Table.RowSelectionModel, Roo.bootstrap.Table.AbstractSelectionModel,  {
26308     /**
26309      * @cfg {Boolean} singleSelect
26310      * True to allow selection of only one row at a time (defaults to false)
26311      */
26312     singleSelect : false,
26313
26314     // private
26315     initEvents : function()
26316     {
26317
26318         //if(!this.grid.enableDragDrop && !this.grid.enableDrag){
26319         //    this.growclickrid.on("mousedown", this.handleMouseDown, this);
26320         //}else{ // allow click to work like normal
26321          //   this.grid.on("rowclick", this.handleDragableRowClick, this);
26322         //}
26323         //this.grid.on("rowdblclick", this.handleMouseDBClick, this);
26324         this.grid.on("rowclick", this.handleMouseDown, this);
26325         
26326         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
26327             "up" : function(e){
26328                 if(!e.shiftKey){
26329                     this.selectPrevious(e.shiftKey);
26330                 }else if(this.last !== false && this.lastActive !== false){
26331                     var last = this.last;
26332                     this.selectRange(this.last,  this.lastActive-1);
26333                     this.grid.getView().focusRow(this.lastActive);
26334                     if(last !== false){
26335                         this.last = last;
26336                     }
26337                 }else{
26338                     this.selectFirstRow();
26339                 }
26340                 this.fireEvent("afterselectionchange", this);
26341             },
26342             "down" : function(e){
26343                 if(!e.shiftKey){
26344                     this.selectNext(e.shiftKey);
26345                 }else if(this.last !== false && this.lastActive !== false){
26346                     var last = this.last;
26347                     this.selectRange(this.last,  this.lastActive+1);
26348                     this.grid.getView().focusRow(this.lastActive);
26349                     if(last !== false){
26350                         this.last = last;
26351                     }
26352                 }else{
26353                     this.selectFirstRow();
26354                 }
26355                 this.fireEvent("afterselectionchange", this);
26356             },
26357             scope: this
26358         });
26359         this.grid.store.on('load', function(){
26360             this.selections.clear();
26361         },this);
26362         /*
26363         var view = this.grid.view;
26364         view.on("refresh", this.onRefresh, this);
26365         view.on("rowupdated", this.onRowUpdated, this);
26366         view.on("rowremoved", this.onRemove, this);
26367         */
26368     },
26369
26370     // private
26371     onRefresh : function()
26372     {
26373         var ds = this.grid.store, i, v = this.grid.view;
26374         var s = this.selections;
26375         s.each(function(r){
26376             if((i = ds.indexOfId(r.id)) != -1){
26377                 v.onRowSelect(i);
26378             }else{
26379                 s.remove(r);
26380             }
26381         });
26382     },
26383
26384     // private
26385     onRemove : function(v, index, r){
26386         this.selections.remove(r);
26387     },
26388
26389     // private
26390     onRowUpdated : function(v, index, r){
26391         if(this.isSelected(r)){
26392             v.onRowSelect(index);
26393         }
26394     },
26395
26396     /**
26397      * Select records.
26398      * @param {Array} records The records to select
26399      * @param {Boolean} keepExisting (optional) True to keep existing selections
26400      */
26401     selectRecords : function(records, keepExisting)
26402     {
26403         if(!keepExisting){
26404             this.clearSelections();
26405         }
26406             var ds = this.grid.store;
26407         for(var i = 0, len = records.length; i < len; i++){
26408             this.selectRow(ds.indexOf(records[i]), true);
26409         }
26410     },
26411
26412     /**
26413      * Gets the number of selected rows.
26414      * @return {Number}
26415      */
26416     getCount : function(){
26417         return this.selections.length;
26418     },
26419
26420     /**
26421      * Selects the first row in the grid.
26422      */
26423     selectFirstRow : function(){
26424         this.selectRow(0);
26425     },
26426
26427     /**
26428      * Select the last row.
26429      * @param {Boolean} keepExisting (optional) True to keep existing selections
26430      */
26431     selectLastRow : function(keepExisting){
26432         //this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
26433         this.selectRow(this.grid.store.getCount() - 1, keepExisting);
26434     },
26435
26436     /**
26437      * Selects the row immediately following the last selected row.
26438      * @param {Boolean} keepExisting (optional) True to keep existing selections
26439      */
26440     selectNext : function(keepExisting)
26441     {
26442             if(this.last !== false && (this.last+1) < this.grid.store.getCount()){
26443             this.selectRow(this.last+1, keepExisting);
26444             this.grid.getView().focusRow(this.last);
26445         }
26446     },
26447
26448     /**
26449      * Selects the row that precedes the last selected row.
26450      * @param {Boolean} keepExisting (optional) True to keep existing selections
26451      */
26452     selectPrevious : function(keepExisting){
26453         if(this.last){
26454             this.selectRow(this.last-1, keepExisting);
26455             this.grid.getView().focusRow(this.last);
26456         }
26457     },
26458
26459     /**
26460      * Returns the selected records
26461      * @return {Array} Array of selected records
26462      */
26463     getSelections : function(){
26464         return [].concat(this.selections.items);
26465     },
26466
26467     /**
26468      * Returns the first selected record.
26469      * @return {Record}
26470      */
26471     getSelected : function(){
26472         return this.selections.itemAt(0);
26473     },
26474
26475
26476     /**
26477      * Clears all selections.
26478      */
26479     clearSelections : function(fast)
26480     {
26481         if(this.locked) {
26482             return;
26483         }
26484         if(fast !== true){
26485                 var ds = this.grid.store;
26486             var s = this.selections;
26487             s.each(function(r){
26488                 this.deselectRow(ds.indexOfId(r.id));
26489             }, this);
26490             s.clear();
26491         }else{
26492             this.selections.clear();
26493         }
26494         this.last = false;
26495     },
26496
26497
26498     /**
26499      * Selects all rows.
26500      */
26501     selectAll : function(){
26502         if(this.locked) {
26503             return;
26504         }
26505         this.selections.clear();
26506         for(var i = 0, len = this.grid.store.getCount(); i < len; i++){
26507             this.selectRow(i, true);
26508         }
26509     },
26510
26511     /**
26512      * Returns True if there is a selection.
26513      * @return {Boolean}
26514      */
26515     hasSelection : function(){
26516         return this.selections.length > 0;
26517     },
26518
26519     /**
26520      * Returns True if the specified row is selected.
26521      * @param {Number/Record} record The record or index of the record to check
26522      * @return {Boolean}
26523      */
26524     isSelected : function(index){
26525             var r = typeof index == "number" ? this.grid.store.getAt(index) : index;
26526         return (r && this.selections.key(r.id) ? true : false);
26527     },
26528
26529     /**
26530      * Returns True if the specified record id is selected.
26531      * @param {String} id The id of record to check
26532      * @return {Boolean}
26533      */
26534     isIdSelected : function(id){
26535         return (this.selections.key(id) ? true : false);
26536     },
26537
26538
26539     // private
26540     handleMouseDBClick : function(e, t){
26541         
26542     },
26543     // private
26544     handleMouseDown : function(e, t)
26545     {
26546             var rowIndex = this.grid.headerShow  ? t.dom.rowIndex - 1 : t.dom.rowIndex ; // first row is header???
26547         if(this.isLocked() || rowIndex < 0 ){
26548             return;
26549         };
26550         if(e.shiftKey && this.last !== false){
26551             var last = this.last;
26552             this.selectRange(last, rowIndex, e.ctrlKey);
26553             this.last = last; // reset the last
26554             t.focus();
26555     
26556         }else{
26557             var isSelected = this.isSelected(rowIndex);
26558             //Roo.log("select row:" + rowIndex);
26559             if(isSelected){
26560                 this.deselectRow(rowIndex);
26561             } else {
26562                         this.selectRow(rowIndex, true);
26563             }
26564     
26565             /*
26566                 if(e.button !== 0 && isSelected){
26567                 alert('rowIndex 2: ' + rowIndex);
26568                     view.focusRow(rowIndex);
26569                 }else if(e.ctrlKey && isSelected){
26570                     this.deselectRow(rowIndex);
26571                 }else if(!isSelected){
26572                     this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
26573                     view.focusRow(rowIndex);
26574                 }
26575             */
26576         }
26577         this.fireEvent("afterselectionchange", this);
26578     },
26579     // private
26580     handleDragableRowClick :  function(grid, rowIndex, e) 
26581     {
26582         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
26583             this.selectRow(rowIndex, false);
26584             grid.view.focusRow(rowIndex);
26585              this.fireEvent("afterselectionchange", this);
26586         }
26587     },
26588     
26589     /**
26590      * Selects multiple rows.
26591      * @param {Array} rows Array of the indexes of the row to select
26592      * @param {Boolean} keepExisting (optional) True to keep existing selections
26593      */
26594     selectRows : function(rows, keepExisting){
26595         if(!keepExisting){
26596             this.clearSelections();
26597         }
26598         for(var i = 0, len = rows.length; i < len; i++){
26599             this.selectRow(rows[i], true);
26600         }
26601     },
26602
26603     /**
26604      * Selects a range of rows. All rows in between startRow and endRow are also selected.
26605      * @param {Number} startRow The index of the first row in the range
26606      * @param {Number} endRow The index of the last row in the range
26607      * @param {Boolean} keepExisting (optional) True to retain existing selections
26608      */
26609     selectRange : function(startRow, endRow, keepExisting){
26610         if(this.locked) {
26611             return;
26612         }
26613         if(!keepExisting){
26614             this.clearSelections();
26615         }
26616         if(startRow <= endRow){
26617             for(var i = startRow; i <= endRow; i++){
26618                 this.selectRow(i, true);
26619             }
26620         }else{
26621             for(var i = startRow; i >= endRow; i--){
26622                 this.selectRow(i, true);
26623             }
26624         }
26625     },
26626
26627     /**
26628      * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
26629      * @param {Number} startRow The index of the first row in the range
26630      * @param {Number} endRow The index of the last row in the range
26631      */
26632     deselectRange : function(startRow, endRow, preventViewNotify){
26633         if(this.locked) {
26634             return;
26635         }
26636         for(var i = startRow; i <= endRow; i++){
26637             this.deselectRow(i, preventViewNotify);
26638         }
26639     },
26640
26641     /**
26642      * Selects a row.
26643      * @param {Number} row The index of the row to select
26644      * @param {Boolean} keepExisting (optional) True to keep existing selections
26645      */
26646     selectRow : function(index, keepExisting, preventViewNotify)
26647     {
26648             if(this.locked || (index < 0 || index > this.grid.store.getCount())) {
26649             return;
26650         }
26651         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
26652             if(!keepExisting || this.singleSelect){
26653                 this.clearSelections();
26654             }
26655             
26656             var r = this.grid.store.getAt(index);
26657             //console.log('selectRow - record id :' + r.id);
26658             
26659             this.selections.add(r);
26660             this.last = this.lastActive = index;
26661             if(!preventViewNotify){
26662                 var proxy = new Roo.Element(
26663                                 this.grid.getRowDom(index)
26664                 );
26665                 proxy.addClass('bg-info info');
26666             }
26667             this.fireEvent("rowselect", this, index, r);
26668             this.fireEvent("selectionchange", this);
26669         }
26670     },
26671
26672     /**
26673      * Deselects a row.
26674      * @param {Number} row The index of the row to deselect
26675      */
26676     deselectRow : function(index, preventViewNotify)
26677     {
26678         if(this.locked) {
26679             return;
26680         }
26681         if(this.last == index){
26682             this.last = false;
26683         }
26684         if(this.lastActive == index){
26685             this.lastActive = false;
26686         }
26687         
26688         var r = this.grid.store.getAt(index);
26689         if (!r) {
26690             return;
26691         }
26692         
26693         this.selections.remove(r);
26694         //.console.log('deselectRow - record id :' + r.id);
26695         if(!preventViewNotify){
26696         
26697             var proxy = new Roo.Element(
26698                 this.grid.getRowDom(index)
26699             );
26700             proxy.removeClass('bg-info info');
26701         }
26702         this.fireEvent("rowdeselect", this, index);
26703         this.fireEvent("selectionchange", this);
26704     },
26705
26706     // private
26707     restoreLast : function(){
26708         if(this._last){
26709             this.last = this._last;
26710         }
26711     },
26712
26713     // private
26714     acceptsNav : function(row, col, cm){
26715         return !cm.isHidden(col) && cm.isCellEditable(col, row);
26716     },
26717
26718     // private
26719     onEditorKey : function(field, e){
26720         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
26721         if(k == e.TAB){
26722             e.stopEvent();
26723             ed.completeEdit();
26724             if(e.shiftKey){
26725                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
26726             }else{
26727                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
26728             }
26729         }else if(k == e.ENTER && !e.ctrlKey){
26730             e.stopEvent();
26731             ed.completeEdit();
26732             if(e.shiftKey){
26733                 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
26734             }else{
26735                 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
26736             }
26737         }else if(k == e.ESC){
26738             ed.cancelEdit();
26739         }
26740         if(newCell){
26741             g.startEditing(newCell[0], newCell[1]);
26742         }
26743     }
26744 });
26745 /*
26746  * Based on:
26747  * Ext JS Library 1.1.1
26748  * Copyright(c) 2006-2007, Ext JS, LLC.
26749  *
26750  * Originally Released Under LGPL - original licence link has changed is not relivant.
26751  *
26752  * Fork - LGPL
26753  * <script type="text/javascript">
26754  */
26755  
26756 /**
26757  * @class Roo.bootstrap.PagingToolbar
26758  * @extends Roo.bootstrap.NavSimplebar
26759  * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
26760  * @constructor
26761  * Create a new PagingToolbar
26762  * @param {Object} config The config object
26763  * @param {Roo.data.Store} store
26764  */
26765 Roo.bootstrap.PagingToolbar = function(config)
26766 {
26767     // old args format still supported... - xtype is prefered..
26768         // created from xtype...
26769     
26770     this.ds = config.dataSource;
26771     
26772     if (config.store && !this.ds) {
26773         this.store= Roo.factory(config.store, Roo.data);
26774         this.ds = this.store;
26775         this.ds.xmodule = this.xmodule || false;
26776     }
26777     
26778     this.toolbarItems = [];
26779     if (config.items) {
26780         this.toolbarItems = config.items;
26781     }
26782     
26783     Roo.bootstrap.PagingToolbar.superclass.constructor.call(this, config);
26784     
26785     this.cursor = 0;
26786     
26787     if (this.ds) { 
26788         this.bind(this.ds);
26789     }
26790     
26791     if (Roo.bootstrap.version == 4) {
26792         this.navgroup = new Roo.bootstrap.ButtonGroup({ cls: 'pagination' });
26793     } else {
26794         this.navgroup = new Roo.bootstrap.NavGroup({ cls: 'pagination' });
26795     }
26796     
26797 };
26798
26799 Roo.extend(Roo.bootstrap.PagingToolbar, Roo.bootstrap.NavSimplebar, {
26800     /**
26801      * @cfg {Roo.data.Store} dataSource
26802      * The underlying data store providing the paged data
26803      */
26804     /**
26805      * @cfg {String/HTMLElement/Element} container
26806      * container The id or element that will contain the toolbar
26807      */
26808     /**
26809      * @cfg {Boolean} displayInfo
26810      * True to display the displayMsg (defaults to false)
26811      */
26812     /**
26813      * @cfg {Number} pageSize
26814      * The number of records to display per page (defaults to 20)
26815      */
26816     pageSize: 20,
26817     /**
26818      * @cfg {String} displayMsg
26819      * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
26820      */
26821     displayMsg : 'Displaying {0} - {1} of {2}',
26822     /**
26823      * @cfg {String} emptyMsg
26824      * The message to display when no records are found (defaults to "No data to display")
26825      */
26826     emptyMsg : 'No data to display',
26827     /**
26828      * Customizable piece of the default paging text (defaults to "Page")
26829      * @type String
26830      */
26831     beforePageText : "Page",
26832     /**
26833      * Customizable piece of the default paging text (defaults to "of %0")
26834      * @type String
26835      */
26836     afterPageText : "of {0}",
26837     /**
26838      * Customizable piece of the default paging text (defaults to "First Page")
26839      * @type String
26840      */
26841     firstText : "First Page",
26842     /**
26843      * Customizable piece of the default paging text (defaults to "Previous Page")
26844      * @type String
26845      */
26846     prevText : "Previous Page",
26847     /**
26848      * Customizable piece of the default paging text (defaults to "Next Page")
26849      * @type String
26850      */
26851     nextText : "Next Page",
26852     /**
26853      * Customizable piece of the default paging text (defaults to "Last Page")
26854      * @type String
26855      */
26856     lastText : "Last Page",
26857     /**
26858      * Customizable piece of the default paging text (defaults to "Refresh")
26859      * @type String
26860      */
26861     refreshText : "Refresh",
26862
26863     buttons : false,
26864     // private
26865     onRender : function(ct, position) 
26866     {
26867         Roo.bootstrap.PagingToolbar.superclass.onRender.call(this, ct, position);
26868         this.navgroup.parentId = this.id;
26869         this.navgroup.onRender(this.el, null);
26870         // add the buttons to the navgroup
26871         
26872         if(this.displayInfo){
26873             this.el.select('ul.navbar-nav',true).first().createChild({cls:'x-paging-info'});
26874             this.displayEl = this.el.select('.x-paging-info', true).first();
26875 //            var navel = this.navgroup.addItem( { tagtype : 'span', html : '', cls : 'x-paging-info', preventDefault : true } );
26876 //            this.displayEl = navel.el.select('span',true).first();
26877         }
26878         
26879         var _this = this;
26880         
26881         if(this.buttons){
26882             Roo.each(_this.buttons, function(e){ // this might need to use render????
26883                Roo.factory(e).render(_this.el);
26884             });
26885         }
26886             
26887         Roo.each(_this.toolbarItems, function(e) {
26888             _this.navgroup.addItem(e);
26889         });
26890         
26891         
26892         this.first = this.navgroup.addItem({
26893             tooltip: this.firstText,
26894             cls: "prev btn-outline-secondary",
26895             html : ' <i class="fa fa-step-backward"></i>',
26896             disabled: true,
26897             preventDefault: true,
26898             listeners : { click : this.onClick.createDelegate(this, ["first"]) }
26899         });
26900         
26901         this.prev =  this.navgroup.addItem({
26902             tooltip: this.prevText,
26903             cls: "prev btn-outline-secondary",
26904             html : ' <i class="fa fa-backward"></i>',
26905             disabled: true,
26906             preventDefault: true,
26907             listeners : { click :  this.onClick.createDelegate(this, ["prev"]) }
26908         });
26909     //this.addSeparator();
26910         
26911         
26912         var field = this.navgroup.addItem( {
26913             tagtype : 'span',
26914             cls : 'x-paging-position  btn-outline-secondary',
26915              disabled: true,
26916             html : this.beforePageText  +
26917                 '<input type="text" size="3" value="1" class="x-grid-page-number">' +
26918                 '<span class="x-paging-after">' +  String.format(this.afterPageText, 1) + '</span>'
26919          } ); //?? escaped?
26920         
26921         this.field = field.el.select('input', true).first();
26922         this.field.on("keydown", this.onPagingKeydown, this);
26923         this.field.on("focus", function(){this.dom.select();});
26924     
26925     
26926         this.afterTextEl =  field.el.select('.x-paging-after',true).first();
26927         //this.field.setHeight(18);
26928         //this.addSeparator();
26929         this.next = this.navgroup.addItem({
26930             tooltip: this.nextText,
26931             cls: "next btn-outline-secondary",
26932             html : ' <i class="fa fa-forward"></i>',
26933             disabled: true,
26934             preventDefault: true,
26935             listeners : { click :  this.onClick.createDelegate(this, ["next"]) }
26936         });
26937         this.last = this.navgroup.addItem({
26938             tooltip: this.lastText,
26939             html : ' <i class="fa fa-step-forward"></i>',
26940             cls: "next btn-outline-secondary",
26941             disabled: true,
26942             preventDefault: true,
26943             listeners : { click :  this.onClick.createDelegate(this, ["last"]) }
26944         });
26945     //this.addSeparator();
26946         this.loading = this.navgroup.addItem({
26947             tooltip: this.refreshText,
26948             cls: "btn-outline-secondary",
26949             html : ' <i class="fa fa-refresh"></i>',
26950             preventDefault: true,
26951             listeners : { click : this.onClick.createDelegate(this, ["refresh"]) }
26952         });
26953         
26954     },
26955
26956     // private
26957     updateInfo : function(){
26958         if(this.displayEl){
26959             var count = (typeof(this.getCount) == 'undefined') ? this.ds.getCount() : this.getCount();
26960             var msg = count == 0 ?
26961                 this.emptyMsg :
26962                 String.format(
26963                     this.displayMsg,
26964                     this.cursor+1, this.cursor+count, this.ds.getTotalCount()    
26965                 );
26966             this.displayEl.update(msg);
26967         }
26968     },
26969
26970     // private
26971     onLoad : function(ds, r, o)
26972     {
26973         this.cursor = o.params.start ? o.params.start : 0;
26974         
26975         var d = this.getPageData(),
26976             ap = d.activePage,
26977             ps = d.pages;
26978         
26979         
26980         this.afterTextEl.dom.innerHTML = String.format(this.afterPageText, d.pages);
26981         this.field.dom.value = ap;
26982         this.first.setDisabled(ap == 1);
26983         this.prev.setDisabled(ap == 1);
26984         this.next.setDisabled(ap == ps);
26985         this.last.setDisabled(ap == ps);
26986         this.loading.enable();
26987         this.updateInfo();
26988     },
26989
26990     // private
26991     getPageData : function(){
26992         var total = this.ds.getTotalCount();
26993         return {
26994             total : total,
26995             activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
26996             pages :  total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
26997         };
26998     },
26999
27000     // private
27001     onLoadError : function(){
27002         this.loading.enable();
27003     },
27004
27005     // private
27006     onPagingKeydown : function(e){
27007         var k = e.getKey();
27008         var d = this.getPageData();
27009         if(k == e.RETURN){
27010             var v = this.field.dom.value, pageNum;
27011             if(!v || isNaN(pageNum = parseInt(v, 10))){
27012                 this.field.dom.value = d.activePage;
27013                 return;
27014             }
27015             pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
27016             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
27017             e.stopEvent();
27018         }
27019         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))
27020         {
27021           var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
27022           this.field.dom.value = pageNum;
27023           this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
27024           e.stopEvent();
27025         }
27026         else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
27027         {
27028           var v = this.field.dom.value, pageNum; 
27029           var increment = (e.shiftKey) ? 10 : 1;
27030           if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN) {
27031                 increment *= -1;
27032           }
27033           if(!v || isNaN(pageNum = parseInt(v, 10))) {
27034             this.field.dom.value = d.activePage;
27035             return;
27036           }
27037           else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
27038           {
27039             this.field.dom.value = parseInt(v, 10) + increment;
27040             pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
27041             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
27042           }
27043           e.stopEvent();
27044         }
27045     },
27046
27047     // private
27048     beforeLoad : function(){
27049         if(this.loading){
27050             this.loading.disable();
27051         }
27052     },
27053
27054     // private
27055     onClick : function(which){
27056         
27057         var ds = this.ds;
27058         if (!ds) {
27059             return;
27060         }
27061         
27062         switch(which){
27063             case "first":
27064                 ds.load({params:{start: 0, limit: this.pageSize}});
27065             break;
27066             case "prev":
27067                 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
27068             break;
27069             case "next":
27070                 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
27071             break;
27072             case "last":
27073                 var total = ds.getTotalCount();
27074                 var extra = total % this.pageSize;
27075                 var lastStart = extra ? (total - extra) : total-this.pageSize;
27076                 ds.load({params:{start: lastStart, limit: this.pageSize}});
27077             break;
27078             case "refresh":
27079                 ds.load({params:{start: this.cursor, limit: this.pageSize}});
27080             break;
27081         }
27082     },
27083
27084     /**
27085      * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
27086      * @param {Roo.data.Store} store The data store to unbind
27087      */
27088     unbind : function(ds){
27089         ds.un("beforeload", this.beforeLoad, this);
27090         ds.un("load", this.onLoad, this);
27091         ds.un("loadexception", this.onLoadError, this);
27092         ds.un("remove", this.updateInfo, this);
27093         ds.un("add", this.updateInfo, this);
27094         this.ds = undefined;
27095     },
27096
27097     /**
27098      * Binds the paging toolbar to the specified {@link Roo.data.Store}
27099      * @param {Roo.data.Store} store The data store to bind
27100      */
27101     bind : function(ds){
27102         ds.on("beforeload", this.beforeLoad, this);
27103         ds.on("load", this.onLoad, this);
27104         ds.on("loadexception", this.onLoadError, this);
27105         ds.on("remove", this.updateInfo, this);
27106         ds.on("add", this.updateInfo, this);
27107         this.ds = ds;
27108     }
27109 });/*
27110  * - LGPL
27111  *
27112  * element
27113  * 
27114  */
27115
27116 /**
27117  * @class Roo.bootstrap.MessageBar
27118  * @extends Roo.bootstrap.Component
27119  * Bootstrap MessageBar class
27120  * @cfg {String} html contents of the MessageBar
27121  * @cfg {String} weight (info | success | warning | danger) default info
27122  * @cfg {String} beforeClass insert the bar before the given class
27123  * @cfg {Boolean} closable (true | false) default false
27124  * @cfg {Boolean} fixed (true | false) default false, fix the bar at the top
27125  * 
27126  * @constructor
27127  * Create a new Element
27128  * @param {Object} config The config object
27129  */
27130
27131 Roo.bootstrap.MessageBar = function(config){
27132     Roo.bootstrap.MessageBar.superclass.constructor.call(this, config);
27133 };
27134
27135 Roo.extend(Roo.bootstrap.MessageBar, Roo.bootstrap.Component,  {
27136     
27137     html: '',
27138     weight: 'info',
27139     closable: false,
27140     fixed: false,
27141     beforeClass: 'bootstrap-sticky-wrap',
27142     
27143     getAutoCreate : function(){
27144         
27145         var cfg = {
27146             tag: 'div',
27147             cls: 'alert alert-dismissable alert-' + this.weight,
27148             cn: [
27149                 {
27150                     tag: 'span',
27151                     cls: 'message',
27152                     html: this.html || ''
27153                 }
27154             ]
27155         };
27156         
27157         if(this.fixed){
27158             cfg.cls += ' alert-messages-fixed';
27159         }
27160         
27161         if(this.closable){
27162             cfg.cn.push({
27163                 tag: 'button',
27164                 cls: 'close',
27165                 html: 'x'
27166             });
27167         }
27168         
27169         return cfg;
27170     },
27171     
27172     onRender : function(ct, position)
27173     {
27174         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
27175         
27176         if(!this.el){
27177             var cfg = Roo.apply({},  this.getAutoCreate());
27178             cfg.id = Roo.id();
27179             
27180             if (this.cls) {
27181                 cfg.cls += ' ' + this.cls;
27182             }
27183             if (this.style) {
27184                 cfg.style = this.style;
27185             }
27186             this.el = Roo.get(document.body).createChild(cfg, Roo.select('.'+this.beforeClass, true).first());
27187             
27188             this.el.setVisibilityMode(Roo.Element.DISPLAY);
27189         }
27190         
27191         this.el.select('>button.close').on('click', this.hide, this);
27192         
27193     },
27194     
27195     show : function()
27196     {
27197         if (!this.rendered) {
27198             this.render();
27199         }
27200         
27201         this.el.show();
27202         
27203         this.fireEvent('show', this);
27204         
27205     },
27206     
27207     hide : function()
27208     {
27209         if (!this.rendered) {
27210             this.render();
27211         }
27212         
27213         this.el.hide();
27214         
27215         this.fireEvent('hide', this);
27216     },
27217     
27218     update : function()
27219     {
27220 //        var e = this.el.dom.firstChild;
27221 //        
27222 //        if(this.closable){
27223 //            e = e.nextSibling;
27224 //        }
27225 //        
27226 //        e.data = this.html || '';
27227
27228         this.el.select('>.message', true).first().dom.innerHTML = this.html || '';
27229     }
27230    
27231 });
27232
27233  
27234
27235      /*
27236  * - LGPL
27237  *
27238  * Graph
27239  * 
27240  */
27241
27242
27243 /**
27244  * @class Roo.bootstrap.Graph
27245  * @extends Roo.bootstrap.Component
27246  * Bootstrap Graph class
27247 > Prameters
27248  -sm {number} sm 4
27249  -md {number} md 5
27250  @cfg {String} graphtype  bar | vbar | pie
27251  @cfg {number} g_x coodinator | centre x (pie)
27252  @cfg {number} g_y coodinator | centre y (pie)
27253  @cfg {number} g_r radius (pie)
27254  @cfg {number} g_height height of the chart (respected by all elements in the set)
27255  @cfg {number} g_width width of the chart (respected by all elements in the set)
27256  @cfg {Object} title The title of the chart
27257     
27258  -{Array}  values
27259  -opts (object) options for the chart 
27260      o {
27261      o type (string) type of endings of the bar. Default: 'square'. Other options are: 'round', 'sharp', 'soft'.
27262      o gutter (number)(string) default '20%' (WHAT DOES IT DO?)
27263      o vgutter (number)
27264      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.
27265      o stacked (boolean) whether or not to tread values as in a stacked bar chart
27266      o to
27267      o stretch (boolean)
27268      o }
27269  -opts (object) options for the pie
27270      o{
27271      o cut
27272      o startAngle (number)
27273      o endAngle (number)
27274      } 
27275  *
27276  * @constructor
27277  * Create a new Input
27278  * @param {Object} config The config object
27279  */
27280
27281 Roo.bootstrap.Graph = function(config){
27282     Roo.bootstrap.Graph.superclass.constructor.call(this, config);
27283     
27284     this.addEvents({
27285         // img events
27286         /**
27287          * @event click
27288          * The img click event for the img.
27289          * @param {Roo.EventObject} e
27290          */
27291         "click" : true
27292     });
27293 };
27294
27295 Roo.extend(Roo.bootstrap.Graph, Roo.bootstrap.Component,  {
27296     
27297     sm: 4,
27298     md: 5,
27299     graphtype: 'bar',
27300     g_height: 250,
27301     g_width: 400,
27302     g_x: 50,
27303     g_y: 50,
27304     g_r: 30,
27305     opts:{
27306         //g_colors: this.colors,
27307         g_type: 'soft',
27308         g_gutter: '20%'
27309
27310     },
27311     title : false,
27312
27313     getAutoCreate : function(){
27314         
27315         var cfg = {
27316             tag: 'div',
27317             html : null
27318         };
27319         
27320         
27321         return  cfg;
27322     },
27323
27324     onRender : function(ct,position){
27325         
27326         
27327         Roo.bootstrap.Graph.superclass.onRender.call(this,ct,position);
27328         
27329         if (typeof(Raphael) == 'undefined') {
27330             Roo.bootstrap.MessageBox.alert("Error","Raphael is not availabe");
27331             return;
27332         }
27333         
27334         this.raphael = Raphael(this.el.dom);
27335         
27336                     // data1 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
27337                     // data2 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
27338                     // data3 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
27339                     // txtattr = { font: "12px 'Fontin Sans', Fontin-Sans, sans-serif" };
27340                 /*
27341                 r.text(160, 10, "Single Series Chart").attr(txtattr);
27342                 r.text(480, 10, "Multiline Series Chart").attr(txtattr);
27343                 r.text(160, 250, "Multiple Series Stacked Chart").attr(txtattr);
27344                 r.text(480, 250, 'Multiline Series Stacked Vertical Chart. Type "round"').attr(txtattr);
27345                 
27346                 r.barchart(10, 10, 300, 220, [[55, 20, 13, 32, 5, 1, 2, 10]], 0, {type: "sharp"});
27347                 r.barchart(330, 10, 300, 220, data1);
27348                 r.barchart(10, 250, 300, 220, data2, {stacked: true});
27349                 r.barchart(330, 250, 300, 220, data3, {stacked: true, type: "round"});
27350                 */
27351                 
27352                 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
27353                 // r.barchart(30, 30, 560, 250,  xdata, {
27354                 //    labels : [55, 20, 13, 32, 5, 1, 2, 10,5 , 10],
27355                 //     axis : "0 0 1 1",
27356                 //     axisxlabels :  xdata
27357                 //     //yvalues : cols,
27358                    
27359                 // });
27360 //        var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
27361 //        
27362 //        this.load(null,xdata,{
27363 //                axis : "0 0 1 1",
27364 //                axisxlabels :  xdata
27365 //                });
27366
27367     },
27368
27369     load : function(graphtype,xdata,opts)
27370     {
27371         this.raphael.clear();
27372         if(!graphtype) {
27373             graphtype = this.graphtype;
27374         }
27375         if(!opts){
27376             opts = this.opts;
27377         }
27378         var r = this.raphael,
27379             fin = function () {
27380                 this.flag = r.popup(this.bar.x, this.bar.y, this.bar.value || "0").insertBefore(this);
27381             },
27382             fout = function () {
27383                 this.flag.animate({opacity: 0}, 300, function () {this.remove();});
27384             },
27385             pfin = function() {
27386                 this.sector.stop();
27387                 this.sector.scale(1.1, 1.1, this.cx, this.cy);
27388
27389                 if (this.label) {
27390                     this.label[0].stop();
27391                     this.label[0].attr({ r: 7.5 });
27392                     this.label[1].attr({ "font-weight": 800 });
27393                 }
27394             },
27395             pfout = function() {
27396                 this.sector.animate({ transform: 's1 1 ' + this.cx + ' ' + this.cy }, 500, "bounce");
27397
27398                 if (this.label) {
27399                     this.label[0].animate({ r: 5 }, 500, "bounce");
27400                     this.label[1].attr({ "font-weight": 400 });
27401                 }
27402             };
27403
27404         switch(graphtype){
27405             case 'bar':
27406                 this.raphael.barchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
27407                 break;
27408             case 'hbar':
27409                 this.raphael.hbarchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
27410                 break;
27411             case 'pie':
27412 //                opts = { legend: ["%% - Enterprise Users", "% - ddd","Chrome Users"], legendpos: "west", 
27413 //                href: ["http://raphaeljs.com", "http://g.raphaeljs.com"]};
27414 //            
27415                 this.raphael.piechart(this.g_x,this.g_y,this.g_r,xdata,opts).hover(pfin, pfout);
27416                 
27417                 break;
27418
27419         }
27420         
27421         if(this.title){
27422             this.raphael.text(this.title.x, this.title.y, this.title.text).attr(this.title.attr);
27423         }
27424         
27425     },
27426     
27427     setTitle: function(o)
27428     {
27429         this.title = o;
27430     },
27431     
27432     initEvents: function() {
27433         
27434         if(!this.href){
27435             this.el.on('click', this.onClick, this);
27436         }
27437     },
27438     
27439     onClick : function(e)
27440     {
27441         Roo.log('img onclick');
27442         this.fireEvent('click', this, e);
27443     }
27444    
27445 });
27446
27447  
27448 /*
27449  * - LGPL
27450  *
27451  * numberBox
27452  * 
27453  */
27454 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
27455
27456 /**
27457  * @class Roo.bootstrap.dash.NumberBox
27458  * @extends Roo.bootstrap.Component
27459  * Bootstrap NumberBox class
27460  * @cfg {String} headline Box headline
27461  * @cfg {String} content Box content
27462  * @cfg {String} icon Box icon
27463  * @cfg {String} footer Footer text
27464  * @cfg {String} fhref Footer href
27465  * 
27466  * @constructor
27467  * Create a new NumberBox
27468  * @param {Object} config The config object
27469  */
27470
27471
27472 Roo.bootstrap.dash.NumberBox = function(config){
27473     Roo.bootstrap.dash.NumberBox.superclass.constructor.call(this, config);
27474     
27475 };
27476
27477 Roo.extend(Roo.bootstrap.dash.NumberBox, Roo.bootstrap.Component,  {
27478     
27479     headline : '',
27480     content : '',
27481     icon : '',
27482     footer : '',
27483     fhref : '',
27484     ficon : '',
27485     
27486     getAutoCreate : function(){
27487         
27488         var cfg = {
27489             tag : 'div',
27490             cls : 'small-box ',
27491             cn : [
27492                 {
27493                     tag : 'div',
27494                     cls : 'inner',
27495                     cn :[
27496                         {
27497                             tag : 'h3',
27498                             cls : 'roo-headline',
27499                             html : this.headline
27500                         },
27501                         {
27502                             tag : 'p',
27503                             cls : 'roo-content',
27504                             html : this.content
27505                         }
27506                     ]
27507                 }
27508             ]
27509         };
27510         
27511         if(this.icon){
27512             cfg.cn.push({
27513                 tag : 'div',
27514                 cls : 'icon',
27515                 cn :[
27516                     {
27517                         tag : 'i',
27518                         cls : 'ion ' + this.icon
27519                     }
27520                 ]
27521             });
27522         }
27523         
27524         if(this.footer){
27525             var footer = {
27526                 tag : 'a',
27527                 cls : 'small-box-footer',
27528                 href : this.fhref || '#',
27529                 html : this.footer
27530             };
27531             
27532             cfg.cn.push(footer);
27533             
27534         }
27535         
27536         return  cfg;
27537     },
27538
27539     onRender : function(ct,position){
27540         Roo.bootstrap.dash.NumberBox.superclass.onRender.call(this,ct,position);
27541
27542
27543        
27544                 
27545     },
27546
27547     setHeadline: function (value)
27548     {
27549         this.el.select('.roo-headline',true).first().dom.innerHTML = value;
27550     },
27551     
27552     setFooter: function (value, href)
27553     {
27554         this.el.select('a.small-box-footer',true).first().dom.innerHTML = value;
27555         
27556         if(href){
27557             this.el.select('a.small-box-footer',true).first().attr('href', href);
27558         }
27559         
27560     },
27561
27562     setContent: function (value)
27563     {
27564         this.el.select('.roo-content',true).first().dom.innerHTML = value;
27565     },
27566
27567     initEvents: function() 
27568     {   
27569         
27570     }
27571     
27572 });
27573
27574  
27575 /*
27576  * - LGPL
27577  *
27578  * TabBox
27579  * 
27580  */
27581 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
27582
27583 /**
27584  * @class Roo.bootstrap.dash.TabBox
27585  * @extends Roo.bootstrap.Component
27586  * Bootstrap TabBox class
27587  * @cfg {String} title Title of the TabBox
27588  * @cfg {String} icon Icon of the TabBox
27589  * @cfg {Boolean} showtabs (true|false) show the tabs default true
27590  * @cfg {Boolean} tabScrollable (true|false) tab scrollable when mobile view default false
27591  * 
27592  * @constructor
27593  * Create a new TabBox
27594  * @param {Object} config The config object
27595  */
27596
27597
27598 Roo.bootstrap.dash.TabBox = function(config){
27599     Roo.bootstrap.dash.TabBox.superclass.constructor.call(this, config);
27600     this.addEvents({
27601         // raw events
27602         /**
27603          * @event addpane
27604          * When a pane is added
27605          * @param {Roo.bootstrap.dash.TabPane} pane
27606          */
27607         "addpane" : true,
27608         /**
27609          * @event activatepane
27610          * When a pane is activated
27611          * @param {Roo.bootstrap.dash.TabPane} pane
27612          */
27613         "activatepane" : true
27614         
27615          
27616     });
27617     
27618     this.panes = [];
27619 };
27620
27621 Roo.extend(Roo.bootstrap.dash.TabBox, Roo.bootstrap.Component,  {
27622
27623     title : '',
27624     icon : false,
27625     showtabs : true,
27626     tabScrollable : false,
27627     
27628     getChildContainer : function()
27629     {
27630         return this.el.select('.tab-content', true).first();
27631     },
27632     
27633     getAutoCreate : function(){
27634         
27635         var header = {
27636             tag: 'li',
27637             cls: 'pull-left header',
27638             html: this.title,
27639             cn : []
27640         };
27641         
27642         if(this.icon){
27643             header.cn.push({
27644                 tag: 'i',
27645                 cls: 'fa ' + this.icon
27646             });
27647         }
27648         
27649         var h = {
27650             tag: 'ul',
27651             cls: 'nav nav-tabs pull-right',
27652             cn: [
27653                 header
27654             ]
27655         };
27656         
27657         if(this.tabScrollable){
27658             h = {
27659                 tag: 'div',
27660                 cls: 'tab-header',
27661                 cn: [
27662                     {
27663                         tag: 'ul',
27664                         cls: 'nav nav-tabs pull-right',
27665                         cn: [
27666                             header
27667                         ]
27668                     }
27669                 ]
27670             };
27671         }
27672         
27673         var cfg = {
27674             tag: 'div',
27675             cls: 'nav-tabs-custom',
27676             cn: [
27677                 h,
27678                 {
27679                     tag: 'div',
27680                     cls: 'tab-content no-padding',
27681                     cn: []
27682                 }
27683             ]
27684         };
27685
27686         return  cfg;
27687     },
27688     initEvents : function()
27689     {
27690         //Roo.log('add add pane handler');
27691         this.on('addpane', this.onAddPane, this);
27692     },
27693      /**
27694      * Updates the box title
27695      * @param {String} html to set the title to.
27696      */
27697     setTitle : function(value)
27698     {
27699         this.el.select('.nav-tabs .header', true).first().dom.innerHTML = value;
27700     },
27701     onAddPane : function(pane)
27702     {
27703         this.panes.push(pane);
27704         //Roo.log('addpane');
27705         //Roo.log(pane);
27706         // tabs are rendere left to right..
27707         if(!this.showtabs){
27708             return;
27709         }
27710         
27711         var ctr = this.el.select('.nav-tabs', true).first();
27712          
27713          
27714         var existing = ctr.select('.nav-tab',true);
27715         var qty = existing.getCount();;
27716         
27717         
27718         var tab = ctr.createChild({
27719             tag : 'li',
27720             cls : 'nav-tab' + (qty ? '' : ' active'),
27721             cn : [
27722                 {
27723                     tag : 'a',
27724                     href:'#',
27725                     html : pane.title
27726                 }
27727             ]
27728         }, qty ? existing.first().dom : ctr.select('.header', true).first().dom );
27729         pane.tab = tab;
27730         
27731         tab.on('click', this.onTabClick.createDelegate(this, [pane], true));
27732         if (!qty) {
27733             pane.el.addClass('active');
27734         }
27735         
27736                 
27737     },
27738     onTabClick : function(ev,un,ob,pane)
27739     {
27740         //Roo.log('tab - prev default');
27741         ev.preventDefault();
27742         
27743         
27744         this.el.select('.nav-tabs li.nav-tab', true).removeClass('active');
27745         pane.tab.addClass('active');
27746         //Roo.log(pane.title);
27747         this.getChildContainer().select('.tab-pane',true).removeClass('active');
27748         // technically we should have a deactivate event.. but maybe add later.
27749         // and it should not de-activate the selected tab...
27750         this.fireEvent('activatepane', pane);
27751         pane.el.addClass('active');
27752         pane.fireEvent('activate');
27753         
27754         
27755     },
27756     
27757     getActivePane : function()
27758     {
27759         var r = false;
27760         Roo.each(this.panes, function(p) {
27761             if(p.el.hasClass('active')){
27762                 r = p;
27763                 return false;
27764             }
27765             
27766             return;
27767         });
27768         
27769         return r;
27770     }
27771     
27772     
27773 });
27774
27775  
27776 /*
27777  * - LGPL
27778  *
27779  * Tab pane
27780  * 
27781  */
27782 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
27783 /**
27784  * @class Roo.bootstrap.TabPane
27785  * @extends Roo.bootstrap.Component
27786  * Bootstrap TabPane class
27787  * @cfg {Boolean} active (false | true) Default false
27788  * @cfg {String} title title of panel
27789
27790  * 
27791  * @constructor
27792  * Create a new TabPane
27793  * @param {Object} config The config object
27794  */
27795
27796 Roo.bootstrap.dash.TabPane = function(config){
27797     Roo.bootstrap.dash.TabPane.superclass.constructor.call(this, config);
27798     
27799     this.addEvents({
27800         // raw events
27801         /**
27802          * @event activate
27803          * When a pane is activated
27804          * @param {Roo.bootstrap.dash.TabPane} pane
27805          */
27806         "activate" : true
27807          
27808     });
27809 };
27810
27811 Roo.extend(Roo.bootstrap.dash.TabPane, Roo.bootstrap.Component,  {
27812     
27813     active : false,
27814     title : '',
27815     
27816     // the tabBox that this is attached to.
27817     tab : false,
27818      
27819     getAutoCreate : function() 
27820     {
27821         var cfg = {
27822             tag: 'div',
27823             cls: 'tab-pane'
27824         };
27825         
27826         if(this.active){
27827             cfg.cls += ' active';
27828         }
27829         
27830         return cfg;
27831     },
27832     initEvents  : function()
27833     {
27834         //Roo.log('trigger add pane handler');
27835         this.parent().fireEvent('addpane', this)
27836     },
27837     
27838      /**
27839      * Updates the tab title 
27840      * @param {String} html to set the title to.
27841      */
27842     setTitle: function(str)
27843     {
27844         if (!this.tab) {
27845             return;
27846         }
27847         this.title = str;
27848         this.tab.select('a', true).first().dom.innerHTML = str;
27849         
27850     }
27851     
27852     
27853     
27854 });
27855
27856  
27857
27858
27859  /*
27860  * - LGPL
27861  *
27862  * menu
27863  * 
27864  */
27865 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
27866
27867 /**
27868  * @class Roo.bootstrap.menu.Menu
27869  * @extends Roo.bootstrap.Component
27870  * Bootstrap Menu class - container for Menu
27871  * @cfg {String} html Text of the menu
27872  * @cfg {String} weight (default | primary | success | info | warning | danger | inverse)
27873  * @cfg {String} icon Font awesome icon
27874  * @cfg {String} pos Menu align to (top | bottom) default bottom
27875  * 
27876  * 
27877  * @constructor
27878  * Create a new Menu
27879  * @param {Object} config The config object
27880  */
27881
27882
27883 Roo.bootstrap.menu.Menu = function(config){
27884     Roo.bootstrap.menu.Menu.superclass.constructor.call(this, config);
27885     
27886     this.addEvents({
27887         /**
27888          * @event beforeshow
27889          * Fires before this menu is displayed
27890          * @param {Roo.bootstrap.menu.Menu} this
27891          */
27892         beforeshow : true,
27893         /**
27894          * @event beforehide
27895          * Fires before this menu is hidden
27896          * @param {Roo.bootstrap.menu.Menu} this
27897          */
27898         beforehide : true,
27899         /**
27900          * @event show
27901          * Fires after this menu is displayed
27902          * @param {Roo.bootstrap.menu.Menu} this
27903          */
27904         show : true,
27905         /**
27906          * @event hide
27907          * Fires after this menu is hidden
27908          * @param {Roo.bootstrap.menu.Menu} this
27909          */
27910         hide : true,
27911         /**
27912          * @event click
27913          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
27914          * @param {Roo.bootstrap.menu.Menu} this
27915          * @param {Roo.EventObject} e
27916          */
27917         click : true
27918     });
27919     
27920 };
27921
27922 Roo.extend(Roo.bootstrap.menu.Menu, Roo.bootstrap.Component,  {
27923     
27924     submenu : false,
27925     html : '',
27926     weight : 'default',
27927     icon : false,
27928     pos : 'bottom',
27929     
27930     
27931     getChildContainer : function() {
27932         if(this.isSubMenu){
27933             return this.el;
27934         }
27935         
27936         return this.el.select('ul.dropdown-menu', true).first();  
27937     },
27938     
27939     getAutoCreate : function()
27940     {
27941         var text = [
27942             {
27943                 tag : 'span',
27944                 cls : 'roo-menu-text',
27945                 html : this.html
27946             }
27947         ];
27948         
27949         if(this.icon){
27950             text.unshift({
27951                 tag : 'i',
27952                 cls : 'fa ' + this.icon
27953             })
27954         }
27955         
27956         
27957         var cfg = {
27958             tag : 'div',
27959             cls : 'btn-group',
27960             cn : [
27961                 {
27962                     tag : 'button',
27963                     cls : 'dropdown-button btn btn-' + this.weight,
27964                     cn : text
27965                 },
27966                 {
27967                     tag : 'button',
27968                     cls : 'dropdown-toggle btn btn-' + this.weight,
27969                     cn : [
27970                         {
27971                             tag : 'span',
27972                             cls : 'caret'
27973                         }
27974                     ]
27975                 },
27976                 {
27977                     tag : 'ul',
27978                     cls : 'dropdown-menu'
27979                 }
27980             ]
27981             
27982         };
27983         
27984         if(this.pos == 'top'){
27985             cfg.cls += ' dropup';
27986         }
27987         
27988         if(this.isSubMenu){
27989             cfg = {
27990                 tag : 'ul',
27991                 cls : 'dropdown-menu'
27992             }
27993         }
27994         
27995         return cfg;
27996     },
27997     
27998     onRender : function(ct, position)
27999     {
28000         this.isSubMenu = ct.hasClass('dropdown-submenu');
28001         
28002         Roo.bootstrap.menu.Menu.superclass.onRender.call(this, ct, position);
28003     },
28004     
28005     initEvents : function() 
28006     {
28007         if(this.isSubMenu){
28008             return;
28009         }
28010         
28011         this.hidden = true;
28012         
28013         this.triggerEl = this.el.select('button.dropdown-toggle', true).first();
28014         this.triggerEl.on('click', this.onTriggerPress, this);
28015         
28016         this.buttonEl = this.el.select('button.dropdown-button', true).first();
28017         this.buttonEl.on('click', this.onClick, this);
28018         
28019     },
28020     
28021     list : function()
28022     {
28023         if(this.isSubMenu){
28024             return this.el;
28025         }
28026         
28027         return this.el.select('ul.dropdown-menu', true).first();
28028     },
28029     
28030     onClick : function(e)
28031     {
28032         this.fireEvent("click", this, e);
28033     },
28034     
28035     onTriggerPress  : function(e)
28036     {   
28037         if (this.isVisible()) {
28038             this.hide();
28039         } else {
28040             this.show();
28041         }
28042     },
28043     
28044     isVisible : function(){
28045         return !this.hidden;
28046     },
28047     
28048     show : function()
28049     {
28050         this.fireEvent("beforeshow", this);
28051         
28052         this.hidden = false;
28053         this.el.addClass('open');
28054         
28055         Roo.get(document).on("mouseup", this.onMouseUp, this);
28056         
28057         this.fireEvent("show", this);
28058         
28059         
28060     },
28061     
28062     hide : function()
28063     {
28064         this.fireEvent("beforehide", this);
28065         
28066         this.hidden = true;
28067         this.el.removeClass('open');
28068         
28069         Roo.get(document).un("mouseup", this.onMouseUp);
28070         
28071         this.fireEvent("hide", this);
28072     },
28073     
28074     onMouseUp : function()
28075     {
28076         this.hide();
28077     }
28078     
28079 });
28080
28081  
28082  /*
28083  * - LGPL
28084  *
28085  * menu item
28086  * 
28087  */
28088 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
28089
28090 /**
28091  * @class Roo.bootstrap.menu.Item
28092  * @extends Roo.bootstrap.Component
28093  * Bootstrap MenuItem class
28094  * @cfg {Boolean} submenu (true | false) default false
28095  * @cfg {String} html text of the item
28096  * @cfg {String} href the link
28097  * @cfg {Boolean} disable (true | false) default false
28098  * @cfg {Boolean} preventDefault (true | false) default true
28099  * @cfg {String} icon Font awesome icon
28100  * @cfg {String} pos Submenu align to (left | right) default right 
28101  * 
28102  * 
28103  * @constructor
28104  * Create a new Item
28105  * @param {Object} config The config object
28106  */
28107
28108
28109 Roo.bootstrap.menu.Item = function(config){
28110     Roo.bootstrap.menu.Item.superclass.constructor.call(this, config);
28111     this.addEvents({
28112         /**
28113          * @event mouseover
28114          * Fires when the mouse is hovering over this menu
28115          * @param {Roo.bootstrap.menu.Item} this
28116          * @param {Roo.EventObject} e
28117          */
28118         mouseover : true,
28119         /**
28120          * @event mouseout
28121          * Fires when the mouse exits this menu
28122          * @param {Roo.bootstrap.menu.Item} this
28123          * @param {Roo.EventObject} e
28124          */
28125         mouseout : true,
28126         // raw events
28127         /**
28128          * @event click
28129          * The raw click event for the entire grid.
28130          * @param {Roo.EventObject} e
28131          */
28132         click : true
28133     });
28134 };
28135
28136 Roo.extend(Roo.bootstrap.menu.Item, Roo.bootstrap.Component,  {
28137     
28138     submenu : false,
28139     href : '',
28140     html : '',
28141     preventDefault: true,
28142     disable : false,
28143     icon : false,
28144     pos : 'right',
28145     
28146     getAutoCreate : function()
28147     {
28148         var text = [
28149             {
28150                 tag : 'span',
28151                 cls : 'roo-menu-item-text',
28152                 html : this.html
28153             }
28154         ];
28155         
28156         if(this.icon){
28157             text.unshift({
28158                 tag : 'i',
28159                 cls : 'fa ' + this.icon
28160             })
28161         }
28162         
28163         var cfg = {
28164             tag : 'li',
28165             cn : [
28166                 {
28167                     tag : 'a',
28168                     href : this.href || '#',
28169                     cn : text
28170                 }
28171             ]
28172         };
28173         
28174         if(this.disable){
28175             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'disabled' : (cfg.cls + ' disabled');
28176         }
28177         
28178         if(this.submenu){
28179             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'dropdown-submenu' : (cfg.cls + ' dropdown-submenu');
28180             
28181             if(this.pos == 'left'){
28182                 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'pull-left' : (cfg.cls + ' pull-left');
28183             }
28184         }
28185         
28186         return cfg;
28187     },
28188     
28189     initEvents : function() 
28190     {
28191         this.el.on('mouseover', this.onMouseOver, this);
28192         this.el.on('mouseout', this.onMouseOut, this);
28193         
28194         this.el.select('a', true).first().on('click', this.onClick, this);
28195         
28196     },
28197     
28198     onClick : function(e)
28199     {
28200         if(this.preventDefault){
28201             e.preventDefault();
28202         }
28203         
28204         this.fireEvent("click", this, e);
28205     },
28206     
28207     onMouseOver : function(e)
28208     {
28209         if(this.submenu && this.pos == 'left'){
28210             this.el.select('ul.dropdown-menu', true).first().setLeft(this.el.select('ul.dropdown-menu', true).first().getWidth() * -1);
28211         }
28212         
28213         this.fireEvent("mouseover", this, e);
28214     },
28215     
28216     onMouseOut : function(e)
28217     {
28218         this.fireEvent("mouseout", this, e);
28219     }
28220 });
28221
28222  
28223
28224  /*
28225  * - LGPL
28226  *
28227  * menu separator
28228  * 
28229  */
28230 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
28231
28232 /**
28233  * @class Roo.bootstrap.menu.Separator
28234  * @extends Roo.bootstrap.Component
28235  * Bootstrap Separator class
28236  * 
28237  * @constructor
28238  * Create a new Separator
28239  * @param {Object} config The config object
28240  */
28241
28242
28243 Roo.bootstrap.menu.Separator = function(config){
28244     Roo.bootstrap.menu.Separator.superclass.constructor.call(this, config);
28245 };
28246
28247 Roo.extend(Roo.bootstrap.menu.Separator, Roo.bootstrap.Component,  {
28248     
28249     getAutoCreate : function(){
28250         var cfg = {
28251             tag : 'li',
28252             cls: 'divider'
28253         };
28254         
28255         return cfg;
28256     }
28257    
28258 });
28259
28260  
28261
28262  /*
28263  * - LGPL
28264  *
28265  * Tooltip
28266  * 
28267  */
28268
28269 /**
28270  * @class Roo.bootstrap.Tooltip
28271  * Bootstrap Tooltip class
28272  * This is basic at present - all componets support it by default, however they should add tooltipEl() method
28273  * to determine which dom element triggers the tooltip.
28274  * 
28275  * It needs to add support for additional attributes like tooltip-position
28276  * 
28277  * @constructor
28278  * Create a new Toolti
28279  * @param {Object} config The config object
28280  */
28281
28282 Roo.bootstrap.Tooltip = function(config){
28283     Roo.bootstrap.Tooltip.superclass.constructor.call(this, config);
28284     
28285     this.alignment = Roo.bootstrap.Tooltip.alignment;
28286     
28287     if(typeof(config) != 'undefined' && typeof(config.alignment) != 'undefined'){
28288         this.alignment = config.alignment;
28289     }
28290     
28291 };
28292
28293 Roo.apply(Roo.bootstrap.Tooltip, {
28294     /**
28295      * @function init initialize tooltip monitoring.
28296      * @static
28297      */
28298     currentEl : false,
28299     currentTip : false,
28300     currentRegion : false,
28301     
28302     //  init : delay?
28303     
28304     init : function()
28305     {
28306         Roo.get(document).on('mouseover', this.enter ,this);
28307         Roo.get(document).on('mouseout', this.leave, this);
28308          
28309         
28310         this.currentTip = new Roo.bootstrap.Tooltip();
28311     },
28312     
28313     enter : function(ev)
28314     {
28315         var dom = ev.getTarget();
28316         
28317         //Roo.log(['enter',dom]);
28318         var el = Roo.fly(dom);
28319         if (this.currentEl) {
28320             //Roo.log(dom);
28321             //Roo.log(this.currentEl);
28322             //Roo.log(this.currentEl.contains(dom));
28323             if (this.currentEl == el) {
28324                 return;
28325             }
28326             if (dom != this.currentEl.dom && this.currentEl.contains(dom)) {
28327                 return;
28328             }
28329
28330         }
28331         
28332         if (this.currentTip.el) {
28333             this.currentTip.el.setVisibilityMode(Roo.Element.DISPLAY).hide(); // force hiding...
28334         }    
28335         //Roo.log(ev);
28336         
28337         if(!el || el.dom == document){
28338             return;
28339         }
28340         
28341         var bindEl = el;
28342         
28343         // you can not look for children, as if el is the body.. then everythign is the child..
28344         if (!el.attr('tooltip')) { //
28345             if (!el.select("[tooltip]").elements.length) {
28346                 return;
28347             }
28348             // is the mouse over this child...?
28349             bindEl = el.select("[tooltip]").first();
28350             var xy = ev.getXY();
28351             if (!bindEl.getRegion().contains( { top : xy[1] ,right : xy[0] , bottom : xy[1], left : xy[0]})) {
28352                 //Roo.log("not in region.");
28353                 return;
28354             }
28355             //Roo.log("child element over..");
28356             
28357         }
28358         this.currentEl = bindEl;
28359         this.currentTip.bind(bindEl);
28360         this.currentRegion = Roo.lib.Region.getRegion(dom);
28361         this.currentTip.enter();
28362         
28363     },
28364     leave : function(ev)
28365     {
28366         var dom = ev.getTarget();
28367         //Roo.log(['leave',dom]);
28368         if (!this.currentEl) {
28369             return;
28370         }
28371         
28372         
28373         if (dom != this.currentEl.dom) {
28374             return;
28375         }
28376         var xy = ev.getXY();
28377         if (this.currentRegion.contains( new Roo.lib.Region( xy[1], xy[0] ,xy[1], xy[0]  ))) {
28378             return;
28379         }
28380         // only activate leave if mouse cursor is outside... bounding box..
28381         
28382         
28383         
28384         
28385         if (this.currentTip) {
28386             this.currentTip.leave();
28387         }
28388         //Roo.log('clear currentEl');
28389         this.currentEl = false;
28390         
28391         
28392     },
28393     alignment : {
28394         'left' : ['r-l', [-2,0], 'right'],
28395         'right' : ['l-r', [2,0], 'left'],
28396         'bottom' : ['t-b', [0,2], 'top'],
28397         'top' : [ 'b-t', [0,-2], 'bottom']
28398     }
28399     
28400 });
28401
28402
28403 Roo.extend(Roo.bootstrap.Tooltip, Roo.bootstrap.Component,  {
28404     
28405     
28406     bindEl : false,
28407     
28408     delay : null, // can be { show : 300 , hide: 500}
28409     
28410     timeout : null,
28411     
28412     hoverState : null, //???
28413     
28414     placement : 'bottom', 
28415     
28416     alignment : false,
28417     
28418     getAutoCreate : function(){
28419     
28420         var cfg = {
28421            cls : 'tooltip',   
28422            role : 'tooltip',
28423            cn : [
28424                 {
28425                     cls : 'tooltip-arrow arrow'
28426                 },
28427                 {
28428                     cls : 'tooltip-inner'
28429                 }
28430            ]
28431         };
28432         
28433         return cfg;
28434     },
28435     bind : function(el)
28436     {
28437         this.bindEl = el;
28438     },
28439     
28440     initEvents : function()
28441     {
28442         this.arrowEl = this.el.select('.arrow', true).first();
28443         this.innerEl = this.el.select('.tooltip-inner', true).first();
28444     },
28445     
28446     enter : function () {
28447        
28448         if (this.timeout != null) {
28449             clearTimeout(this.timeout);
28450         }
28451         
28452         this.hoverState = 'in';
28453          //Roo.log("enter - show");
28454         if (!this.delay || !this.delay.show) {
28455             this.show();
28456             return;
28457         }
28458         var _t = this;
28459         this.timeout = setTimeout(function () {
28460             if (_t.hoverState == 'in') {
28461                 _t.show();
28462             }
28463         }, this.delay.show);
28464     },
28465     leave : function()
28466     {
28467         clearTimeout(this.timeout);
28468     
28469         this.hoverState = 'out';
28470          if (!this.delay || !this.delay.hide) {
28471             this.hide();
28472             return;
28473         }
28474        
28475         var _t = this;
28476         this.timeout = setTimeout(function () {
28477             //Roo.log("leave - timeout");
28478             
28479             if (_t.hoverState == 'out') {
28480                 _t.hide();
28481                 Roo.bootstrap.Tooltip.currentEl = false;
28482             }
28483         }, delay);
28484     },
28485     
28486     show : function (msg)
28487     {
28488         if (!this.el) {
28489             this.render(document.body);
28490         }
28491         // set content.
28492         //Roo.log([this.bindEl, this.bindEl.attr('tooltip')]);
28493         
28494         var tip = msg || this.bindEl.attr('tooltip') || this.bindEl.select("[tooltip]").first().attr('tooltip');
28495         
28496         this.el.select('.tooltip-inner',true).first().dom.innerHTML = tip;
28497         
28498         this.el.removeClass(['fade','top','bottom', 'left', 'right','in',
28499                              'bs-tooltip-top','bs-tooltip-bottom', 'bs-tooltip-left', 'bs-tooltip-right']);
28500         
28501         var placement = typeof this.placement == 'function' ?
28502             this.placement.call(this, this.el, on_el) :
28503             this.placement;
28504             
28505         var autoToken = /\s?auto?\s?/i;
28506         var autoPlace = autoToken.test(placement);
28507         if (autoPlace) {
28508             placement = placement.replace(autoToken, '') || 'top';
28509         }
28510         
28511         //this.el.detach()
28512         //this.el.setXY([0,0]);
28513         this.el.show();
28514         //this.el.dom.style.display='block';
28515         
28516         //this.el.appendTo(on_el);
28517         
28518         var p = this.getPosition();
28519         var box = this.el.getBox();
28520         
28521         if (autoPlace) {
28522             // fixme..
28523         }
28524         
28525         var align = this.alignment[placement];
28526         
28527         var xy = this.el.getAlignToXY(this.bindEl, align[0], align[1]);
28528         
28529         if(placement == 'top' || placement == 'bottom'){
28530             if(xy[0] < 0){
28531                 placement = 'right';
28532             }
28533             
28534             if(xy[0] + this.el.getWidth() > Roo.lib.Dom.getViewWidth()){
28535                 placement = 'left';
28536             }
28537             
28538             var scroll = Roo.select('body', true).first().getScroll();
28539             
28540             if(xy[1] > Roo.lib.Dom.getViewHeight() + scroll.top - this.el.getHeight()){
28541                 placement = 'top';
28542             }
28543             
28544             align = this.alignment[placement];
28545             
28546             this.arrowEl.setLeft((this.innerEl.getWidth()/2) - 5);
28547             
28548         }
28549         
28550         this.el.alignTo(this.bindEl, align[0],align[1]);
28551         //var arrow = this.el.select('.arrow',true).first();
28552         //arrow.set(align[2], 
28553         
28554         this.el.addClass(placement);
28555         this.el.addClass("bs-tooltip-"+ placement);
28556         
28557         this.el.addClass('in fade show');
28558         
28559         this.hoverState = null;
28560         
28561         if (this.el.hasClass('fade')) {
28562             // fade it?
28563         }
28564         
28565         
28566         
28567         
28568         
28569     },
28570     hide : function()
28571     {
28572          
28573         if (!this.el) {
28574             return;
28575         }
28576         //this.el.setXY([0,0]);
28577         this.el.removeClass(['show', 'in']);
28578         //this.el.hide();
28579         
28580     }
28581     
28582 });
28583  
28584
28585  /*
28586  * - LGPL
28587  *
28588  * Location Picker
28589  * 
28590  */
28591
28592 /**
28593  * @class Roo.bootstrap.LocationPicker
28594  * @extends Roo.bootstrap.Component
28595  * Bootstrap LocationPicker class
28596  * @cfg {Number} latitude Position when init default 0
28597  * @cfg {Number} longitude Position when init default 0
28598  * @cfg {Number} zoom default 15
28599  * @cfg {String} mapTypeId default google.maps.MapTypeId.ROADMAP
28600  * @cfg {Boolean} mapTypeControl default false
28601  * @cfg {Boolean} disableDoubleClickZoom default false
28602  * @cfg {Boolean} scrollwheel default true
28603  * @cfg {Boolean} streetViewControl default false
28604  * @cfg {Number} radius default 0
28605  * @cfg {String} locationName
28606  * @cfg {Boolean} draggable default true
28607  * @cfg {Boolean} enableAutocomplete default false
28608  * @cfg {Boolean} enableReverseGeocode default true
28609  * @cfg {String} markerTitle
28610  * 
28611  * @constructor
28612  * Create a new LocationPicker
28613  * @param {Object} config The config object
28614  */
28615
28616
28617 Roo.bootstrap.LocationPicker = function(config){
28618     
28619     Roo.bootstrap.LocationPicker.superclass.constructor.call(this, config);
28620     
28621     this.addEvents({
28622         /**
28623          * @event initial
28624          * Fires when the picker initialized.
28625          * @param {Roo.bootstrap.LocationPicker} this
28626          * @param {Google Location} location
28627          */
28628         initial : true,
28629         /**
28630          * @event positionchanged
28631          * Fires when the picker position changed.
28632          * @param {Roo.bootstrap.LocationPicker} this
28633          * @param {Google Location} location
28634          */
28635         positionchanged : true,
28636         /**
28637          * @event resize
28638          * Fires when the map resize.
28639          * @param {Roo.bootstrap.LocationPicker} this
28640          */
28641         resize : true,
28642         /**
28643          * @event show
28644          * Fires when the map show.
28645          * @param {Roo.bootstrap.LocationPicker} this
28646          */
28647         show : true,
28648         /**
28649          * @event hide
28650          * Fires when the map hide.
28651          * @param {Roo.bootstrap.LocationPicker} this
28652          */
28653         hide : true,
28654         /**
28655          * @event mapClick
28656          * Fires when click the map.
28657          * @param {Roo.bootstrap.LocationPicker} this
28658          * @param {Map event} e
28659          */
28660         mapClick : true,
28661         /**
28662          * @event mapRightClick
28663          * Fires when right click the map.
28664          * @param {Roo.bootstrap.LocationPicker} this
28665          * @param {Map event} e
28666          */
28667         mapRightClick : true,
28668         /**
28669          * @event markerClick
28670          * Fires when click the marker.
28671          * @param {Roo.bootstrap.LocationPicker} this
28672          * @param {Map event} e
28673          */
28674         markerClick : true,
28675         /**
28676          * @event markerRightClick
28677          * Fires when right click the marker.
28678          * @param {Roo.bootstrap.LocationPicker} this
28679          * @param {Map event} e
28680          */
28681         markerRightClick : true,
28682         /**
28683          * @event OverlayViewDraw
28684          * Fires when OverlayView Draw
28685          * @param {Roo.bootstrap.LocationPicker} this
28686          */
28687         OverlayViewDraw : true,
28688         /**
28689          * @event OverlayViewOnAdd
28690          * Fires when OverlayView Draw
28691          * @param {Roo.bootstrap.LocationPicker} this
28692          */
28693         OverlayViewOnAdd : true,
28694         /**
28695          * @event OverlayViewOnRemove
28696          * Fires when OverlayView Draw
28697          * @param {Roo.bootstrap.LocationPicker} this
28698          */
28699         OverlayViewOnRemove : true,
28700         /**
28701          * @event OverlayViewShow
28702          * Fires when OverlayView Draw
28703          * @param {Roo.bootstrap.LocationPicker} this
28704          * @param {Pixel} cpx
28705          */
28706         OverlayViewShow : true,
28707         /**
28708          * @event OverlayViewHide
28709          * Fires when OverlayView Draw
28710          * @param {Roo.bootstrap.LocationPicker} this
28711          */
28712         OverlayViewHide : true,
28713         /**
28714          * @event loadexception
28715          * Fires when load google lib failed.
28716          * @param {Roo.bootstrap.LocationPicker} this
28717          */
28718         loadexception : true
28719     });
28720         
28721 };
28722
28723 Roo.extend(Roo.bootstrap.LocationPicker, Roo.bootstrap.Component,  {
28724     
28725     gMapContext: false,
28726     
28727     latitude: 0,
28728     longitude: 0,
28729     zoom: 15,
28730     mapTypeId: false,
28731     mapTypeControl: false,
28732     disableDoubleClickZoom: false,
28733     scrollwheel: true,
28734     streetViewControl: false,
28735     radius: 0,
28736     locationName: '',
28737     draggable: true,
28738     enableAutocomplete: false,
28739     enableReverseGeocode: true,
28740     markerTitle: '',
28741     
28742     getAutoCreate: function()
28743     {
28744
28745         var cfg = {
28746             tag: 'div',
28747             cls: 'roo-location-picker'
28748         };
28749         
28750         return cfg
28751     },
28752     
28753     initEvents: function(ct, position)
28754     {       
28755         if(!this.el.getWidth() || this.isApplied()){
28756             return;
28757         }
28758         
28759         this.el.setVisibilityMode(Roo.Element.DISPLAY);
28760         
28761         this.initial();
28762     },
28763     
28764     initial: function()
28765     {
28766         if(typeof(google) == 'undefined' || typeof(google.maps) == 'undefined'){
28767             this.fireEvent('loadexception', this);
28768             return;
28769         }
28770         
28771         if(!this.mapTypeId){
28772             this.mapTypeId = google.maps.MapTypeId.ROADMAP;
28773         }
28774         
28775         this.gMapContext = this.GMapContext();
28776         
28777         this.initOverlayView();
28778         
28779         this.OverlayView = new Roo.bootstrap.LocationPicker.OverlayView(this.gMapContext.map);
28780         
28781         var _this = this;
28782                 
28783         google.maps.event.addListener(this.gMapContext.marker, "dragend", function(event) {
28784             _this.setPosition(_this.gMapContext.marker.position);
28785         });
28786         
28787         google.maps.event.addListener(this.gMapContext.map, 'click', function(event){
28788             _this.fireEvent('mapClick', this, event);
28789             
28790         });
28791
28792         google.maps.event.addListener(this.gMapContext.map, 'rightclick', function(event){
28793             _this.fireEvent('mapRightClick', this, event);
28794             
28795         });
28796         
28797         google.maps.event.addListener(this.gMapContext.marker, 'click', function(event){
28798             _this.fireEvent('markerClick', this, event);
28799             
28800         });
28801
28802         google.maps.event.addListener(this.gMapContext.marker, 'rightclick', function(event){
28803             _this.fireEvent('markerRightClick', this, event);
28804             
28805         });
28806         
28807         this.setPosition(this.gMapContext.location);
28808         
28809         this.fireEvent('initial', this, this.gMapContext.location);
28810     },
28811     
28812     initOverlayView: function()
28813     {
28814         var _this = this;
28815         
28816         Roo.bootstrap.LocationPicker.OverlayView.prototype = Roo.apply(new google.maps.OverlayView(), {
28817             
28818             draw: function()
28819             {
28820                 _this.fireEvent('OverlayViewDraw', _this);
28821             },
28822             
28823             onAdd: function()
28824             {
28825                 _this.fireEvent('OverlayViewOnAdd', _this);
28826             },
28827             
28828             onRemove: function()
28829             {
28830                 _this.fireEvent('OverlayViewOnRemove', _this);
28831             },
28832             
28833             show: function(cpx)
28834             {
28835                 _this.fireEvent('OverlayViewShow', _this, cpx);
28836             },
28837             
28838             hide: function()
28839             {
28840                 _this.fireEvent('OverlayViewHide', _this);
28841             }
28842             
28843         });
28844     },
28845     
28846     fromLatLngToContainerPixel: function(event)
28847     {
28848         return this.OverlayView.getProjection().fromLatLngToContainerPixel(event.latLng);
28849     },
28850     
28851     isApplied: function() 
28852     {
28853         return this.getGmapContext() == false ? false : true;
28854     },
28855     
28856     getGmapContext: function() 
28857     {
28858         return (typeof(this.gMapContext) == 'undefined') ? false : this.gMapContext;
28859     },
28860     
28861     GMapContext: function() 
28862     {
28863         var position = new google.maps.LatLng(this.latitude, this.longitude);
28864         
28865         var _map = new google.maps.Map(this.el.dom, {
28866             center: position,
28867             zoom: this.zoom,
28868             mapTypeId: this.mapTypeId,
28869             mapTypeControl: this.mapTypeControl,
28870             disableDoubleClickZoom: this.disableDoubleClickZoom,
28871             scrollwheel: this.scrollwheel,
28872             streetViewControl: this.streetViewControl,
28873             locationName: this.locationName,
28874             draggable: this.draggable,
28875             enableAutocomplete: this.enableAutocomplete,
28876             enableReverseGeocode: this.enableReverseGeocode
28877         });
28878         
28879         var _marker = new google.maps.Marker({
28880             position: position,
28881             map: _map,
28882             title: this.markerTitle,
28883             draggable: this.draggable
28884         });
28885         
28886         return {
28887             map: _map,
28888             marker: _marker,
28889             circle: null,
28890             location: position,
28891             radius: this.radius,
28892             locationName: this.locationName,
28893             addressComponents: {
28894                 formatted_address: null,
28895                 addressLine1: null,
28896                 addressLine2: null,
28897                 streetName: null,
28898                 streetNumber: null,
28899                 city: null,
28900                 district: null,
28901                 state: null,
28902                 stateOrProvince: null
28903             },
28904             settings: this,
28905             domContainer: this.el.dom,
28906             geodecoder: new google.maps.Geocoder()
28907         };
28908     },
28909     
28910     drawCircle: function(center, radius, options) 
28911     {
28912         if (this.gMapContext.circle != null) {
28913             this.gMapContext.circle.setMap(null);
28914         }
28915         if (radius > 0) {
28916             radius *= 1;
28917             options = Roo.apply({}, options, {
28918                 strokeColor: "#0000FF",
28919                 strokeOpacity: .35,
28920                 strokeWeight: 2,
28921                 fillColor: "#0000FF",
28922                 fillOpacity: .2
28923             });
28924             
28925             options.map = this.gMapContext.map;
28926             options.radius = radius;
28927             options.center = center;
28928             this.gMapContext.circle = new google.maps.Circle(options);
28929             return this.gMapContext.circle;
28930         }
28931         
28932         return null;
28933     },
28934     
28935     setPosition: function(location) 
28936     {
28937         this.gMapContext.location = location;
28938         this.gMapContext.marker.setPosition(location);
28939         this.gMapContext.map.panTo(location);
28940         this.drawCircle(location, this.gMapContext.radius, {});
28941         
28942         var _this = this;
28943         
28944         if (this.gMapContext.settings.enableReverseGeocode) {
28945             this.gMapContext.geodecoder.geocode({
28946                 latLng: this.gMapContext.location
28947             }, function(results, status) {
28948                 
28949                 if (status == google.maps.GeocoderStatus.OK && results.length > 0) {
28950                     _this.gMapContext.locationName = results[0].formatted_address;
28951                     _this.gMapContext.addressComponents = _this.address_component_from_google_geocode(results[0].address_components);
28952                     
28953                     _this.fireEvent('positionchanged', this, location);
28954                 }
28955             });
28956             
28957             return;
28958         }
28959         
28960         this.fireEvent('positionchanged', this, location);
28961     },
28962     
28963     resize: function()
28964     {
28965         google.maps.event.trigger(this.gMapContext.map, "resize");
28966         
28967         this.gMapContext.map.setCenter(this.gMapContext.marker.position);
28968         
28969         this.fireEvent('resize', this);
28970     },
28971     
28972     setPositionByLatLng: function(latitude, longitude)
28973     {
28974         this.setPosition(new google.maps.LatLng(latitude, longitude));
28975     },
28976     
28977     getCurrentPosition: function() 
28978     {
28979         return {
28980             latitude: this.gMapContext.location.lat(),
28981             longitude: this.gMapContext.location.lng()
28982         };
28983     },
28984     
28985     getAddressName: function() 
28986     {
28987         return this.gMapContext.locationName;
28988     },
28989     
28990     getAddressComponents: function() 
28991     {
28992         return this.gMapContext.addressComponents;
28993     },
28994     
28995     address_component_from_google_geocode: function(address_components) 
28996     {
28997         var result = {};
28998         
28999         for (var i = 0; i < address_components.length; i++) {
29000             var component = address_components[i];
29001             if (component.types.indexOf("postal_code") >= 0) {
29002                 result.postalCode = component.short_name;
29003             } else if (component.types.indexOf("street_number") >= 0) {
29004                 result.streetNumber = component.short_name;
29005             } else if (component.types.indexOf("route") >= 0) {
29006                 result.streetName = component.short_name;
29007             } else if (component.types.indexOf("neighborhood") >= 0) {
29008                 result.city = component.short_name;
29009             } else if (component.types.indexOf("locality") >= 0) {
29010                 result.city = component.short_name;
29011             } else if (component.types.indexOf("sublocality") >= 0) {
29012                 result.district = component.short_name;
29013             } else if (component.types.indexOf("administrative_area_level_1") >= 0) {
29014                 result.stateOrProvince = component.short_name;
29015             } else if (component.types.indexOf("country") >= 0) {
29016                 result.country = component.short_name;
29017             }
29018         }
29019         
29020         result.addressLine1 = [ result.streetNumber, result.streetName ].join(" ").trim();
29021         result.addressLine2 = "";
29022         return result;
29023     },
29024     
29025     setZoomLevel: function(zoom)
29026     {
29027         this.gMapContext.map.setZoom(zoom);
29028     },
29029     
29030     show: function()
29031     {
29032         if(!this.el){
29033             return;
29034         }
29035         
29036         this.el.show();
29037         
29038         this.resize();
29039         
29040         this.fireEvent('show', this);
29041     },
29042     
29043     hide: function()
29044     {
29045         if(!this.el){
29046             return;
29047         }
29048         
29049         this.el.hide();
29050         
29051         this.fireEvent('hide', this);
29052     }
29053     
29054 });
29055
29056 Roo.apply(Roo.bootstrap.LocationPicker, {
29057     
29058     OverlayView : function(map, options)
29059     {
29060         options = options || {};
29061         
29062         this.setMap(map);
29063     }
29064     
29065     
29066 });/**
29067  * @class Roo.bootstrap.Alert
29068  * @extends Roo.bootstrap.Component
29069  * Bootstrap Alert class - shows an alert area box
29070  * eg
29071  * <div class="alert alert-danger" role="alert"><span class="fa fa-exclamation-triangle"></span><span class="sr-only">Error:</span>
29072   Enter a valid email address
29073 </div>
29074  * @licence LGPL
29075  * @cfg {String} title The title of alert
29076  * @cfg {String} html The content of alert
29077  * @cfg {String} weight (  success | info | warning | danger )
29078  * @cfg {String} faicon font-awesomeicon
29079  * 
29080  * @constructor
29081  * Create a new alert
29082  * @param {Object} config The config object
29083  */
29084
29085
29086 Roo.bootstrap.Alert = function(config){
29087     Roo.bootstrap.Alert.superclass.constructor.call(this, config);
29088     
29089 };
29090
29091 Roo.extend(Roo.bootstrap.Alert, Roo.bootstrap.Component,  {
29092     
29093     title: '',
29094     html: '',
29095     weight: false,
29096     faicon: false,
29097     
29098     getAutoCreate : function()
29099     {
29100         
29101         var cfg = {
29102             tag : 'div',
29103             cls : 'alert',
29104             cn : [
29105                 {
29106                     tag : 'i',
29107                     cls : 'roo-alert-icon'
29108                     
29109                 },
29110                 {
29111                     tag : 'b',
29112                     cls : 'roo-alert-title',
29113                     html : this.title
29114                 },
29115                 {
29116                     tag : 'span',
29117                     cls : 'roo-alert-text',
29118                     html : this.html
29119                 }
29120             ]
29121         };
29122         
29123         if(this.faicon){
29124             cfg.cn[0].cls += ' fa ' + this.faicon;
29125         }
29126         
29127         if(this.weight){
29128             cfg.cls += ' alert-' + this.weight;
29129         }
29130         
29131         return cfg;
29132     },
29133     
29134     initEvents: function() 
29135     {
29136         this.el.setVisibilityMode(Roo.Element.DISPLAY);
29137     },
29138     
29139     setTitle : function(str)
29140     {
29141         this.el.select('.roo-alert-title',true).first().dom.innerHTML = str;
29142     },
29143     
29144     setText : function(str)
29145     {
29146         this.el.select('.roo-alert-text',true).first().dom.innerHTML = str;
29147     },
29148     
29149     setWeight : function(weight)
29150     {
29151         if(this.weight){
29152             this.el.select('.alert',true).first().removeClass('alert-' + this.weight);
29153         }
29154         
29155         this.weight = weight;
29156         
29157         this.el.select('.alert',true).first().addClass('alert-' + this.weight);
29158     },
29159     
29160     setIcon : function(icon)
29161     {
29162         if(this.faicon){
29163             this.el.select('.roo-alert-icon',true).first().removeClass(['fa', 'fa-' + this.faicon]);
29164         }
29165         
29166         this.faicon = icon;
29167         
29168         this.el.select('.roo-alert-icon',true).first().addClass(['fa', 'fa-' + this.faicon]);
29169     },
29170     
29171     hide: function() 
29172     {
29173         this.el.hide();   
29174     },
29175     
29176     show: function() 
29177     {  
29178         this.el.show();   
29179     }
29180     
29181 });
29182
29183  
29184 /*
29185 * Licence: LGPL
29186 */
29187
29188 /**
29189  * @class Roo.bootstrap.UploadCropbox
29190  * @extends Roo.bootstrap.Component
29191  * Bootstrap UploadCropbox class
29192  * @cfg {String} emptyText show when image has been loaded
29193  * @cfg {String} rotateNotify show when image too small to rotate
29194  * @cfg {Number} errorTimeout default 3000
29195  * @cfg {Number} minWidth default 300
29196  * @cfg {Number} minHeight default 300
29197  * @cfg {Array} buttons default ['rotateLeft', 'pictureBtn', 'rotateRight']
29198  * @cfg {Boolean} isDocument (true|false) default false
29199  * @cfg {String} url action url
29200  * @cfg {String} paramName default 'imageUpload'
29201  * @cfg {String} method default POST
29202  * @cfg {Boolean} loadMask (true|false) default true
29203  * @cfg {Boolean} loadingText default 'Loading...'
29204  * 
29205  * @constructor
29206  * Create a new UploadCropbox
29207  * @param {Object} config The config object
29208  */
29209
29210 Roo.bootstrap.UploadCropbox = function(config){
29211     Roo.bootstrap.UploadCropbox.superclass.constructor.call(this, config);
29212     
29213     this.addEvents({
29214         /**
29215          * @event beforeselectfile
29216          * Fire before select file
29217          * @param {Roo.bootstrap.UploadCropbox} this
29218          */
29219         "beforeselectfile" : true,
29220         /**
29221          * @event initial
29222          * Fire after initEvent
29223          * @param {Roo.bootstrap.UploadCropbox} this
29224          */
29225         "initial" : true,
29226         /**
29227          * @event crop
29228          * Fire after initEvent
29229          * @param {Roo.bootstrap.UploadCropbox} this
29230          * @param {String} data
29231          */
29232         "crop" : true,
29233         /**
29234          * @event prepare
29235          * Fire when preparing the file data
29236          * @param {Roo.bootstrap.UploadCropbox} this
29237          * @param {Object} file
29238          */
29239         "prepare" : true,
29240         /**
29241          * @event exception
29242          * Fire when get exception
29243          * @param {Roo.bootstrap.UploadCropbox} this
29244          * @param {XMLHttpRequest} xhr
29245          */
29246         "exception" : true,
29247         /**
29248          * @event beforeloadcanvas
29249          * Fire before load the canvas
29250          * @param {Roo.bootstrap.UploadCropbox} this
29251          * @param {String} src
29252          */
29253         "beforeloadcanvas" : true,
29254         /**
29255          * @event trash
29256          * Fire when trash image
29257          * @param {Roo.bootstrap.UploadCropbox} this
29258          */
29259         "trash" : true,
29260         /**
29261          * @event download
29262          * Fire when download the image
29263          * @param {Roo.bootstrap.UploadCropbox} this
29264          */
29265         "download" : true,
29266         /**
29267          * @event footerbuttonclick
29268          * Fire when footerbuttonclick
29269          * @param {Roo.bootstrap.UploadCropbox} this
29270          * @param {String} type
29271          */
29272         "footerbuttonclick" : true,
29273         /**
29274          * @event resize
29275          * Fire when resize
29276          * @param {Roo.bootstrap.UploadCropbox} this
29277          */
29278         "resize" : true,
29279         /**
29280          * @event rotate
29281          * Fire when rotate the image
29282          * @param {Roo.bootstrap.UploadCropbox} this
29283          * @param {String} pos
29284          */
29285         "rotate" : true,
29286         /**
29287          * @event inspect
29288          * Fire when inspect the file
29289          * @param {Roo.bootstrap.UploadCropbox} this
29290          * @param {Object} file
29291          */
29292         "inspect" : true,
29293         /**
29294          * @event upload
29295          * Fire when xhr upload the file
29296          * @param {Roo.bootstrap.UploadCropbox} this
29297          * @param {Object} data
29298          */
29299         "upload" : true,
29300         /**
29301          * @event arrange
29302          * Fire when arrange the file data
29303          * @param {Roo.bootstrap.UploadCropbox} this
29304          * @param {Object} formData
29305          */
29306         "arrange" : true
29307     });
29308     
29309     this.buttons = this.buttons || Roo.bootstrap.UploadCropbox.footer.STANDARD;
29310 };
29311
29312 Roo.extend(Roo.bootstrap.UploadCropbox, Roo.bootstrap.Component,  {
29313     
29314     emptyText : 'Click to upload image',
29315     rotateNotify : 'Image is too small to rotate',
29316     errorTimeout : 3000,
29317     scale : 0,
29318     baseScale : 1,
29319     rotate : 0,
29320     dragable : false,
29321     pinching : false,
29322     mouseX : 0,
29323     mouseY : 0,
29324     cropData : false,
29325     minWidth : 300,
29326     minHeight : 300,
29327     file : false,
29328     exif : {},
29329     baseRotate : 1,
29330     cropType : 'image/jpeg',
29331     buttons : false,
29332     canvasLoaded : false,
29333     isDocument : false,
29334     method : 'POST',
29335     paramName : 'imageUpload',
29336     loadMask : true,
29337     loadingText : 'Loading...',
29338     maskEl : false,
29339     
29340     getAutoCreate : function()
29341     {
29342         var cfg = {
29343             tag : 'div',
29344             cls : 'roo-upload-cropbox',
29345             cn : [
29346                 {
29347                     tag : 'input',
29348                     cls : 'roo-upload-cropbox-selector',
29349                     type : 'file'
29350                 },
29351                 {
29352                     tag : 'div',
29353                     cls : 'roo-upload-cropbox-body',
29354                     style : 'cursor:pointer',
29355                     cn : [
29356                         {
29357                             tag : 'div',
29358                             cls : 'roo-upload-cropbox-preview'
29359                         },
29360                         {
29361                             tag : 'div',
29362                             cls : 'roo-upload-cropbox-thumb'
29363                         },
29364                         {
29365                             tag : 'div',
29366                             cls : 'roo-upload-cropbox-empty-notify',
29367                             html : this.emptyText
29368                         },
29369                         {
29370                             tag : 'div',
29371                             cls : 'roo-upload-cropbox-error-notify alert alert-danger',
29372                             html : this.rotateNotify
29373                         }
29374                     ]
29375                 },
29376                 {
29377                     tag : 'div',
29378                     cls : 'roo-upload-cropbox-footer',
29379                     cn : {
29380                         tag : 'div',
29381                         cls : 'btn-group btn-group-justified roo-upload-cropbox-btn-group',
29382                         cn : []
29383                     }
29384                 }
29385             ]
29386         };
29387         
29388         return cfg;
29389     },
29390     
29391     onRender : function(ct, position)
29392     {
29393         Roo.bootstrap.UploadCropbox.superclass.onRender.call(this, ct, position);
29394         
29395         if (this.buttons.length) {
29396             
29397             Roo.each(this.buttons, function(bb) {
29398                 
29399                 var btn = this.el.select('.roo-upload-cropbox-footer div.roo-upload-cropbox-btn-group').first().createChild(bb);
29400                 
29401                 btn.on('click', this.onFooterButtonClick.createDelegate(this, [bb.action], true));
29402                 
29403             }, this);
29404         }
29405         
29406         if(this.loadMask){
29407             this.maskEl = this.el;
29408         }
29409     },
29410     
29411     initEvents : function()
29412     {
29413         this.urlAPI = (window.createObjectURL && window) || 
29414                                 (window.URL && URL.revokeObjectURL && URL) || 
29415                                 (window.webkitURL && webkitURL);
29416                         
29417         this.bodyEl = this.el.select('.roo-upload-cropbox-body', true).first();
29418         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29419         
29420         this.selectorEl = this.el.select('.roo-upload-cropbox-selector', true).first();
29421         this.selectorEl.hide();
29422         
29423         this.previewEl = this.el.select('.roo-upload-cropbox-preview', true).first();
29424         this.previewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29425         
29426         this.thumbEl = this.el.select('.roo-upload-cropbox-thumb', true).first();
29427         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29428         this.thumbEl.hide();
29429         
29430         this.notifyEl = this.el.select('.roo-upload-cropbox-empty-notify', true).first();
29431         this.notifyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29432         
29433         this.errorEl = this.el.select('.roo-upload-cropbox-error-notify', true).first();
29434         this.errorEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29435         this.errorEl.hide();
29436         
29437         this.footerEl = this.el.select('.roo-upload-cropbox-footer', true).first();
29438         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29439         this.footerEl.hide();
29440         
29441         this.setThumbBoxSize();
29442         
29443         this.bind();
29444         
29445         this.resize();
29446         
29447         this.fireEvent('initial', this);
29448     },
29449
29450     bind : function()
29451     {
29452         var _this = this;
29453         
29454         window.addEventListener("resize", function() { _this.resize(); } );
29455         
29456         this.bodyEl.on('click', this.beforeSelectFile, this);
29457         
29458         if(Roo.isTouch){
29459             this.bodyEl.on('touchstart', this.onTouchStart, this);
29460             this.bodyEl.on('touchmove', this.onTouchMove, this);
29461             this.bodyEl.on('touchend', this.onTouchEnd, this);
29462         }
29463         
29464         if(!Roo.isTouch){
29465             this.bodyEl.on('mousedown', this.onMouseDown, this);
29466             this.bodyEl.on('mousemove', this.onMouseMove, this);
29467             var mousewheel = (/Firefox/i.test(navigator.userAgent))? 'DOMMouseScroll' : 'mousewheel';
29468             this.bodyEl.on(mousewheel, this.onMouseWheel, this);
29469             Roo.get(document).on('mouseup', this.onMouseUp, this);
29470         }
29471         
29472         this.selectorEl.on('change', this.onFileSelected, this);
29473     },
29474     
29475     reset : function()
29476     {    
29477         this.scale = 0;
29478         this.baseScale = 1;
29479         this.rotate = 0;
29480         this.baseRotate = 1;
29481         this.dragable = false;
29482         this.pinching = false;
29483         this.mouseX = 0;
29484         this.mouseY = 0;
29485         this.cropData = false;
29486         this.notifyEl.dom.innerHTML = this.emptyText;
29487         
29488         this.selectorEl.dom.value = '';
29489         
29490     },
29491     
29492     resize : function()
29493     {
29494         if(this.fireEvent('resize', this) != false){
29495             this.setThumbBoxPosition();
29496             this.setCanvasPosition();
29497         }
29498     },
29499     
29500     onFooterButtonClick : function(e, el, o, type)
29501     {
29502         switch (type) {
29503             case 'rotate-left' :
29504                 this.onRotateLeft(e);
29505                 break;
29506             case 'rotate-right' :
29507                 this.onRotateRight(e);
29508                 break;
29509             case 'picture' :
29510                 this.beforeSelectFile(e);
29511                 break;
29512             case 'trash' :
29513                 this.trash(e);
29514                 break;
29515             case 'crop' :
29516                 this.crop(e);
29517                 break;
29518             case 'download' :
29519                 this.download(e);
29520                 break;
29521             default :
29522                 break;
29523         }
29524         
29525         this.fireEvent('footerbuttonclick', this, type);
29526     },
29527     
29528     beforeSelectFile : function(e)
29529     {
29530         e.preventDefault();
29531         
29532         if(this.fireEvent('beforeselectfile', this) != false){
29533             this.selectorEl.dom.click();
29534         }
29535     },
29536     
29537     onFileSelected : function(e)
29538     {
29539         e.preventDefault();
29540         
29541         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
29542             return;
29543         }
29544         
29545         var file = this.selectorEl.dom.files[0];
29546         
29547         if(this.fireEvent('inspect', this, file) != false){
29548             this.prepare(file);
29549         }
29550         
29551     },
29552     
29553     trash : function(e)
29554     {
29555         this.fireEvent('trash', this);
29556     },
29557     
29558     download : function(e)
29559     {
29560         this.fireEvent('download', this);
29561     },
29562     
29563     loadCanvas : function(src)
29564     {   
29565         if(this.fireEvent('beforeloadcanvas', this, src) != false){
29566             
29567             this.reset();
29568             
29569             this.imageEl = document.createElement('img');
29570             
29571             var _this = this;
29572             
29573             this.imageEl.addEventListener("load", function(){ _this.onLoadCanvas(); });
29574             
29575             this.imageEl.src = src;
29576         }
29577     },
29578     
29579     onLoadCanvas : function()
29580     {   
29581         this.imageEl.OriginWidth = this.imageEl.naturalWidth || this.imageEl.width;
29582         this.imageEl.OriginHeight = this.imageEl.naturalHeight || this.imageEl.height;
29583         
29584         this.bodyEl.un('click', this.beforeSelectFile, this);
29585         
29586         this.notifyEl.hide();
29587         this.thumbEl.show();
29588         this.footerEl.show();
29589         
29590         this.baseRotateLevel();
29591         
29592         if(this.isDocument){
29593             this.setThumbBoxSize();
29594         }
29595         
29596         this.setThumbBoxPosition();
29597         
29598         this.baseScaleLevel();
29599         
29600         this.draw();
29601         
29602         this.resize();
29603         
29604         this.canvasLoaded = true;
29605         
29606         if(this.loadMask){
29607             this.maskEl.unmask();
29608         }
29609         
29610     },
29611     
29612     setCanvasPosition : function()
29613     {   
29614         if(!this.canvasEl){
29615             return;
29616         }
29617         
29618         var pw = Math.ceil((this.bodyEl.getWidth() - this.canvasEl.width) / 2);
29619         var ph = Math.ceil((this.bodyEl.getHeight() - this.canvasEl.height) / 2);
29620         
29621         this.previewEl.setLeft(pw);
29622         this.previewEl.setTop(ph);
29623         
29624     },
29625     
29626     onMouseDown : function(e)
29627     {   
29628         e.stopEvent();
29629         
29630         this.dragable = true;
29631         this.pinching = false;
29632         
29633         if(this.isDocument && (this.canvasEl.width < this.thumbEl.getWidth() || this.canvasEl.height < this.thumbEl.getHeight())){
29634             this.dragable = false;
29635             return;
29636         }
29637         
29638         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
29639         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
29640         
29641     },
29642     
29643     onMouseMove : function(e)
29644     {   
29645         e.stopEvent();
29646         
29647         if(!this.canvasLoaded){
29648             return;
29649         }
29650         
29651         if (!this.dragable){
29652             return;
29653         }
29654         
29655         var minX = Math.ceil(this.thumbEl.getLeft(true));
29656         var minY = Math.ceil(this.thumbEl.getTop(true));
29657         
29658         var maxX = Math.ceil(minX + this.thumbEl.getWidth() - this.canvasEl.width);
29659         var maxY = Math.ceil(minY + this.thumbEl.getHeight() - this.canvasEl.height);
29660         
29661         var x = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
29662         var y = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
29663         
29664         x = x - this.mouseX;
29665         y = y - this.mouseY;
29666         
29667         var bgX = Math.ceil(x + this.previewEl.getLeft(true));
29668         var bgY = Math.ceil(y + this.previewEl.getTop(true));
29669         
29670         bgX = (minX < bgX) ? minX : ((maxX > bgX) ? maxX : bgX);
29671         bgY = (minY < bgY) ? minY : ((maxY > bgY) ? maxY : bgY);
29672         
29673         this.previewEl.setLeft(bgX);
29674         this.previewEl.setTop(bgY);
29675         
29676         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
29677         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
29678     },
29679     
29680     onMouseUp : function(e)
29681     {   
29682         e.stopEvent();
29683         
29684         this.dragable = false;
29685     },
29686     
29687     onMouseWheel : function(e)
29688     {   
29689         e.stopEvent();
29690         
29691         this.startScale = this.scale;
29692         
29693         this.scale = (e.getWheelDelta() == 1) ? (this.scale + 1) : (this.scale - 1);
29694         
29695         if(!this.zoomable()){
29696             this.scale = this.startScale;
29697             return;
29698         }
29699         
29700         this.draw();
29701         
29702         return;
29703     },
29704     
29705     zoomable : function()
29706     {
29707         var minScale = this.thumbEl.getWidth() / this.minWidth;
29708         
29709         if(this.minWidth < this.minHeight){
29710             minScale = this.thumbEl.getHeight() / this.minHeight;
29711         }
29712         
29713         var width = Math.ceil(this.imageEl.OriginWidth * this.getScaleLevel() / minScale);
29714         var height = Math.ceil(this.imageEl.OriginHeight * this.getScaleLevel() / minScale);
29715         
29716         if(
29717                 this.isDocument &&
29718                 (this.rotate == 0 || this.rotate == 180) && 
29719                 (
29720                     width > this.imageEl.OriginWidth || 
29721                     height > this.imageEl.OriginHeight ||
29722                     (width < this.minWidth && height < this.minHeight)
29723                 )
29724         ){
29725             return false;
29726         }
29727         
29728         if(
29729                 this.isDocument &&
29730                 (this.rotate == 90 || this.rotate == 270) && 
29731                 (
29732                     width > this.imageEl.OriginWidth || 
29733                     height > this.imageEl.OriginHeight ||
29734                     (width < this.minHeight && height < this.minWidth)
29735                 )
29736         ){
29737             return false;
29738         }
29739         
29740         if(
29741                 !this.isDocument &&
29742                 (this.rotate == 0 || this.rotate == 180) && 
29743                 (
29744                     width < this.minWidth || 
29745                     width > this.imageEl.OriginWidth || 
29746                     height < this.minHeight || 
29747                     height > this.imageEl.OriginHeight
29748                 )
29749         ){
29750             return false;
29751         }
29752         
29753         if(
29754                 !this.isDocument &&
29755                 (this.rotate == 90 || this.rotate == 270) && 
29756                 (
29757                     width < this.minHeight || 
29758                     width > this.imageEl.OriginWidth || 
29759                     height < this.minWidth || 
29760                     height > this.imageEl.OriginHeight
29761                 )
29762         ){
29763             return false;
29764         }
29765         
29766         return true;
29767         
29768     },
29769     
29770     onRotateLeft : function(e)
29771     {   
29772         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
29773             
29774             var minScale = this.thumbEl.getWidth() / this.minWidth;
29775             
29776             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
29777             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
29778             
29779             this.startScale = this.scale;
29780             
29781             while (this.getScaleLevel() < minScale){
29782             
29783                 this.scale = this.scale + 1;
29784                 
29785                 if(!this.zoomable()){
29786                     break;
29787                 }
29788                 
29789                 if(
29790                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
29791                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
29792                 ){
29793                     continue;
29794                 }
29795                 
29796                 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
29797
29798                 this.draw();
29799                 
29800                 return;
29801             }
29802             
29803             this.scale = this.startScale;
29804             
29805             this.onRotateFail();
29806             
29807             return false;
29808         }
29809         
29810         this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
29811
29812         if(this.isDocument){
29813             this.setThumbBoxSize();
29814             this.setThumbBoxPosition();
29815             this.setCanvasPosition();
29816         }
29817         
29818         this.draw();
29819         
29820         this.fireEvent('rotate', this, 'left');
29821         
29822     },
29823     
29824     onRotateRight : function(e)
29825     {
29826         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
29827             
29828             var minScale = this.thumbEl.getWidth() / this.minWidth;
29829         
29830             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
29831             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
29832             
29833             this.startScale = this.scale;
29834             
29835             while (this.getScaleLevel() < minScale){
29836             
29837                 this.scale = this.scale + 1;
29838                 
29839                 if(!this.zoomable()){
29840                     break;
29841                 }
29842                 
29843                 if(
29844                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
29845                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
29846                 ){
29847                     continue;
29848                 }
29849                 
29850                 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
29851
29852                 this.draw();
29853                 
29854                 return;
29855             }
29856             
29857             this.scale = this.startScale;
29858             
29859             this.onRotateFail();
29860             
29861             return false;
29862         }
29863         
29864         this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
29865
29866         if(this.isDocument){
29867             this.setThumbBoxSize();
29868             this.setThumbBoxPosition();
29869             this.setCanvasPosition();
29870         }
29871         
29872         this.draw();
29873         
29874         this.fireEvent('rotate', this, 'right');
29875     },
29876     
29877     onRotateFail : function()
29878     {
29879         this.errorEl.show(true);
29880         
29881         var _this = this;
29882         
29883         (function() { _this.errorEl.hide(true); }).defer(this.errorTimeout);
29884     },
29885     
29886     draw : function()
29887     {
29888         this.previewEl.dom.innerHTML = '';
29889         
29890         var canvasEl = document.createElement("canvas");
29891         
29892         var contextEl = canvasEl.getContext("2d");
29893         
29894         canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
29895         canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
29896         var center = this.imageEl.OriginWidth / 2;
29897         
29898         if(this.imageEl.OriginWidth < this.imageEl.OriginHeight){
29899             canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
29900             canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
29901             center = this.imageEl.OriginHeight / 2;
29902         }
29903         
29904         contextEl.scale(this.getScaleLevel(), this.getScaleLevel());
29905         
29906         contextEl.translate(center, center);
29907         contextEl.rotate(this.rotate * Math.PI / 180);
29908
29909         contextEl.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
29910         
29911         this.canvasEl = document.createElement("canvas");
29912         
29913         this.contextEl = this.canvasEl.getContext("2d");
29914         
29915         switch (this.rotate) {
29916             case 0 :
29917                 
29918                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
29919                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
29920                 
29921                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
29922                 
29923                 break;
29924             case 90 : 
29925                 
29926                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
29927                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
29928                 
29929                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
29930                     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);
29931                     break;
29932                 }
29933                 
29934                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
29935                 
29936                 break;
29937             case 180 :
29938                 
29939                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
29940                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
29941                 
29942                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
29943                     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);
29944                     break;
29945                 }
29946                 
29947                 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);
29948                 
29949                 break;
29950             case 270 :
29951                 
29952                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
29953                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
29954         
29955                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
29956                     this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
29957                     break;
29958                 }
29959                 
29960                 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);
29961                 
29962                 break;
29963             default : 
29964                 break;
29965         }
29966         
29967         this.previewEl.appendChild(this.canvasEl);
29968         
29969         this.setCanvasPosition();
29970     },
29971     
29972     crop : function()
29973     {
29974         if(!this.canvasLoaded){
29975             return;
29976         }
29977         
29978         var imageCanvas = document.createElement("canvas");
29979         
29980         var imageContext = imageCanvas.getContext("2d");
29981         
29982         imageCanvas.width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
29983         imageCanvas.height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
29984         
29985         var center = imageCanvas.width / 2;
29986         
29987         imageContext.translate(center, center);
29988         
29989         imageContext.rotate(this.rotate * Math.PI / 180);
29990         
29991         imageContext.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
29992         
29993         var canvas = document.createElement("canvas");
29994         
29995         var context = canvas.getContext("2d");
29996                 
29997         canvas.width = this.minWidth;
29998         canvas.height = this.minHeight;
29999
30000         switch (this.rotate) {
30001             case 0 :
30002                 
30003                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
30004                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
30005                 
30006                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
30007                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
30008                 
30009                 var targetWidth = this.minWidth - 2 * x;
30010                 var targetHeight = this.minHeight - 2 * y;
30011                 
30012                 var scale = 1;
30013                 
30014                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
30015                     scale = targetWidth / width;
30016                 }
30017                 
30018                 if(x > 0 && y == 0){
30019                     scale = targetHeight / height;
30020                 }
30021                 
30022                 if(x > 0 && y > 0){
30023                     scale = targetWidth / width;
30024                     
30025                     if(width < height){
30026                         scale = targetHeight / height;
30027                     }
30028                 }
30029                 
30030                 context.scale(scale, scale);
30031                 
30032                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
30033                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
30034
30035                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
30036                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
30037
30038                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
30039                 
30040                 break;
30041             case 90 : 
30042                 
30043                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
30044                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
30045                 
30046                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
30047                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
30048                 
30049                 var targetWidth = this.minWidth - 2 * x;
30050                 var targetHeight = this.minHeight - 2 * y;
30051                 
30052                 var scale = 1;
30053                 
30054                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
30055                     scale = targetWidth / width;
30056                 }
30057                 
30058                 if(x > 0 && y == 0){
30059                     scale = targetHeight / height;
30060                 }
30061                 
30062                 if(x > 0 && y > 0){
30063                     scale = targetWidth / width;
30064                     
30065                     if(width < height){
30066                         scale = targetHeight / height;
30067                     }
30068                 }
30069                 
30070                 context.scale(scale, scale);
30071                 
30072                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
30073                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
30074
30075                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
30076                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
30077                 
30078                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
30079                 
30080                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
30081                 
30082                 break;
30083             case 180 :
30084                 
30085                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
30086                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
30087                 
30088                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
30089                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
30090                 
30091                 var targetWidth = this.minWidth - 2 * x;
30092                 var targetHeight = this.minHeight - 2 * y;
30093                 
30094                 var scale = 1;
30095                 
30096                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
30097                     scale = targetWidth / width;
30098                 }
30099                 
30100                 if(x > 0 && y == 0){
30101                     scale = targetHeight / height;
30102                 }
30103                 
30104                 if(x > 0 && y > 0){
30105                     scale = targetWidth / width;
30106                     
30107                     if(width < height){
30108                         scale = targetHeight / height;
30109                     }
30110                 }
30111                 
30112                 context.scale(scale, scale);
30113                 
30114                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
30115                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
30116
30117                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
30118                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
30119
30120                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
30121                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
30122                 
30123                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
30124                 
30125                 break;
30126             case 270 :
30127                 
30128                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
30129                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
30130                 
30131                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
30132                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
30133                 
30134                 var targetWidth = this.minWidth - 2 * x;
30135                 var targetHeight = this.minHeight - 2 * y;
30136                 
30137                 var scale = 1;
30138                 
30139                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
30140                     scale = targetWidth / width;
30141                 }
30142                 
30143                 if(x > 0 && y == 0){
30144                     scale = targetHeight / height;
30145                 }
30146                 
30147                 if(x > 0 && y > 0){
30148                     scale = targetWidth / width;
30149                     
30150                     if(width < height){
30151                         scale = targetHeight / height;
30152                     }
30153                 }
30154                 
30155                 context.scale(scale, scale);
30156                 
30157                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
30158                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
30159
30160                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
30161                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
30162                 
30163                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
30164                 
30165                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
30166                 
30167                 break;
30168             default : 
30169                 break;
30170         }
30171         
30172         this.cropData = canvas.toDataURL(this.cropType);
30173         
30174         if(this.fireEvent('crop', this, this.cropData) !== false){
30175             this.process(this.file, this.cropData);
30176         }
30177         
30178         return;
30179         
30180     },
30181     
30182     setThumbBoxSize : function()
30183     {
30184         var width, height;
30185         
30186         if(this.isDocument && typeof(this.imageEl) != 'undefined'){
30187             width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.max(this.minWidth, this.minHeight) : Math.min(this.minWidth, this.minHeight);
30188             height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.min(this.minWidth, this.minHeight) : Math.max(this.minWidth, this.minHeight);
30189             
30190             this.minWidth = width;
30191             this.minHeight = height;
30192             
30193             if(this.rotate == 90 || this.rotate == 270){
30194                 this.minWidth = height;
30195                 this.minHeight = width;
30196             }
30197         }
30198         
30199         height = 300;
30200         width = Math.ceil(this.minWidth * height / this.minHeight);
30201         
30202         if(this.minWidth > this.minHeight){
30203             width = 300;
30204             height = Math.ceil(this.minHeight * width / this.minWidth);
30205         }
30206         
30207         this.thumbEl.setStyle({
30208             width : width + 'px',
30209             height : height + 'px'
30210         });
30211
30212         return;
30213             
30214     },
30215     
30216     setThumbBoxPosition : function()
30217     {
30218         var x = Math.ceil((this.bodyEl.getWidth() - this.thumbEl.getWidth()) / 2 );
30219         var y = Math.ceil((this.bodyEl.getHeight() - this.thumbEl.getHeight()) / 2);
30220         
30221         this.thumbEl.setLeft(x);
30222         this.thumbEl.setTop(y);
30223         
30224     },
30225     
30226     baseRotateLevel : function()
30227     {
30228         this.baseRotate = 1;
30229         
30230         if(
30231                 typeof(this.exif) != 'undefined' &&
30232                 typeof(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != 'undefined' &&
30233                 [1, 3, 6, 8].indexOf(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != -1
30234         ){
30235             this.baseRotate = this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']];
30236         }
30237         
30238         this.rotate = Roo.bootstrap.UploadCropbox['Orientation'][this.baseRotate];
30239         
30240     },
30241     
30242     baseScaleLevel : function()
30243     {
30244         var width, height;
30245         
30246         if(this.isDocument){
30247             
30248             if(this.baseRotate == 6 || this.baseRotate == 8){
30249             
30250                 height = this.thumbEl.getHeight();
30251                 this.baseScale = height / this.imageEl.OriginWidth;
30252
30253                 if(this.imageEl.OriginHeight * this.baseScale > this.thumbEl.getWidth()){
30254                     width = this.thumbEl.getWidth();
30255                     this.baseScale = width / this.imageEl.OriginHeight;
30256                 }
30257
30258                 return;
30259             }
30260
30261             height = this.thumbEl.getHeight();
30262             this.baseScale = height / this.imageEl.OriginHeight;
30263
30264             if(this.imageEl.OriginWidth * this.baseScale > this.thumbEl.getWidth()){
30265                 width = this.thumbEl.getWidth();
30266                 this.baseScale = width / this.imageEl.OriginWidth;
30267             }
30268
30269             return;
30270         }
30271         
30272         if(this.baseRotate == 6 || this.baseRotate == 8){
30273             
30274             width = this.thumbEl.getHeight();
30275             this.baseScale = width / this.imageEl.OriginHeight;
30276             
30277             if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getWidth()){
30278                 height = this.thumbEl.getWidth();
30279                 this.baseScale = height / this.imageEl.OriginHeight;
30280             }
30281             
30282             if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
30283                 height = this.thumbEl.getWidth();
30284                 this.baseScale = height / this.imageEl.OriginHeight;
30285                 
30286                 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getHeight()){
30287                     width = this.thumbEl.getHeight();
30288                     this.baseScale = width / this.imageEl.OriginWidth;
30289                 }
30290             }
30291             
30292             return;
30293         }
30294         
30295         width = this.thumbEl.getWidth();
30296         this.baseScale = width / this.imageEl.OriginWidth;
30297         
30298         if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getHeight()){
30299             height = this.thumbEl.getHeight();
30300             this.baseScale = height / this.imageEl.OriginHeight;
30301         }
30302         
30303         if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
30304             
30305             height = this.thumbEl.getHeight();
30306             this.baseScale = height / this.imageEl.OriginHeight;
30307             
30308             if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getWidth()){
30309                 width = this.thumbEl.getWidth();
30310                 this.baseScale = width / this.imageEl.OriginWidth;
30311             }
30312             
30313         }
30314         
30315         return;
30316     },
30317     
30318     getScaleLevel : function()
30319     {
30320         return this.baseScale * Math.pow(1.1, this.scale);
30321     },
30322     
30323     onTouchStart : function(e)
30324     {
30325         if(!this.canvasLoaded){
30326             this.beforeSelectFile(e);
30327             return;
30328         }
30329         
30330         var touches = e.browserEvent.touches;
30331         
30332         if(!touches){
30333             return;
30334         }
30335         
30336         if(touches.length == 1){
30337             this.onMouseDown(e);
30338             return;
30339         }
30340         
30341         if(touches.length != 2){
30342             return;
30343         }
30344         
30345         var coords = [];
30346         
30347         for(var i = 0, finger; finger = touches[i]; i++){
30348             coords.push(finger.pageX, finger.pageY);
30349         }
30350         
30351         var x = Math.pow(coords[0] - coords[2], 2);
30352         var y = Math.pow(coords[1] - coords[3], 2);
30353         
30354         this.startDistance = Math.sqrt(x + y);
30355         
30356         this.startScale = this.scale;
30357         
30358         this.pinching = true;
30359         this.dragable = false;
30360         
30361     },
30362     
30363     onTouchMove : function(e)
30364     {
30365         if(!this.pinching && !this.dragable){
30366             return;
30367         }
30368         
30369         var touches = e.browserEvent.touches;
30370         
30371         if(!touches){
30372             return;
30373         }
30374         
30375         if(this.dragable){
30376             this.onMouseMove(e);
30377             return;
30378         }
30379         
30380         var coords = [];
30381         
30382         for(var i = 0, finger; finger = touches[i]; i++){
30383             coords.push(finger.pageX, finger.pageY);
30384         }
30385         
30386         var x = Math.pow(coords[0] - coords[2], 2);
30387         var y = Math.pow(coords[1] - coords[3], 2);
30388         
30389         this.endDistance = Math.sqrt(x + y);
30390         
30391         this.scale = this.startScale + Math.floor(Math.log(this.endDistance / this.startDistance) / Math.log(1.1));
30392         
30393         if(!this.zoomable()){
30394             this.scale = this.startScale;
30395             return;
30396         }
30397         
30398         this.draw();
30399         
30400     },
30401     
30402     onTouchEnd : function(e)
30403     {
30404         this.pinching = false;
30405         this.dragable = false;
30406         
30407     },
30408     
30409     process : function(file, crop)
30410     {
30411         if(this.loadMask){
30412             this.maskEl.mask(this.loadingText);
30413         }
30414         
30415         this.xhr = new XMLHttpRequest();
30416         
30417         file.xhr = this.xhr;
30418
30419         this.xhr.open(this.method, this.url, true);
30420         
30421         var headers = {
30422             "Accept": "application/json",
30423             "Cache-Control": "no-cache",
30424             "X-Requested-With": "XMLHttpRequest"
30425         };
30426         
30427         for (var headerName in headers) {
30428             var headerValue = headers[headerName];
30429             if (headerValue) {
30430                 this.xhr.setRequestHeader(headerName, headerValue);
30431             }
30432         }
30433         
30434         var _this = this;
30435         
30436         this.xhr.onload = function()
30437         {
30438             _this.xhrOnLoad(_this.xhr);
30439         }
30440         
30441         this.xhr.onerror = function()
30442         {
30443             _this.xhrOnError(_this.xhr);
30444         }
30445         
30446         var formData = new FormData();
30447
30448         formData.append('returnHTML', 'NO');
30449         
30450         if(crop){
30451             formData.append('crop', crop);
30452         }
30453         
30454         if(typeof(file) != 'undefined' && (typeof(file.id) == 'undefined' || file.id * 1 < 1)){
30455             formData.append(this.paramName, file, file.name);
30456         }
30457         
30458         if(typeof(file.filename) != 'undefined'){
30459             formData.append('filename', file.filename);
30460         }
30461         
30462         if(typeof(file.mimetype) != 'undefined'){
30463             formData.append('mimetype', file.mimetype);
30464         }
30465         
30466         if(this.fireEvent('arrange', this, formData) != false){
30467             this.xhr.send(formData);
30468         };
30469     },
30470     
30471     xhrOnLoad : function(xhr)
30472     {
30473         if(this.loadMask){
30474             this.maskEl.unmask();
30475         }
30476         
30477         if (xhr.readyState !== 4) {
30478             this.fireEvent('exception', this, xhr);
30479             return;
30480         }
30481
30482         var response = Roo.decode(xhr.responseText);
30483         
30484         if(!response.success){
30485             this.fireEvent('exception', this, xhr);
30486             return;
30487         }
30488         
30489         var response = Roo.decode(xhr.responseText);
30490         
30491         this.fireEvent('upload', this, response);
30492         
30493     },
30494     
30495     xhrOnError : function()
30496     {
30497         if(this.loadMask){
30498             this.maskEl.unmask();
30499         }
30500         
30501         Roo.log('xhr on error');
30502         
30503         var response = Roo.decode(xhr.responseText);
30504           
30505         Roo.log(response);
30506         
30507     },
30508     
30509     prepare : function(file)
30510     {   
30511         if(this.loadMask){
30512             this.maskEl.mask(this.loadingText);
30513         }
30514         
30515         this.file = false;
30516         this.exif = {};
30517         
30518         if(typeof(file) === 'string'){
30519             this.loadCanvas(file);
30520             return;
30521         }
30522         
30523         if(!file || !this.urlAPI){
30524             return;
30525         }
30526         
30527         this.file = file;
30528         this.cropType = file.type;
30529         
30530         var _this = this;
30531         
30532         if(this.fireEvent('prepare', this, this.file) != false){
30533             
30534             var reader = new FileReader();
30535             
30536             reader.onload = function (e) {
30537                 if (e.target.error) {
30538                     Roo.log(e.target.error);
30539                     return;
30540                 }
30541                 
30542                 var buffer = e.target.result,
30543                     dataView = new DataView(buffer),
30544                     offset = 2,
30545                     maxOffset = dataView.byteLength - 4,
30546                     markerBytes,
30547                     markerLength;
30548                 
30549                 if (dataView.getUint16(0) === 0xffd8) {
30550                     while (offset < maxOffset) {
30551                         markerBytes = dataView.getUint16(offset);
30552                         
30553                         if ((markerBytes >= 0xffe0 && markerBytes <= 0xffef) || markerBytes === 0xfffe) {
30554                             markerLength = dataView.getUint16(offset + 2) + 2;
30555                             if (offset + markerLength > dataView.byteLength) {
30556                                 Roo.log('Invalid meta data: Invalid segment size.');
30557                                 break;
30558                             }
30559                             
30560                             if(markerBytes == 0xffe1){
30561                                 _this.parseExifData(
30562                                     dataView,
30563                                     offset,
30564                                     markerLength
30565                                 );
30566                             }
30567                             
30568                             offset += markerLength;
30569                             
30570                             continue;
30571                         }
30572                         
30573                         break;
30574                     }
30575                     
30576                 }
30577                 
30578                 var url = _this.urlAPI.createObjectURL(_this.file);
30579                 
30580                 _this.loadCanvas(url);
30581                 
30582                 return;
30583             }
30584             
30585             reader.readAsArrayBuffer(this.file);
30586             
30587         }
30588         
30589     },
30590     
30591     parseExifData : function(dataView, offset, length)
30592     {
30593         var tiffOffset = offset + 10,
30594             littleEndian,
30595             dirOffset;
30596     
30597         if (dataView.getUint32(offset + 4) !== 0x45786966) {
30598             // No Exif data, might be XMP data instead
30599             return;
30600         }
30601         
30602         // Check for the ASCII code for "Exif" (0x45786966):
30603         if (dataView.getUint32(offset + 4) !== 0x45786966) {
30604             // No Exif data, might be XMP data instead
30605             return;
30606         }
30607         if (tiffOffset + 8 > dataView.byteLength) {
30608             Roo.log('Invalid Exif data: Invalid segment size.');
30609             return;
30610         }
30611         // Check for the two null bytes:
30612         if (dataView.getUint16(offset + 8) !== 0x0000) {
30613             Roo.log('Invalid Exif data: Missing byte alignment offset.');
30614             return;
30615         }
30616         // Check the byte alignment:
30617         switch (dataView.getUint16(tiffOffset)) {
30618         case 0x4949:
30619             littleEndian = true;
30620             break;
30621         case 0x4D4D:
30622             littleEndian = false;
30623             break;
30624         default:
30625             Roo.log('Invalid Exif data: Invalid byte alignment marker.');
30626             return;
30627         }
30628         // Check for the TIFF tag marker (0x002A):
30629         if (dataView.getUint16(tiffOffset + 2, littleEndian) !== 0x002A) {
30630             Roo.log('Invalid Exif data: Missing TIFF marker.');
30631             return;
30632         }
30633         // Retrieve the directory offset bytes, usually 0x00000008 or 8 decimal:
30634         dirOffset = dataView.getUint32(tiffOffset + 4, littleEndian);
30635         
30636         this.parseExifTags(
30637             dataView,
30638             tiffOffset,
30639             tiffOffset + dirOffset,
30640             littleEndian
30641         );
30642     },
30643     
30644     parseExifTags : function(dataView, tiffOffset, dirOffset, littleEndian)
30645     {
30646         var tagsNumber,
30647             dirEndOffset,
30648             i;
30649         if (dirOffset + 6 > dataView.byteLength) {
30650             Roo.log('Invalid Exif data: Invalid directory offset.');
30651             return;
30652         }
30653         tagsNumber = dataView.getUint16(dirOffset, littleEndian);
30654         dirEndOffset = dirOffset + 2 + 12 * tagsNumber;
30655         if (dirEndOffset + 4 > dataView.byteLength) {
30656             Roo.log('Invalid Exif data: Invalid directory size.');
30657             return;
30658         }
30659         for (i = 0; i < tagsNumber; i += 1) {
30660             this.parseExifTag(
30661                 dataView,
30662                 tiffOffset,
30663                 dirOffset + 2 + 12 * i, // tag offset
30664                 littleEndian
30665             );
30666         }
30667         // Return the offset to the next directory:
30668         return dataView.getUint32(dirEndOffset, littleEndian);
30669     },
30670     
30671     parseExifTag : function (dataView, tiffOffset, offset, littleEndian) 
30672     {
30673         var tag = dataView.getUint16(offset, littleEndian);
30674         
30675         this.exif[tag] = this.getExifValue(
30676             dataView,
30677             tiffOffset,
30678             offset,
30679             dataView.getUint16(offset + 2, littleEndian), // tag type
30680             dataView.getUint32(offset + 4, littleEndian), // tag length
30681             littleEndian
30682         );
30683     },
30684     
30685     getExifValue : function (dataView, tiffOffset, offset, type, length, littleEndian)
30686     {
30687         var tagType = Roo.bootstrap.UploadCropbox.exifTagTypes[type],
30688             tagSize,
30689             dataOffset,
30690             values,
30691             i,
30692             str,
30693             c;
30694     
30695         if (!tagType) {
30696             Roo.log('Invalid Exif data: Invalid tag type.');
30697             return;
30698         }
30699         
30700         tagSize = tagType.size * length;
30701         // Determine if the value is contained in the dataOffset bytes,
30702         // or if the value at the dataOffset is a pointer to the actual data:
30703         dataOffset = tagSize > 4 ?
30704                 tiffOffset + dataView.getUint32(offset + 8, littleEndian) : (offset + 8);
30705         if (dataOffset + tagSize > dataView.byteLength) {
30706             Roo.log('Invalid Exif data: Invalid data offset.');
30707             return;
30708         }
30709         if (length === 1) {
30710             return tagType.getValue(dataView, dataOffset, littleEndian);
30711         }
30712         values = [];
30713         for (i = 0; i < length; i += 1) {
30714             values[i] = tagType.getValue(dataView, dataOffset + i * tagType.size, littleEndian);
30715         }
30716         
30717         if (tagType.ascii) {
30718             str = '';
30719             // Concatenate the chars:
30720             for (i = 0; i < values.length; i += 1) {
30721                 c = values[i];
30722                 // Ignore the terminating NULL byte(s):
30723                 if (c === '\u0000') {
30724                     break;
30725                 }
30726                 str += c;
30727             }
30728             return str;
30729         }
30730         return values;
30731     }
30732     
30733 });
30734
30735 Roo.apply(Roo.bootstrap.UploadCropbox, {
30736     tags : {
30737         'Orientation': 0x0112
30738     },
30739     
30740     Orientation: {
30741             1: 0, //'top-left',
30742 //            2: 'top-right',
30743             3: 180, //'bottom-right',
30744 //            4: 'bottom-left',
30745 //            5: 'left-top',
30746             6: 90, //'right-top',
30747 //            7: 'right-bottom',
30748             8: 270 //'left-bottom'
30749     },
30750     
30751     exifTagTypes : {
30752         // byte, 8-bit unsigned int:
30753         1: {
30754             getValue: function (dataView, dataOffset) {
30755                 return dataView.getUint8(dataOffset);
30756             },
30757             size: 1
30758         },
30759         // ascii, 8-bit byte:
30760         2: {
30761             getValue: function (dataView, dataOffset) {
30762                 return String.fromCharCode(dataView.getUint8(dataOffset));
30763             },
30764             size: 1,
30765             ascii: true
30766         },
30767         // short, 16 bit int:
30768         3: {
30769             getValue: function (dataView, dataOffset, littleEndian) {
30770                 return dataView.getUint16(dataOffset, littleEndian);
30771             },
30772             size: 2
30773         },
30774         // long, 32 bit int:
30775         4: {
30776             getValue: function (dataView, dataOffset, littleEndian) {
30777                 return dataView.getUint32(dataOffset, littleEndian);
30778             },
30779             size: 4
30780         },
30781         // rational = two long values, first is numerator, second is denominator:
30782         5: {
30783             getValue: function (dataView, dataOffset, littleEndian) {
30784                 return dataView.getUint32(dataOffset, littleEndian) /
30785                     dataView.getUint32(dataOffset + 4, littleEndian);
30786             },
30787             size: 8
30788         },
30789         // slong, 32 bit signed int:
30790         9: {
30791             getValue: function (dataView, dataOffset, littleEndian) {
30792                 return dataView.getInt32(dataOffset, littleEndian);
30793             },
30794             size: 4
30795         },
30796         // srational, two slongs, first is numerator, second is denominator:
30797         10: {
30798             getValue: function (dataView, dataOffset, littleEndian) {
30799                 return dataView.getInt32(dataOffset, littleEndian) /
30800                     dataView.getInt32(dataOffset + 4, littleEndian);
30801             },
30802             size: 8
30803         }
30804     },
30805     
30806     footer : {
30807         STANDARD : [
30808             {
30809                 tag : 'div',
30810                 cls : 'btn-group roo-upload-cropbox-rotate-left',
30811                 action : 'rotate-left',
30812                 cn : [
30813                     {
30814                         tag : 'button',
30815                         cls : 'btn btn-default',
30816                         html : '<i class="fa fa-undo"></i>'
30817                     }
30818                 ]
30819             },
30820             {
30821                 tag : 'div',
30822                 cls : 'btn-group roo-upload-cropbox-picture',
30823                 action : 'picture',
30824                 cn : [
30825                     {
30826                         tag : 'button',
30827                         cls : 'btn btn-default',
30828                         html : '<i class="fa fa-picture-o"></i>'
30829                     }
30830                 ]
30831             },
30832             {
30833                 tag : 'div',
30834                 cls : 'btn-group roo-upload-cropbox-rotate-right',
30835                 action : 'rotate-right',
30836                 cn : [
30837                     {
30838                         tag : 'button',
30839                         cls : 'btn btn-default',
30840                         html : '<i class="fa fa-repeat"></i>'
30841                     }
30842                 ]
30843             }
30844         ],
30845         DOCUMENT : [
30846             {
30847                 tag : 'div',
30848                 cls : 'btn-group roo-upload-cropbox-rotate-left',
30849                 action : 'rotate-left',
30850                 cn : [
30851                     {
30852                         tag : 'button',
30853                         cls : 'btn btn-default',
30854                         html : '<i class="fa fa-undo"></i>'
30855                     }
30856                 ]
30857             },
30858             {
30859                 tag : 'div',
30860                 cls : 'btn-group roo-upload-cropbox-download',
30861                 action : 'download',
30862                 cn : [
30863                     {
30864                         tag : 'button',
30865                         cls : 'btn btn-default',
30866                         html : '<i class="fa fa-download"></i>'
30867                     }
30868                 ]
30869             },
30870             {
30871                 tag : 'div',
30872                 cls : 'btn-group roo-upload-cropbox-crop',
30873                 action : 'crop',
30874                 cn : [
30875                     {
30876                         tag : 'button',
30877                         cls : 'btn btn-default',
30878                         html : '<i class="fa fa-crop"></i>'
30879                     }
30880                 ]
30881             },
30882             {
30883                 tag : 'div',
30884                 cls : 'btn-group roo-upload-cropbox-trash',
30885                 action : 'trash',
30886                 cn : [
30887                     {
30888                         tag : 'button',
30889                         cls : 'btn btn-default',
30890                         html : '<i class="fa fa-trash"></i>'
30891                     }
30892                 ]
30893             },
30894             {
30895                 tag : 'div',
30896                 cls : 'btn-group roo-upload-cropbox-rotate-right',
30897                 action : 'rotate-right',
30898                 cn : [
30899                     {
30900                         tag : 'button',
30901                         cls : 'btn btn-default',
30902                         html : '<i class="fa fa-repeat"></i>'
30903                     }
30904                 ]
30905             }
30906         ],
30907         ROTATOR : [
30908             {
30909                 tag : 'div',
30910                 cls : 'btn-group roo-upload-cropbox-rotate-left',
30911                 action : 'rotate-left',
30912                 cn : [
30913                     {
30914                         tag : 'button',
30915                         cls : 'btn btn-default',
30916                         html : '<i class="fa fa-undo"></i>'
30917                     }
30918                 ]
30919             },
30920             {
30921                 tag : 'div',
30922                 cls : 'btn-group roo-upload-cropbox-rotate-right',
30923                 action : 'rotate-right',
30924                 cn : [
30925                     {
30926                         tag : 'button',
30927                         cls : 'btn btn-default',
30928                         html : '<i class="fa fa-repeat"></i>'
30929                     }
30930                 ]
30931             }
30932         ]
30933     }
30934 });
30935
30936 /*
30937 * Licence: LGPL
30938 */
30939
30940 /**
30941  * @class Roo.bootstrap.DocumentManager
30942  * @extends Roo.bootstrap.Component
30943  * Bootstrap DocumentManager class
30944  * @cfg {String} paramName default 'imageUpload'
30945  * @cfg {String} toolTipName default 'filename'
30946  * @cfg {String} method default POST
30947  * @cfg {String} url action url
30948  * @cfg {Number} boxes number of boxes, 0 is no limit.. default 0
30949  * @cfg {Boolean} multiple multiple upload default true
30950  * @cfg {Number} thumbSize default 300
30951  * @cfg {String} fieldLabel
30952  * @cfg {Number} labelWidth default 4
30953  * @cfg {String} labelAlign (left|top) default left
30954  * @cfg {Boolean} editable (true|false) allow edit when upload a image default true
30955 * @cfg {Number} labellg set the width of label (1-12)
30956  * @cfg {Number} labelmd set the width of label (1-12)
30957  * @cfg {Number} labelsm set the width of label (1-12)
30958  * @cfg {Number} labelxs set the width of label (1-12)
30959  * 
30960  * @constructor
30961  * Create a new DocumentManager
30962  * @param {Object} config The config object
30963  */
30964
30965 Roo.bootstrap.DocumentManager = function(config){
30966     Roo.bootstrap.DocumentManager.superclass.constructor.call(this, config);
30967     
30968     this.files = [];
30969     this.delegates = [];
30970     
30971     this.addEvents({
30972         /**
30973          * @event initial
30974          * Fire when initial the DocumentManager
30975          * @param {Roo.bootstrap.DocumentManager} this
30976          */
30977         "initial" : true,
30978         /**
30979          * @event inspect
30980          * inspect selected file
30981          * @param {Roo.bootstrap.DocumentManager} this
30982          * @param {File} file
30983          */
30984         "inspect" : true,
30985         /**
30986          * @event exception
30987          * Fire when xhr load exception
30988          * @param {Roo.bootstrap.DocumentManager} this
30989          * @param {XMLHttpRequest} xhr
30990          */
30991         "exception" : true,
30992         /**
30993          * @event afterupload
30994          * Fire when xhr load exception
30995          * @param {Roo.bootstrap.DocumentManager} this
30996          * @param {XMLHttpRequest} xhr
30997          */
30998         "afterupload" : true,
30999         /**
31000          * @event prepare
31001          * prepare the form data
31002          * @param {Roo.bootstrap.DocumentManager} this
31003          * @param {Object} formData
31004          */
31005         "prepare" : true,
31006         /**
31007          * @event remove
31008          * Fire when remove the file
31009          * @param {Roo.bootstrap.DocumentManager} this
31010          * @param {Object} file
31011          */
31012         "remove" : true,
31013         /**
31014          * @event refresh
31015          * Fire after refresh the file
31016          * @param {Roo.bootstrap.DocumentManager} this
31017          */
31018         "refresh" : true,
31019         /**
31020          * @event click
31021          * Fire after click the image
31022          * @param {Roo.bootstrap.DocumentManager} this
31023          * @param {Object} file
31024          */
31025         "click" : true,
31026         /**
31027          * @event edit
31028          * Fire when upload a image and editable set to true
31029          * @param {Roo.bootstrap.DocumentManager} this
31030          * @param {Object} file
31031          */
31032         "edit" : true,
31033         /**
31034          * @event beforeselectfile
31035          * Fire before select file
31036          * @param {Roo.bootstrap.DocumentManager} this
31037          */
31038         "beforeselectfile" : true,
31039         /**
31040          * @event process
31041          * Fire before process file
31042          * @param {Roo.bootstrap.DocumentManager} this
31043          * @param {Object} file
31044          */
31045         "process" : true,
31046         /**
31047          * @event previewrendered
31048          * Fire when preview rendered
31049          * @param {Roo.bootstrap.DocumentManager} this
31050          * @param {Object} file
31051          */
31052         "previewrendered" : true,
31053         /**
31054          */
31055         "previewResize" : true
31056         
31057     });
31058 };
31059
31060 Roo.extend(Roo.bootstrap.DocumentManager, Roo.bootstrap.Component,  {
31061     
31062     boxes : 0,
31063     inputName : '',
31064     thumbSize : 300,
31065     multiple : true,
31066     files : false,
31067     method : 'POST',
31068     url : '',
31069     paramName : 'imageUpload',
31070     toolTipName : 'filename',
31071     fieldLabel : '',
31072     labelWidth : 4,
31073     labelAlign : 'left',
31074     editable : true,
31075     delegates : false,
31076     xhr : false, 
31077     
31078     labellg : 0,
31079     labelmd : 0,
31080     labelsm : 0,
31081     labelxs : 0,
31082     
31083     getAutoCreate : function()
31084     {   
31085         var managerWidget = {
31086             tag : 'div',
31087             cls : 'roo-document-manager',
31088             cn : [
31089                 {
31090                     tag : 'input',
31091                     cls : 'roo-document-manager-selector',
31092                     type : 'file'
31093                 },
31094                 {
31095                     tag : 'div',
31096                     cls : 'roo-document-manager-uploader',
31097                     cn : [
31098                         {
31099                             tag : 'div',
31100                             cls : 'roo-document-manager-upload-btn',
31101                             html : '<i class="fa fa-plus"></i>'
31102                         }
31103                     ]
31104                     
31105                 }
31106             ]
31107         };
31108         
31109         var content = [
31110             {
31111                 tag : 'div',
31112                 cls : 'column col-md-12',
31113                 cn : managerWidget
31114             }
31115         ];
31116         
31117         if(this.fieldLabel.length){
31118             
31119             content = [
31120                 {
31121                     tag : 'div',
31122                     cls : 'column col-md-12',
31123                     html : this.fieldLabel
31124                 },
31125                 {
31126                     tag : 'div',
31127                     cls : 'column col-md-12',
31128                     cn : managerWidget
31129                 }
31130             ];
31131
31132             if(this.labelAlign == 'left'){
31133                 content = [
31134                     {
31135                         tag : 'div',
31136                         cls : 'column',
31137                         html : this.fieldLabel
31138                     },
31139                     {
31140                         tag : 'div',
31141                         cls : 'column',
31142                         cn : managerWidget
31143                     }
31144                 ];
31145                 
31146                 if(this.labelWidth > 12){
31147                     content[0].style = "width: " + this.labelWidth + 'px';
31148                 }
31149
31150                 if(this.labelWidth < 13 && this.labelmd == 0){
31151                     this.labelmd = this.labelWidth;
31152                 }
31153
31154                 if(this.labellg > 0){
31155                     content[0].cls += ' col-lg-' + this.labellg;
31156                     content[1].cls += ' col-lg-' + (12 - this.labellg);
31157                 }
31158
31159                 if(this.labelmd > 0){
31160                     content[0].cls += ' col-md-' + this.labelmd;
31161                     content[1].cls += ' col-md-' + (12 - this.labelmd);
31162                 }
31163
31164                 if(this.labelsm > 0){
31165                     content[0].cls += ' col-sm-' + this.labelsm;
31166                     content[1].cls += ' col-sm-' + (12 - this.labelsm);
31167                 }
31168
31169                 if(this.labelxs > 0){
31170                     content[0].cls += ' col-xs-' + this.labelxs;
31171                     content[1].cls += ' col-xs-' + (12 - this.labelxs);
31172                 }
31173                 
31174             }
31175         }
31176         
31177         var cfg = {
31178             tag : 'div',
31179             cls : 'row clearfix',
31180             cn : content
31181         };
31182         
31183         return cfg;
31184         
31185     },
31186     
31187     initEvents : function()
31188     {
31189         this.managerEl = this.el.select('.roo-document-manager', true).first();
31190         this.managerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
31191         
31192         this.selectorEl = this.el.select('.roo-document-manager-selector', true).first();
31193         this.selectorEl.hide();
31194         
31195         if(this.multiple){
31196             this.selectorEl.attr('multiple', 'multiple');
31197         }
31198         
31199         this.selectorEl.on('change', this.onFileSelected, this);
31200         
31201         this.uploader = this.el.select('.roo-document-manager-uploader', true).first();
31202         this.uploader.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
31203         
31204         this.uploader.on('click', this.onUploaderClick, this);
31205         
31206         this.renderProgressDialog();
31207         
31208         var _this = this;
31209         
31210         window.addEventListener("resize", function() { _this.refresh(); } );
31211         
31212         this.fireEvent('initial', this);
31213     },
31214     
31215     renderProgressDialog : function()
31216     {
31217         var _this = this;
31218         
31219         this.progressDialog = new Roo.bootstrap.Modal({
31220             cls : 'roo-document-manager-progress-dialog',
31221             allow_close : false,
31222             animate : false,
31223             title : '',
31224             buttons : [
31225                 {
31226                     name  :'cancel',
31227                     weight : 'danger',
31228                     html : 'Cancel'
31229                 }
31230             ], 
31231             listeners : { 
31232                 btnclick : function() {
31233                     _this.uploadCancel();
31234                     this.hide();
31235                 }
31236             }
31237         });
31238          
31239         this.progressDialog.render(Roo.get(document.body));
31240          
31241         this.progress = new Roo.bootstrap.Progress({
31242             cls : 'roo-document-manager-progress',
31243             active : true,
31244             striped : true
31245         });
31246         
31247         this.progress.render(this.progressDialog.getChildContainer());
31248         
31249         this.progressBar = new Roo.bootstrap.ProgressBar({
31250             cls : 'roo-document-manager-progress-bar',
31251             aria_valuenow : 0,
31252             aria_valuemin : 0,
31253             aria_valuemax : 12,
31254             panel : 'success'
31255         });
31256         
31257         this.progressBar.render(this.progress.getChildContainer());
31258     },
31259     
31260     onUploaderClick : function(e)
31261     {
31262         e.preventDefault();
31263      
31264         if(this.fireEvent('beforeselectfile', this) != false){
31265             this.selectorEl.dom.click();
31266         }
31267         
31268     },
31269     
31270     onFileSelected : function(e)
31271     {
31272         e.preventDefault();
31273         
31274         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
31275             return;
31276         }
31277         
31278         Roo.each(this.selectorEl.dom.files, function(file){
31279             if(this.fireEvent('inspect', this, file) != false){
31280                 this.files.push(file);
31281             }
31282         }, this);
31283         
31284         this.queue();
31285         
31286     },
31287     
31288     queue : function()
31289     {
31290         this.selectorEl.dom.value = '';
31291         
31292         if(!this.files || !this.files.length){
31293             return;
31294         }
31295         
31296         if(this.boxes > 0 && this.files.length > this.boxes){
31297             this.files = this.files.slice(0, this.boxes);
31298         }
31299         
31300         this.uploader.show();
31301         
31302         if(this.boxes > 0 && this.files.length > this.boxes - 1){
31303             this.uploader.hide();
31304         }
31305         
31306         var _this = this;
31307         
31308         var files = [];
31309         
31310         var docs = [];
31311         
31312         Roo.each(this.files, function(file){
31313             
31314             if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
31315                 var f = this.renderPreview(file);
31316                 files.push(f);
31317                 return;
31318             }
31319             
31320             if(file.type.indexOf('image') != -1){
31321                 this.delegates.push(
31322                     (function(){
31323                         _this.process(file);
31324                     }).createDelegate(this)
31325                 );
31326         
31327                 return;
31328             }
31329             
31330             docs.push(
31331                 (function(){
31332                     _this.process(file);
31333                 }).createDelegate(this)
31334             );
31335             
31336         }, this);
31337         
31338         this.files = files;
31339         
31340         this.delegates = this.delegates.concat(docs);
31341         
31342         if(!this.delegates.length){
31343             this.refresh();
31344             return;
31345         }
31346         
31347         this.progressBar.aria_valuemax = this.delegates.length;
31348         
31349         this.arrange();
31350         
31351         return;
31352     },
31353     
31354     arrange : function()
31355     {
31356         if(!this.delegates.length){
31357             this.progressDialog.hide();
31358             this.refresh();
31359             return;
31360         }
31361         
31362         var delegate = this.delegates.shift();
31363         
31364         this.progressDialog.show();
31365         
31366         this.progressDialog.setTitle((this.progressBar.aria_valuemax - this.delegates.length) + ' / ' + this.progressBar.aria_valuemax);
31367         
31368         this.progressBar.update(this.progressBar.aria_valuemax - this.delegates.length);
31369         
31370         delegate();
31371     },
31372     
31373     refresh : function()
31374     {
31375         this.uploader.show();
31376         
31377         if(this.boxes > 0 && this.files.length > this.boxes - 1){
31378             this.uploader.hide();
31379         }
31380         
31381         Roo.isTouch ? this.closable(false) : this.closable(true);
31382         
31383         this.fireEvent('refresh', this);
31384     },
31385     
31386     onRemove : function(e, el, o)
31387     {
31388         e.preventDefault();
31389         
31390         this.fireEvent('remove', this, o);
31391         
31392     },
31393     
31394     remove : function(o)
31395     {
31396         var files = [];
31397         
31398         Roo.each(this.files, function(file){
31399             if(typeof(file.id) == 'undefined' || file.id * 1 < 1 || file.id != o.id){
31400                 files.push(file);
31401                 return;
31402             }
31403
31404             o.target.remove();
31405
31406         }, this);
31407         
31408         this.files = files;
31409         
31410         this.refresh();
31411     },
31412     
31413     clear : function()
31414     {
31415         Roo.each(this.files, function(file){
31416             if(!file.target){
31417                 return;
31418             }
31419             
31420             file.target.remove();
31421
31422         }, this);
31423         
31424         this.files = [];
31425         
31426         this.refresh();
31427     },
31428     
31429     onClick : function(e, el, o)
31430     {
31431         e.preventDefault();
31432         
31433         this.fireEvent('click', this, o);
31434         
31435     },
31436     
31437     closable : function(closable)
31438     {
31439         Roo.each(this.managerEl.select('.roo-document-manager-preview > button.close', true).elements, function(el){
31440             
31441             el.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
31442             
31443             if(closable){
31444                 el.show();
31445                 return;
31446             }
31447             
31448             el.hide();
31449             
31450         }, this);
31451     },
31452     
31453     xhrOnLoad : function(xhr)
31454     {
31455         Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
31456             el.remove();
31457         }, this);
31458         
31459         if (xhr.readyState !== 4) {
31460             this.arrange();
31461             this.fireEvent('exception', this, xhr);
31462             return;
31463         }
31464
31465         var response = Roo.decode(xhr.responseText);
31466         
31467         if(!response.success){
31468             this.arrange();
31469             this.fireEvent('exception', this, xhr);
31470             return;
31471         }
31472         
31473         var file = this.renderPreview(response.data);
31474         
31475         this.files.push(file);
31476         
31477         this.arrange();
31478         
31479         this.fireEvent('afterupload', this, xhr);
31480         
31481     },
31482     
31483     xhrOnError : function(xhr)
31484     {
31485         Roo.log('xhr on error');
31486         
31487         var response = Roo.decode(xhr.responseText);
31488           
31489         Roo.log(response);
31490         
31491         this.arrange();
31492     },
31493     
31494     process : function(file)
31495     {
31496         if(this.fireEvent('process', this, file) !== false){
31497             if(this.editable && file.type.indexOf('image') != -1){
31498                 this.fireEvent('edit', this, file);
31499                 return;
31500             }
31501
31502             this.uploadStart(file, false);
31503
31504             return;
31505         }
31506         
31507     },
31508     
31509     uploadStart : function(file, crop)
31510     {
31511         this.xhr = new XMLHttpRequest();
31512         
31513         if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
31514             this.arrange();
31515             return;
31516         }
31517         
31518         file.xhr = this.xhr;
31519             
31520         this.managerEl.createChild({
31521             tag : 'div',
31522             cls : 'roo-document-manager-loading',
31523             cn : [
31524                 {
31525                     tag : 'div',
31526                     tooltip : file.name,
31527                     cls : 'roo-document-manager-thumb',
31528                     html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
31529                 }
31530             ]
31531
31532         });
31533
31534         this.xhr.open(this.method, this.url, true);
31535         
31536         var headers = {
31537             "Accept": "application/json",
31538             "Cache-Control": "no-cache",
31539             "X-Requested-With": "XMLHttpRequest"
31540         };
31541         
31542         for (var headerName in headers) {
31543             var headerValue = headers[headerName];
31544             if (headerValue) {
31545                 this.xhr.setRequestHeader(headerName, headerValue);
31546             }
31547         }
31548         
31549         var _this = this;
31550         
31551         this.xhr.onload = function()
31552         {
31553             _this.xhrOnLoad(_this.xhr);
31554         }
31555         
31556         this.xhr.onerror = function()
31557         {
31558             _this.xhrOnError(_this.xhr);
31559         }
31560         
31561         var formData = new FormData();
31562
31563         formData.append('returnHTML', 'NO');
31564         
31565         if(crop){
31566             formData.append('crop', crop);
31567         }
31568         
31569         formData.append(this.paramName, file, file.name);
31570         
31571         var options = {
31572             file : file, 
31573             manually : false
31574         };
31575         
31576         if(this.fireEvent('prepare', this, formData, options) != false){
31577             
31578             if(options.manually){
31579                 return;
31580             }
31581             
31582             this.xhr.send(formData);
31583             return;
31584         };
31585         
31586         this.uploadCancel();
31587     },
31588     
31589     uploadCancel : function()
31590     {
31591         if (this.xhr) {
31592             this.xhr.abort();
31593         }
31594         
31595         this.delegates = [];
31596         
31597         Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
31598             el.remove();
31599         }, this);
31600         
31601         this.arrange();
31602     },
31603     
31604     renderPreview : function(file)
31605     {
31606         if(typeof(file.target) != 'undefined' && file.target){
31607             return file;
31608         }
31609         
31610         var img_src = encodeURI(baseURL +'/Images/Thumb/' + this.thumbSize + '/' + file.id + '/' + file.filename);
31611         
31612         var previewEl = this.managerEl.createChild({
31613             tag : 'div',
31614             cls : 'roo-document-manager-preview',
31615             cn : [
31616                 {
31617                     tag : 'div',
31618                     tooltip : file[this.toolTipName],
31619                     cls : 'roo-document-manager-thumb',
31620                     html : '<img tooltip="' + file[this.toolTipName] + '" src="' + img_src + '">'
31621                 },
31622                 {
31623                     tag : 'button',
31624                     cls : 'close',
31625                     html : '<i class="fa fa-times-circle"></i>'
31626                 }
31627             ]
31628         });
31629
31630         var close = previewEl.select('button.close', true).first();
31631
31632         close.on('click', this.onRemove, this, file);
31633
31634         file.target = previewEl;
31635
31636         var image = previewEl.select('img', true).first();
31637         
31638         var _this = this;
31639         
31640         image.dom.addEventListener("load", function(){ _this.onPreviewLoad(file, image); });
31641         
31642         image.on('click', this.onClick, this, file);
31643         
31644         this.fireEvent('previewrendered', this, file);
31645         
31646         return file;
31647         
31648     },
31649     
31650     onPreviewLoad : function(file, image)
31651     {
31652         if(typeof(file.target) == 'undefined' || !file.target){
31653             return;
31654         }
31655         
31656         var width = image.dom.naturalWidth || image.dom.width;
31657         var height = image.dom.naturalHeight || image.dom.height;
31658         
31659         if(!this.previewResize) {
31660             return;
31661         }
31662         
31663         if(width > height){
31664             file.target.addClass('wide');
31665             return;
31666         }
31667         
31668         file.target.addClass('tall');
31669         return;
31670         
31671     },
31672     
31673     uploadFromSource : function(file, crop)
31674     {
31675         this.xhr = new XMLHttpRequest();
31676         
31677         this.managerEl.createChild({
31678             tag : 'div',
31679             cls : 'roo-document-manager-loading',
31680             cn : [
31681                 {
31682                     tag : 'div',
31683                     tooltip : file.name,
31684                     cls : 'roo-document-manager-thumb',
31685                     html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
31686                 }
31687             ]
31688
31689         });
31690
31691         this.xhr.open(this.method, this.url, true);
31692         
31693         var headers = {
31694             "Accept": "application/json",
31695             "Cache-Control": "no-cache",
31696             "X-Requested-With": "XMLHttpRequest"
31697         };
31698         
31699         for (var headerName in headers) {
31700             var headerValue = headers[headerName];
31701             if (headerValue) {
31702                 this.xhr.setRequestHeader(headerName, headerValue);
31703             }
31704         }
31705         
31706         var _this = this;
31707         
31708         this.xhr.onload = function()
31709         {
31710             _this.xhrOnLoad(_this.xhr);
31711         }
31712         
31713         this.xhr.onerror = function()
31714         {
31715             _this.xhrOnError(_this.xhr);
31716         }
31717         
31718         var formData = new FormData();
31719
31720         formData.append('returnHTML', 'NO');
31721         
31722         formData.append('crop', crop);
31723         
31724         if(typeof(file.filename) != 'undefined'){
31725             formData.append('filename', file.filename);
31726         }
31727         
31728         if(typeof(file.mimetype) != 'undefined'){
31729             formData.append('mimetype', file.mimetype);
31730         }
31731         
31732         Roo.log(formData);
31733         
31734         if(this.fireEvent('prepare', this, formData) != false){
31735             this.xhr.send(formData);
31736         };
31737     }
31738 });
31739
31740 /*
31741 * Licence: LGPL
31742 */
31743
31744 /**
31745  * @class Roo.bootstrap.DocumentViewer
31746  * @extends Roo.bootstrap.Component
31747  * Bootstrap DocumentViewer class
31748  * @cfg {Boolean} showDownload (true|false) show download button (default true)
31749  * @cfg {Boolean} showTrash (true|false) show trash button (default true)
31750  * 
31751  * @constructor
31752  * Create a new DocumentViewer
31753  * @param {Object} config The config object
31754  */
31755
31756 Roo.bootstrap.DocumentViewer = function(config){
31757     Roo.bootstrap.DocumentViewer.superclass.constructor.call(this, config);
31758     
31759     this.addEvents({
31760         /**
31761          * @event initial
31762          * Fire after initEvent
31763          * @param {Roo.bootstrap.DocumentViewer} this
31764          */
31765         "initial" : true,
31766         /**
31767          * @event click
31768          * Fire after click
31769          * @param {Roo.bootstrap.DocumentViewer} this
31770          */
31771         "click" : true,
31772         /**
31773          * @event download
31774          * Fire after download button
31775          * @param {Roo.bootstrap.DocumentViewer} this
31776          */
31777         "download" : true,
31778         /**
31779          * @event trash
31780          * Fire after trash button
31781          * @param {Roo.bootstrap.DocumentViewer} this
31782          */
31783         "trash" : true
31784         
31785     });
31786 };
31787
31788 Roo.extend(Roo.bootstrap.DocumentViewer, Roo.bootstrap.Component,  {
31789     
31790     showDownload : true,
31791     
31792     showTrash : true,
31793     
31794     getAutoCreate : function()
31795     {
31796         var cfg = {
31797             tag : 'div',
31798             cls : 'roo-document-viewer',
31799             cn : [
31800                 {
31801                     tag : 'div',
31802                     cls : 'roo-document-viewer-body',
31803                     cn : [
31804                         {
31805                             tag : 'div',
31806                             cls : 'roo-document-viewer-thumb',
31807                             cn : [
31808                                 {
31809                                     tag : 'img',
31810                                     cls : 'roo-document-viewer-image'
31811                                 }
31812                             ]
31813                         }
31814                     ]
31815                 },
31816                 {
31817                     tag : 'div',
31818                     cls : 'roo-document-viewer-footer',
31819                     cn : {
31820                         tag : 'div',
31821                         cls : 'btn-group btn-group-justified roo-document-viewer-btn-group',
31822                         cn : [
31823                             {
31824                                 tag : 'div',
31825                                 cls : 'btn-group roo-document-viewer-download',
31826                                 cn : [
31827                                     {
31828                                         tag : 'button',
31829                                         cls : 'btn btn-default',
31830                                         html : '<i class="fa fa-download"></i>'
31831                                     }
31832                                 ]
31833                             },
31834                             {
31835                                 tag : 'div',
31836                                 cls : 'btn-group roo-document-viewer-trash',
31837                                 cn : [
31838                                     {
31839                                         tag : 'button',
31840                                         cls : 'btn btn-default',
31841                                         html : '<i class="fa fa-trash"></i>'
31842                                     }
31843                                 ]
31844                             }
31845                         ]
31846                     }
31847                 }
31848             ]
31849         };
31850         
31851         return cfg;
31852     },
31853     
31854     initEvents : function()
31855     {
31856         this.bodyEl = this.el.select('.roo-document-viewer-body', true).first();
31857         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
31858         
31859         this.thumbEl = this.el.select('.roo-document-viewer-thumb', true).first();
31860         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
31861         
31862         this.imageEl = this.el.select('.roo-document-viewer-image', true).first();
31863         this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
31864         
31865         this.footerEl = this.el.select('.roo-document-viewer-footer', true).first();
31866         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY);
31867         
31868         this.downloadBtn = this.el.select('.roo-document-viewer-download', true).first();
31869         this.downloadBtn.setVisibilityMode(Roo.Element.DISPLAY);
31870         
31871         this.trashBtn = this.el.select('.roo-document-viewer-trash', true).first();
31872         this.trashBtn.setVisibilityMode(Roo.Element.DISPLAY);
31873         
31874         this.bodyEl.on('click', this.onClick, this);
31875         this.downloadBtn.on('click', this.onDownload, this);
31876         this.trashBtn.on('click', this.onTrash, this);
31877         
31878         this.downloadBtn.hide();
31879         this.trashBtn.hide();
31880         
31881         if(this.showDownload){
31882             this.downloadBtn.show();
31883         }
31884         
31885         if(this.showTrash){
31886             this.trashBtn.show();
31887         }
31888         
31889         if(!this.showDownload && !this.showTrash) {
31890             this.footerEl.hide();
31891         }
31892         
31893     },
31894     
31895     initial : function()
31896     {
31897         this.fireEvent('initial', this);
31898         
31899     },
31900     
31901     onClick : function(e)
31902     {
31903         e.preventDefault();
31904         
31905         this.fireEvent('click', this);
31906     },
31907     
31908     onDownload : function(e)
31909     {
31910         e.preventDefault();
31911         
31912         this.fireEvent('download', this);
31913     },
31914     
31915     onTrash : function(e)
31916     {
31917         e.preventDefault();
31918         
31919         this.fireEvent('trash', this);
31920     }
31921     
31922 });
31923 /*
31924  * - LGPL
31925  *
31926  * nav progress bar
31927  * 
31928  */
31929
31930 /**
31931  * @class Roo.bootstrap.NavProgressBar
31932  * @extends Roo.bootstrap.Component
31933  * Bootstrap NavProgressBar class
31934  * 
31935  * @constructor
31936  * Create a new nav progress bar
31937  * @param {Object} config The config object
31938  */
31939
31940 Roo.bootstrap.NavProgressBar = function(config){
31941     Roo.bootstrap.NavProgressBar.superclass.constructor.call(this, config);
31942
31943     this.bullets = this.bullets || [];
31944    
31945 //    Roo.bootstrap.NavProgressBar.register(this);
31946      this.addEvents({
31947         /**
31948              * @event changed
31949              * Fires when the active item changes
31950              * @param {Roo.bootstrap.NavProgressBar} this
31951              * @param {Roo.bootstrap.NavProgressItem} selected The item selected
31952              * @param {Roo.bootstrap.NavProgressItem} prev The previously selected item 
31953          */
31954         'changed': true
31955      });
31956     
31957 };
31958
31959 Roo.extend(Roo.bootstrap.NavProgressBar, Roo.bootstrap.Component,  {
31960     
31961     bullets : [],
31962     barItems : [],
31963     
31964     getAutoCreate : function()
31965     {
31966         var cfg = Roo.apply({}, Roo.bootstrap.NavProgressBar.superclass.getAutoCreate.call(this));
31967         
31968         cfg = {
31969             tag : 'div',
31970             cls : 'roo-navigation-bar-group',
31971             cn : [
31972                 {
31973                     tag : 'div',
31974                     cls : 'roo-navigation-top-bar'
31975                 },
31976                 {
31977                     tag : 'div',
31978                     cls : 'roo-navigation-bullets-bar',
31979                     cn : [
31980                         {
31981                             tag : 'ul',
31982                             cls : 'roo-navigation-bar'
31983                         }
31984                     ]
31985                 },
31986                 
31987                 {
31988                     tag : 'div',
31989                     cls : 'roo-navigation-bottom-bar'
31990                 }
31991             ]
31992             
31993         };
31994         
31995         return cfg;
31996         
31997     },
31998     
31999     initEvents: function() 
32000     {
32001         
32002     },
32003     
32004     onRender : function(ct, position) 
32005     {
32006         Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
32007         
32008         if(this.bullets.length){
32009             Roo.each(this.bullets, function(b){
32010                this.addItem(b);
32011             }, this);
32012         }
32013         
32014         this.format();
32015         
32016     },
32017     
32018     addItem : function(cfg)
32019     {
32020         var item = new Roo.bootstrap.NavProgressItem(cfg);
32021         
32022         item.parentId = this.id;
32023         item.render(this.el.select('.roo-navigation-bar', true).first(), null);
32024         
32025         if(cfg.html){
32026             var top = new Roo.bootstrap.Element({
32027                 tag : 'div',
32028                 cls : 'roo-navigation-bar-text'
32029             });
32030             
32031             var bottom = new Roo.bootstrap.Element({
32032                 tag : 'div',
32033                 cls : 'roo-navigation-bar-text'
32034             });
32035             
32036             top.onRender(this.el.select('.roo-navigation-top-bar', true).first(), null);
32037             bottom.onRender(this.el.select('.roo-navigation-bottom-bar', true).first(), null);
32038             
32039             var topText = new Roo.bootstrap.Element({
32040                 tag : 'span',
32041                 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? cfg.html : ''
32042             });
32043             
32044             var bottomText = new Roo.bootstrap.Element({
32045                 tag : 'span',
32046                 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? '' : cfg.html
32047             });
32048             
32049             topText.onRender(top.el, null);
32050             bottomText.onRender(bottom.el, null);
32051             
32052             item.topEl = top;
32053             item.bottomEl = bottom;
32054         }
32055         
32056         this.barItems.push(item);
32057         
32058         return item;
32059     },
32060     
32061     getActive : function()
32062     {
32063         var active = false;
32064         
32065         Roo.each(this.barItems, function(v){
32066             
32067             if (!v.isActive()) {
32068                 return;
32069             }
32070             
32071             active = v;
32072             return false;
32073             
32074         });
32075         
32076         return active;
32077     },
32078     
32079     setActiveItem : function(item)
32080     {
32081         var prev = false;
32082         
32083         Roo.each(this.barItems, function(v){
32084             if (v.rid == item.rid) {
32085                 return ;
32086             }
32087             
32088             if (v.isActive()) {
32089                 v.setActive(false);
32090                 prev = v;
32091             }
32092         });
32093
32094         item.setActive(true);
32095         
32096         this.fireEvent('changed', this, item, prev);
32097     },
32098     
32099     getBarItem: function(rid)
32100     {
32101         var ret = false;
32102         
32103         Roo.each(this.barItems, function(e) {
32104             if (e.rid != rid) {
32105                 return;
32106             }
32107             
32108             ret =  e;
32109             return false;
32110         });
32111         
32112         return ret;
32113     },
32114     
32115     indexOfItem : function(item)
32116     {
32117         var index = false;
32118         
32119         Roo.each(this.barItems, function(v, i){
32120             
32121             if (v.rid != item.rid) {
32122                 return;
32123             }
32124             
32125             index = i;
32126             return false
32127         });
32128         
32129         return index;
32130     },
32131     
32132     setActiveNext : function()
32133     {
32134         var i = this.indexOfItem(this.getActive());
32135         
32136         if (i > this.barItems.length) {
32137             return;
32138         }
32139         
32140         this.setActiveItem(this.barItems[i+1]);
32141     },
32142     
32143     setActivePrev : function()
32144     {
32145         var i = this.indexOfItem(this.getActive());
32146         
32147         if (i  < 1) {
32148             return;
32149         }
32150         
32151         this.setActiveItem(this.barItems[i-1]);
32152     },
32153     
32154     format : function()
32155     {
32156         if(!this.barItems.length){
32157             return;
32158         }
32159      
32160         var width = 100 / this.barItems.length;
32161         
32162         Roo.each(this.barItems, function(i){
32163             i.el.setStyle('width', width + '%');
32164             i.topEl.el.setStyle('width', width + '%');
32165             i.bottomEl.el.setStyle('width', width + '%');
32166         }, this);
32167         
32168     }
32169     
32170 });
32171 /*
32172  * - LGPL
32173  *
32174  * Nav Progress Item
32175  * 
32176  */
32177
32178 /**
32179  * @class Roo.bootstrap.NavProgressItem
32180  * @extends Roo.bootstrap.Component
32181  * Bootstrap NavProgressItem class
32182  * @cfg {String} rid the reference id
32183  * @cfg {Boolean} active (true|false) Is item active default false
32184  * @cfg {Boolean} disabled (true|false) Is item active default false
32185  * @cfg {String} html
32186  * @cfg {String} position (top|bottom) text position default bottom
32187  * @cfg {String} icon show icon instead of number
32188  * 
32189  * @constructor
32190  * Create a new NavProgressItem
32191  * @param {Object} config The config object
32192  */
32193 Roo.bootstrap.NavProgressItem = function(config){
32194     Roo.bootstrap.NavProgressItem.superclass.constructor.call(this, config);
32195     this.addEvents({
32196         // raw events
32197         /**
32198          * @event click
32199          * The raw click event for the entire grid.
32200          * @param {Roo.bootstrap.NavProgressItem} this
32201          * @param {Roo.EventObject} e
32202          */
32203         "click" : true
32204     });
32205    
32206 };
32207
32208 Roo.extend(Roo.bootstrap.NavProgressItem, Roo.bootstrap.Component,  {
32209     
32210     rid : '',
32211     active : false,
32212     disabled : false,
32213     html : '',
32214     position : 'bottom',
32215     icon : false,
32216     
32217     getAutoCreate : function()
32218     {
32219         var iconCls = 'roo-navigation-bar-item-icon';
32220         
32221         iconCls += ((this.icon) ? (' ' + this.icon) : (' step-number')) ;
32222         
32223         var cfg = {
32224             tag: 'li',
32225             cls: 'roo-navigation-bar-item',
32226             cn : [
32227                 {
32228                     tag : 'i',
32229                     cls : iconCls
32230                 }
32231             ]
32232         };
32233         
32234         if(this.active){
32235             cfg.cls += ' active';
32236         }
32237         if(this.disabled){
32238             cfg.cls += ' disabled';
32239         }
32240         
32241         return cfg;
32242     },
32243     
32244     disable : function()
32245     {
32246         this.setDisabled(true);
32247     },
32248     
32249     enable : function()
32250     {
32251         this.setDisabled(false);
32252     },
32253     
32254     initEvents: function() 
32255     {
32256         this.iconEl = this.el.select('.roo-navigation-bar-item-icon', true).first();
32257         
32258         this.iconEl.on('click', this.onClick, this);
32259     },
32260     
32261     onClick : function(e)
32262     {
32263         e.preventDefault();
32264         
32265         if(this.disabled){
32266             return;
32267         }
32268         
32269         if(this.fireEvent('click', this, e) === false){
32270             return;
32271         };
32272         
32273         this.parent().setActiveItem(this);
32274     },
32275     
32276     isActive: function () 
32277     {
32278         return this.active;
32279     },
32280     
32281     setActive : function(state)
32282     {
32283         if(this.active == state){
32284             return;
32285         }
32286         
32287         this.active = state;
32288         
32289         if (state) {
32290             this.el.addClass('active');
32291             return;
32292         }
32293         
32294         this.el.removeClass('active');
32295         
32296         return;
32297     },
32298     
32299     setDisabled : function(state)
32300     {
32301         if(this.disabled == state){
32302             return;
32303         }
32304         
32305         this.disabled = state;
32306         
32307         if (state) {
32308             this.el.addClass('disabled');
32309             return;
32310         }
32311         
32312         this.el.removeClass('disabled');
32313     },
32314     
32315     tooltipEl : function()
32316     {
32317         return this.el.select('.roo-navigation-bar-item-icon', true).first();;
32318     }
32319 });
32320  
32321
32322  /*
32323  * - LGPL
32324  *
32325  * FieldLabel
32326  * 
32327  */
32328
32329 /**
32330  * @class Roo.bootstrap.FieldLabel
32331  * @extends Roo.bootstrap.Component
32332  * Bootstrap FieldLabel class
32333  * @cfg {String} html contents of the element
32334  * @cfg {String} tag tag of the element default label
32335  * @cfg {String} cls class of the element
32336  * @cfg {String} target label target 
32337  * @cfg {Boolean} allowBlank (true|false) target allowBlank default true
32338  * @cfg {String} invalidClass DEPRICATED - BS4 uses is-invalid
32339  * @cfg {String} validClass DEPRICATED - BS4 uses is-valid
32340  * @cfg {String} iconTooltip default "This field is required"
32341  * @cfg {String} indicatorpos (left|right) default left
32342  * 
32343  * @constructor
32344  * Create a new FieldLabel
32345  * @param {Object} config The config object
32346  */
32347
32348 Roo.bootstrap.FieldLabel = function(config){
32349     Roo.bootstrap.Element.superclass.constructor.call(this, config);
32350     
32351     this.addEvents({
32352             /**
32353              * @event invalid
32354              * Fires after the field has been marked as invalid.
32355              * @param {Roo.form.FieldLabel} this
32356              * @param {String} msg The validation message
32357              */
32358             invalid : true,
32359             /**
32360              * @event valid
32361              * Fires after the field has been validated with no errors.
32362              * @param {Roo.form.FieldLabel} this
32363              */
32364             valid : true
32365         });
32366 };
32367
32368 Roo.extend(Roo.bootstrap.FieldLabel, Roo.bootstrap.Component,  {
32369     
32370     tag: 'label',
32371     cls: '',
32372     html: '',
32373     target: '',
32374     allowBlank : true,
32375     invalidClass : 'has-warning',
32376     validClass : 'has-success',
32377     iconTooltip : 'This field is required',
32378     indicatorpos : 'left',
32379     
32380     getAutoCreate : function(){
32381         
32382         var cls = "";
32383         if (!this.allowBlank) {
32384             cls  = "visible";
32385         }
32386         
32387         var cfg = {
32388             tag : this.tag,
32389             cls : 'roo-bootstrap-field-label ' + this.cls,
32390             for : this.target,
32391             cn : [
32392                 {
32393                     tag : 'i',
32394                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star ' + cls,
32395                     tooltip : this.iconTooltip
32396                 },
32397                 {
32398                     tag : 'span',
32399                     html : this.html
32400                 }
32401             ] 
32402         };
32403         
32404         if(this.indicatorpos == 'right'){
32405             var cfg = {
32406                 tag : this.tag,
32407                 cls : 'roo-bootstrap-field-label ' + this.cls,
32408                 for : this.target,
32409                 cn : [
32410                     {
32411                         tag : 'span',
32412                         html : this.html
32413                     },
32414                     {
32415                         tag : 'i',
32416                         cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star '+ cls,
32417                         tooltip : this.iconTooltip
32418                     }
32419                 ] 
32420             };
32421         }
32422         
32423         return cfg;
32424     },
32425     
32426     initEvents: function() 
32427     {
32428         Roo.bootstrap.Element.superclass.initEvents.call(this);
32429         
32430         this.indicator = this.indicatorEl();
32431         
32432         if(this.indicator){
32433             this.indicator.removeClass('visible');
32434             this.indicator.addClass('invisible');
32435         }
32436         
32437         Roo.bootstrap.FieldLabel.register(this);
32438     },
32439     
32440     indicatorEl : function()
32441     {
32442         var indicator = this.el.select('i.roo-required-indicator',true).first();
32443         
32444         if(!indicator){
32445             return false;
32446         }
32447         
32448         return indicator;
32449         
32450     },
32451     
32452     /**
32453      * Mark this field as valid
32454      */
32455     markValid : function()
32456     {
32457         if(this.indicator){
32458             this.indicator.removeClass('visible');
32459             this.indicator.addClass('invisible');
32460         }
32461         if (Roo.bootstrap.version == 3) {
32462             this.el.removeClass(this.invalidClass);
32463             this.el.addClass(this.validClass);
32464         } else {
32465             this.el.removeClass('is-invalid');
32466             this.el.addClass('is-valid');
32467         }
32468         
32469         
32470         this.fireEvent('valid', this);
32471     },
32472     
32473     /**
32474      * Mark this field as invalid
32475      * @param {String} msg The validation message
32476      */
32477     markInvalid : function(msg)
32478     {
32479         if(this.indicator){
32480             this.indicator.removeClass('invisible');
32481             this.indicator.addClass('visible');
32482         }
32483           if (Roo.bootstrap.version == 3) {
32484             this.el.removeClass(this.validClass);
32485             this.el.addClass(this.invalidClass);
32486         } else {
32487             this.el.removeClass('is-valid');
32488             this.el.addClass('is-invalid');
32489         }
32490         
32491         
32492         this.fireEvent('invalid', this, msg);
32493     }
32494     
32495    
32496 });
32497
32498 Roo.apply(Roo.bootstrap.FieldLabel, {
32499     
32500     groups: {},
32501     
32502      /**
32503     * register a FieldLabel Group
32504     * @param {Roo.bootstrap.FieldLabel} the FieldLabel to add
32505     */
32506     register : function(label)
32507     {
32508         if(this.groups.hasOwnProperty(label.target)){
32509             return;
32510         }
32511      
32512         this.groups[label.target] = label;
32513         
32514     },
32515     /**
32516     * fetch a FieldLabel Group based on the target
32517     * @param {string} target
32518     * @returns {Roo.bootstrap.FieldLabel} the CheckBox group
32519     */
32520     get: function(target) {
32521         if (typeof(this.groups[target]) == 'undefined') {
32522             return false;
32523         }
32524         
32525         return this.groups[target] ;
32526     }
32527 });
32528
32529  
32530
32531  /*
32532  * - LGPL
32533  *
32534  * page DateSplitField.
32535  * 
32536  */
32537
32538
32539 /**
32540  * @class Roo.bootstrap.DateSplitField
32541  * @extends Roo.bootstrap.Component
32542  * Bootstrap DateSplitField class
32543  * @cfg {string} fieldLabel - the label associated
32544  * @cfg {Number} labelWidth set the width of label (0-12)
32545  * @cfg {String} labelAlign (top|left)
32546  * @cfg {Boolean} dayAllowBlank (true|false) default false
32547  * @cfg {Boolean} monthAllowBlank (true|false) default false
32548  * @cfg {Boolean} yearAllowBlank (true|false) default false
32549  * @cfg {string} dayPlaceholder 
32550  * @cfg {string} monthPlaceholder
32551  * @cfg {string} yearPlaceholder
32552  * @cfg {string} dayFormat default 'd'
32553  * @cfg {string} monthFormat default 'm'
32554  * @cfg {string} yearFormat default 'Y'
32555  * @cfg {Number} labellg set the width of label (1-12)
32556  * @cfg {Number} labelmd set the width of label (1-12)
32557  * @cfg {Number} labelsm set the width of label (1-12)
32558  * @cfg {Number} labelxs set the width of label (1-12)
32559
32560  *     
32561  * @constructor
32562  * Create a new DateSplitField
32563  * @param {Object} config The config object
32564  */
32565
32566 Roo.bootstrap.DateSplitField = function(config){
32567     Roo.bootstrap.DateSplitField.superclass.constructor.call(this, config);
32568     
32569     this.addEvents({
32570         // raw events
32571          /**
32572          * @event years
32573          * getting the data of years
32574          * @param {Roo.bootstrap.DateSplitField} this
32575          * @param {Object} years
32576          */
32577         "years" : true,
32578         /**
32579          * @event days
32580          * getting the data of days
32581          * @param {Roo.bootstrap.DateSplitField} this
32582          * @param {Object} days
32583          */
32584         "days" : true,
32585         /**
32586          * @event invalid
32587          * Fires after the field has been marked as invalid.
32588          * @param {Roo.form.Field} this
32589          * @param {String} msg The validation message
32590          */
32591         invalid : true,
32592        /**
32593          * @event valid
32594          * Fires after the field has been validated with no errors.
32595          * @param {Roo.form.Field} this
32596          */
32597         valid : true
32598     });
32599 };
32600
32601 Roo.extend(Roo.bootstrap.DateSplitField, Roo.bootstrap.Component,  {
32602     
32603     fieldLabel : '',
32604     labelAlign : 'top',
32605     labelWidth : 3,
32606     dayAllowBlank : false,
32607     monthAllowBlank : false,
32608     yearAllowBlank : false,
32609     dayPlaceholder : '',
32610     monthPlaceholder : '',
32611     yearPlaceholder : '',
32612     dayFormat : 'd',
32613     monthFormat : 'm',
32614     yearFormat : 'Y',
32615     isFormField : true,
32616     labellg : 0,
32617     labelmd : 0,
32618     labelsm : 0,
32619     labelxs : 0,
32620     
32621     getAutoCreate : function()
32622     {
32623         var cfg = {
32624             tag : 'div',
32625             cls : 'row roo-date-split-field-group',
32626             cn : [
32627                 {
32628                     tag : 'input',
32629                     type : 'hidden',
32630                     cls : 'form-hidden-field roo-date-split-field-group-value',
32631                     name : this.name
32632                 }
32633             ]
32634         };
32635         
32636         var labelCls = 'col-md-12';
32637         var contentCls = 'col-md-4';
32638         
32639         if(this.fieldLabel){
32640             
32641             var label = {
32642                 tag : 'div',
32643                 cls : 'column roo-date-split-field-label col-md-' + ((this.labelAlign == 'top') ? '12' : this.labelWidth),
32644                 cn : [
32645                     {
32646                         tag : 'label',
32647                         html : this.fieldLabel
32648                     }
32649                 ]
32650             };
32651             
32652             if(this.labelAlign == 'left'){
32653             
32654                 if(this.labelWidth > 12){
32655                     label.style = "width: " + this.labelWidth + 'px';
32656                 }
32657
32658                 if(this.labelWidth < 13 && this.labelmd == 0){
32659                     this.labelmd = this.labelWidth;
32660                 }
32661
32662                 if(this.labellg > 0){
32663                     labelCls = ' col-lg-' + this.labellg;
32664                     contentCls = ' col-lg-' + ((12 - this.labellg) / 3);
32665                 }
32666
32667                 if(this.labelmd > 0){
32668                     labelCls = ' col-md-' + this.labelmd;
32669                     contentCls = ' col-md-' + ((12 - this.labelmd) / 3);
32670                 }
32671
32672                 if(this.labelsm > 0){
32673                     labelCls = ' col-sm-' + this.labelsm;
32674                     contentCls = ' col-sm-' + ((12 - this.labelsm) / 3);
32675                 }
32676
32677                 if(this.labelxs > 0){
32678                     labelCls = ' col-xs-' + this.labelxs;
32679                     contentCls = ' col-xs-' + ((12 - this.labelxs) / 3);
32680                 }
32681             }
32682             
32683             label.cls += ' ' + labelCls;
32684             
32685             cfg.cn.push(label);
32686         }
32687         
32688         Roo.each(['day', 'month', 'year'], function(t){
32689             cfg.cn.push({
32690                 tag : 'div',
32691                 cls : 'column roo-date-split-field-' + t + ' ' + contentCls
32692             });
32693         }, this);
32694         
32695         return cfg;
32696     },
32697     
32698     inputEl: function ()
32699     {
32700         return this.el.select('.roo-date-split-field-group-value', true).first();
32701     },
32702     
32703     onRender : function(ct, position) 
32704     {
32705         var _this = this;
32706         
32707         Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
32708         
32709         this.inputEl = this.el.select('.roo-date-split-field-group-value', true).first();
32710         
32711         this.dayField = new Roo.bootstrap.ComboBox({
32712             allowBlank : this.dayAllowBlank,
32713             alwaysQuery : true,
32714             displayField : 'value',
32715             editable : false,
32716             fieldLabel : '',
32717             forceSelection : true,
32718             mode : 'local',
32719             placeholder : this.dayPlaceholder,
32720             selectOnFocus : true,
32721             tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
32722             triggerAction : 'all',
32723             typeAhead : true,
32724             valueField : 'value',
32725             store : new Roo.data.SimpleStore({
32726                 data : (function() {    
32727                     var days = [];
32728                     _this.fireEvent('days', _this, days);
32729                     return days;
32730                 })(),
32731                 fields : [ 'value' ]
32732             }),
32733             listeners : {
32734                 select : function (_self, record, index)
32735                 {
32736                     _this.setValue(_this.getValue());
32737                 }
32738             }
32739         });
32740
32741         this.dayField.render(this.el.select('.roo-date-split-field-day', true).first(), null);
32742         
32743         this.monthField = new Roo.bootstrap.MonthField({
32744             after : '<i class=\"fa fa-calendar\"></i>',
32745             allowBlank : this.monthAllowBlank,
32746             placeholder : this.monthPlaceholder,
32747             readOnly : true,
32748             listeners : {
32749                 render : function (_self)
32750                 {
32751                     this.el.select('span.input-group-addon', true).first().on('click', function(e){
32752                         e.preventDefault();
32753                         _self.focus();
32754                     });
32755                 },
32756                 select : function (_self, oldvalue, newvalue)
32757                 {
32758                     _this.setValue(_this.getValue());
32759                 }
32760             }
32761         });
32762         
32763         this.monthField.render(this.el.select('.roo-date-split-field-month', true).first(), null);
32764         
32765         this.yearField = new Roo.bootstrap.ComboBox({
32766             allowBlank : this.yearAllowBlank,
32767             alwaysQuery : true,
32768             displayField : 'value',
32769             editable : false,
32770             fieldLabel : '',
32771             forceSelection : true,
32772             mode : 'local',
32773             placeholder : this.yearPlaceholder,
32774             selectOnFocus : true,
32775             tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
32776             triggerAction : 'all',
32777             typeAhead : true,
32778             valueField : 'value',
32779             store : new Roo.data.SimpleStore({
32780                 data : (function() {
32781                     var years = [];
32782                     _this.fireEvent('years', _this, years);
32783                     return years;
32784                 })(),
32785                 fields : [ 'value' ]
32786             }),
32787             listeners : {
32788                 select : function (_self, record, index)
32789                 {
32790                     _this.setValue(_this.getValue());
32791                 }
32792             }
32793         });
32794
32795         this.yearField.render(this.el.select('.roo-date-split-field-year', true).first(), null);
32796     },
32797     
32798     setValue : function(v, format)
32799     {
32800         this.inputEl.dom.value = v;
32801         
32802         var f = format || (this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat);
32803         
32804         var d = Date.parseDate(v, f);
32805         
32806         if(!d){
32807             this.validate();
32808             return;
32809         }
32810         
32811         this.setDay(d.format(this.dayFormat));
32812         this.setMonth(d.format(this.monthFormat));
32813         this.setYear(d.format(this.yearFormat));
32814         
32815         this.validate();
32816         
32817         return;
32818     },
32819     
32820     setDay : function(v)
32821     {
32822         this.dayField.setValue(v);
32823         this.inputEl.dom.value = this.getValue();
32824         this.validate();
32825         return;
32826     },
32827     
32828     setMonth : function(v)
32829     {
32830         this.monthField.setValue(v, true);
32831         this.inputEl.dom.value = this.getValue();
32832         this.validate();
32833         return;
32834     },
32835     
32836     setYear : function(v)
32837     {
32838         this.yearField.setValue(v);
32839         this.inputEl.dom.value = this.getValue();
32840         this.validate();
32841         return;
32842     },
32843     
32844     getDay : function()
32845     {
32846         return this.dayField.getValue();
32847     },
32848     
32849     getMonth : function()
32850     {
32851         return this.monthField.getValue();
32852     },
32853     
32854     getYear : function()
32855     {
32856         return this.yearField.getValue();
32857     },
32858     
32859     getValue : function()
32860     {
32861         var f = this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat;
32862         
32863         var date = this.yearField.getValue() + '-' + this.monthField.getValue() + '-' + this.dayField.getValue();
32864         
32865         return date;
32866     },
32867     
32868     reset : function()
32869     {
32870         this.setDay('');
32871         this.setMonth('');
32872         this.setYear('');
32873         this.inputEl.dom.value = '';
32874         this.validate();
32875         return;
32876     },
32877     
32878     validate : function()
32879     {
32880         var d = this.dayField.validate();
32881         var m = this.monthField.validate();
32882         var y = this.yearField.validate();
32883         
32884         var valid = true;
32885         
32886         if(
32887                 (!this.dayAllowBlank && !d) ||
32888                 (!this.monthAllowBlank && !m) ||
32889                 (!this.yearAllowBlank && !y)
32890         ){
32891             valid = false;
32892         }
32893         
32894         if(this.dayAllowBlank && this.monthAllowBlank && this.yearAllowBlank){
32895             return valid;
32896         }
32897         
32898         if(valid){
32899             this.markValid();
32900             return valid;
32901         }
32902         
32903         this.markInvalid();
32904         
32905         return valid;
32906     },
32907     
32908     markValid : function()
32909     {
32910         
32911         var label = this.el.select('label', true).first();
32912         var icon = this.el.select('i.fa-star', true).first();
32913
32914         if(label && icon){
32915             icon.remove();
32916         }
32917         
32918         this.fireEvent('valid', this);
32919     },
32920     
32921      /**
32922      * Mark this field as invalid
32923      * @param {String} msg The validation message
32924      */
32925     markInvalid : function(msg)
32926     {
32927         
32928         var label = this.el.select('label', true).first();
32929         var icon = this.el.select('i.fa-star', true).first();
32930
32931         if(label && !icon){
32932             this.el.select('.roo-date-split-field-label', true).createChild({
32933                 tag : 'i',
32934                 cls : 'text-danger fa fa-lg fa-star',
32935                 tooltip : 'This field is required',
32936                 style : 'margin-right:5px;'
32937             }, label, true);
32938         }
32939         
32940         this.fireEvent('invalid', this, msg);
32941     },
32942     
32943     clearInvalid : function()
32944     {
32945         var label = this.el.select('label', true).first();
32946         var icon = this.el.select('i.fa-star', true).first();
32947
32948         if(label && icon){
32949             icon.remove();
32950         }
32951         
32952         this.fireEvent('valid', this);
32953     },
32954     
32955     getName: function()
32956     {
32957         return this.name;
32958     }
32959     
32960 });
32961
32962  /**
32963  *
32964  * This is based on 
32965  * http://masonry.desandro.com
32966  *
32967  * The idea is to render all the bricks based on vertical width...
32968  *
32969  * The original code extends 'outlayer' - we might need to use that....
32970  * 
32971  */
32972
32973
32974 /**
32975  * @class Roo.bootstrap.LayoutMasonry
32976  * @extends Roo.bootstrap.Component
32977  * Bootstrap Layout Masonry class
32978  * 
32979  * @constructor
32980  * Create a new Element
32981  * @param {Object} config The config object
32982  */
32983
32984 Roo.bootstrap.LayoutMasonry = function(config){
32985     
32986     Roo.bootstrap.LayoutMasonry.superclass.constructor.call(this, config);
32987     
32988     this.bricks = [];
32989     
32990     Roo.bootstrap.LayoutMasonry.register(this);
32991     
32992     this.addEvents({
32993         // raw events
32994         /**
32995          * @event layout
32996          * Fire after layout the items
32997          * @param {Roo.bootstrap.LayoutMasonry} this
32998          * @param {Roo.EventObject} e
32999          */
33000         "layout" : true
33001     });
33002     
33003 };
33004
33005 Roo.extend(Roo.bootstrap.LayoutMasonry, Roo.bootstrap.Component,  {
33006     
33007     /**
33008      * @cfg {Boolean} isLayoutInstant = no animation?
33009      */   
33010     isLayoutInstant : false, // needed?
33011    
33012     /**
33013      * @cfg {Number} boxWidth  width of the columns
33014      */   
33015     boxWidth : 450,
33016     
33017       /**
33018      * @cfg {Number} boxHeight  - 0 for square, or fix it at a certian height
33019      */   
33020     boxHeight : 0,
33021     
33022     /**
33023      * @cfg {Number} padWidth padding below box..
33024      */   
33025     padWidth : 10, 
33026     
33027     /**
33028      * @cfg {Number} gutter gutter width..
33029      */   
33030     gutter : 10,
33031     
33032      /**
33033      * @cfg {Number} maxCols maximum number of columns
33034      */   
33035     
33036     maxCols: 0,
33037     
33038     /**
33039      * @cfg {Boolean} isAutoInitial defalut true
33040      */   
33041     isAutoInitial : true, 
33042     
33043     containerWidth: 0,
33044     
33045     /**
33046      * @cfg {Boolean} isHorizontal defalut false
33047      */   
33048     isHorizontal : false, 
33049
33050     currentSize : null,
33051     
33052     tag: 'div',
33053     
33054     cls: '',
33055     
33056     bricks: null, //CompositeElement
33057     
33058     cols : 1,
33059     
33060     _isLayoutInited : false,
33061     
33062 //    isAlternative : false, // only use for vertical layout...
33063     
33064     /**
33065      * @cfg {Number} alternativePadWidth padding below box..
33066      */   
33067     alternativePadWidth : 50,
33068     
33069     selectedBrick : [],
33070     
33071     getAutoCreate : function(){
33072         
33073         var cfg = Roo.apply({}, Roo.bootstrap.LayoutMasonry.superclass.getAutoCreate.call(this));
33074         
33075         var cfg = {
33076             tag: this.tag,
33077             cls: 'blog-masonary-wrapper ' + this.cls,
33078             cn : {
33079                 cls : 'mas-boxes masonary'
33080             }
33081         };
33082         
33083         return cfg;
33084     },
33085     
33086     getChildContainer: function( )
33087     {
33088         if (this.boxesEl) {
33089             return this.boxesEl;
33090         }
33091         
33092         this.boxesEl = this.el.select('.mas-boxes').first();
33093         
33094         return this.boxesEl;
33095     },
33096     
33097     
33098     initEvents : function()
33099     {
33100         var _this = this;
33101         
33102         if(this.isAutoInitial){
33103             Roo.log('hook children rendered');
33104             this.on('childrenrendered', function() {
33105                 Roo.log('children rendered');
33106                 _this.initial();
33107             } ,this);
33108         }
33109     },
33110     
33111     initial : function()
33112     {
33113         this.selectedBrick = [];
33114         
33115         this.currentSize = this.el.getBox(true);
33116         
33117         Roo.EventManager.onWindowResize(this.resize, this); 
33118
33119         if(!this.isAutoInitial){
33120             this.layout();
33121             return;
33122         }
33123         
33124         this.layout();
33125         
33126         return;
33127         //this.layout.defer(500,this);
33128         
33129     },
33130     
33131     resize : function()
33132     {
33133         var cs = this.el.getBox(true);
33134         
33135         if (
33136                 this.currentSize.width == cs.width && 
33137                 this.currentSize.x == cs.x && 
33138                 this.currentSize.height == cs.height && 
33139                 this.currentSize.y == cs.y 
33140         ) {
33141             Roo.log("no change in with or X or Y");
33142             return;
33143         }
33144         
33145         this.currentSize = cs;
33146         
33147         this.layout();
33148         
33149     },
33150     
33151     layout : function()
33152     {   
33153         this._resetLayout();
33154         
33155         var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
33156         
33157         this.layoutItems( isInstant );
33158       
33159         this._isLayoutInited = true;
33160         
33161         this.fireEvent('layout', this);
33162         
33163     },
33164     
33165     _resetLayout : function()
33166     {
33167         if(this.isHorizontal){
33168             this.horizontalMeasureColumns();
33169             return;
33170         }
33171         
33172         this.verticalMeasureColumns();
33173         
33174     },
33175     
33176     verticalMeasureColumns : function()
33177     {
33178         this.getContainerWidth();
33179         
33180 //        if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
33181 //            this.colWidth = Math.floor(this.containerWidth * 0.8);
33182 //            return;
33183 //        }
33184         
33185         var boxWidth = this.boxWidth + this.padWidth;
33186         
33187         if(this.containerWidth < this.boxWidth){
33188             boxWidth = this.containerWidth
33189         }
33190         
33191         var containerWidth = this.containerWidth;
33192         
33193         var cols = Math.floor(containerWidth / boxWidth);
33194         
33195         this.cols = Math.max( cols, 1 );
33196         
33197         this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
33198         
33199         var totalBoxWidth = this.cols * boxWidth - this.padWidth;
33200         
33201         var avail = Math.floor((containerWidth - totalBoxWidth) / this.cols);
33202         
33203         this.colWidth = boxWidth + avail - this.padWidth;
33204         
33205         this.unitWidth = Math.round((this.colWidth - (this.gutter * 2)) / 3);
33206         this.unitHeight = this.boxHeight > 0 ? this.boxHeight  : this.unitWidth;
33207     },
33208     
33209     horizontalMeasureColumns : function()
33210     {
33211         this.getContainerWidth();
33212         
33213         var boxWidth = this.boxWidth;
33214         
33215         if(this.containerWidth < boxWidth){
33216             boxWidth = this.containerWidth;
33217         }
33218         
33219         this.unitWidth = Math.floor((boxWidth - (this.gutter * 2)) / 3);
33220         
33221         this.el.setHeight(boxWidth);
33222         
33223     },
33224     
33225     getContainerWidth : function()
33226     {
33227         this.containerWidth = this.el.getBox(true).width;  //maybe use getComputedWidth
33228     },
33229     
33230     layoutItems : function( isInstant )
33231     {
33232         Roo.log(this.bricks);
33233         
33234         var items = Roo.apply([], this.bricks);
33235         
33236         if(this.isHorizontal){
33237             this._horizontalLayoutItems( items , isInstant );
33238             return;
33239         }
33240         
33241 //        if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
33242 //            this._verticalAlternativeLayoutItems( items , isInstant );
33243 //            return;
33244 //        }
33245         
33246         this._verticalLayoutItems( items , isInstant );
33247         
33248     },
33249     
33250     _verticalLayoutItems : function ( items , isInstant)
33251     {
33252         if ( !items || !items.length ) {
33253             return;
33254         }
33255         
33256         var standard = [
33257             ['xs', 'xs', 'xs', 'tall'],
33258             ['xs', 'xs', 'tall'],
33259             ['xs', 'xs', 'sm'],
33260             ['xs', 'xs', 'xs'],
33261             ['xs', 'tall'],
33262             ['xs', 'sm'],
33263             ['xs', 'xs'],
33264             ['xs'],
33265             
33266             ['sm', 'xs', 'xs'],
33267             ['sm', 'xs'],
33268             ['sm'],
33269             
33270             ['tall', 'xs', 'xs', 'xs'],
33271             ['tall', 'xs', 'xs'],
33272             ['tall', 'xs'],
33273             ['tall']
33274             
33275         ];
33276         
33277         var queue = [];
33278         
33279         var boxes = [];
33280         
33281         var box = [];
33282         
33283         Roo.each(items, function(item, k){
33284             
33285             switch (item.size) {
33286                 // these layouts take up a full box,
33287                 case 'md' :
33288                 case 'md-left' :
33289                 case 'md-right' :
33290                 case 'wide' :
33291                     
33292                     if(box.length){
33293                         boxes.push(box);
33294                         box = [];
33295                     }
33296                     
33297                     boxes.push([item]);
33298                     
33299                     break;
33300                     
33301                 case 'xs' :
33302                 case 'sm' :
33303                 case 'tall' :
33304                     
33305                     box.push(item);
33306                     
33307                     break;
33308                 default :
33309                     break;
33310                     
33311             }
33312             
33313         }, this);
33314         
33315         if(box.length){
33316             boxes.push(box);
33317             box = [];
33318         }
33319         
33320         var filterPattern = function(box, length)
33321         {
33322             if(!box.length){
33323                 return;
33324             }
33325             
33326             var match = false;
33327             
33328             var pattern = box.slice(0, length);
33329             
33330             var format = [];
33331             
33332             Roo.each(pattern, function(i){
33333                 format.push(i.size);
33334             }, this);
33335             
33336             Roo.each(standard, function(s){
33337                 
33338                 if(String(s) != String(format)){
33339                     return;
33340                 }
33341                 
33342                 match = true;
33343                 return false;
33344                 
33345             }, this);
33346             
33347             if(!match && length == 1){
33348                 return;
33349             }
33350             
33351             if(!match){
33352                 filterPattern(box, length - 1);
33353                 return;
33354             }
33355                 
33356             queue.push(pattern);
33357
33358             box = box.slice(length, box.length);
33359
33360             filterPattern(box, 4);
33361
33362             return;
33363             
33364         }
33365         
33366         Roo.each(boxes, function(box, k){
33367             
33368             if(!box.length){
33369                 return;
33370             }
33371             
33372             if(box.length == 1){
33373                 queue.push(box);
33374                 return;
33375             }
33376             
33377             filterPattern(box, 4);
33378             
33379         }, this);
33380         
33381         this._processVerticalLayoutQueue( queue, isInstant );
33382         
33383     },
33384     
33385 //    _verticalAlternativeLayoutItems : function( items , isInstant )
33386 //    {
33387 //        if ( !items || !items.length ) {
33388 //            return;
33389 //        }
33390 //
33391 //        this._processVerticalAlternativeLayoutQueue( items, isInstant );
33392 //        
33393 //    },
33394     
33395     _horizontalLayoutItems : function ( items , isInstant)
33396     {
33397         if ( !items || !items.length || items.length < 3) {
33398             return;
33399         }
33400         
33401         items.reverse();
33402         
33403         var eItems = items.slice(0, 3);
33404         
33405         items = items.slice(3, items.length);
33406         
33407         var standard = [
33408             ['xs', 'xs', 'xs', 'wide'],
33409             ['xs', 'xs', 'wide'],
33410             ['xs', 'xs', 'sm'],
33411             ['xs', 'xs', 'xs'],
33412             ['xs', 'wide'],
33413             ['xs', 'sm'],
33414             ['xs', 'xs'],
33415             ['xs'],
33416             
33417             ['sm', 'xs', 'xs'],
33418             ['sm', 'xs'],
33419             ['sm'],
33420             
33421             ['wide', 'xs', 'xs', 'xs'],
33422             ['wide', 'xs', 'xs'],
33423             ['wide', 'xs'],
33424             ['wide'],
33425             
33426             ['wide-thin']
33427         ];
33428         
33429         var queue = [];
33430         
33431         var boxes = [];
33432         
33433         var box = [];
33434         
33435         Roo.each(items, function(item, k){
33436             
33437             switch (item.size) {
33438                 case 'md' :
33439                 case 'md-left' :
33440                 case 'md-right' :
33441                 case 'tall' :
33442                     
33443                     if(box.length){
33444                         boxes.push(box);
33445                         box = [];
33446                     }
33447                     
33448                     boxes.push([item]);
33449                     
33450                     break;
33451                     
33452                 case 'xs' :
33453                 case 'sm' :
33454                 case 'wide' :
33455                 case 'wide-thin' :
33456                     
33457                     box.push(item);
33458                     
33459                     break;
33460                 default :
33461                     break;
33462                     
33463             }
33464             
33465         }, this);
33466         
33467         if(box.length){
33468             boxes.push(box);
33469             box = [];
33470         }
33471         
33472         var filterPattern = function(box, length)
33473         {
33474             if(!box.length){
33475                 return;
33476             }
33477             
33478             var match = false;
33479             
33480             var pattern = box.slice(0, length);
33481             
33482             var format = [];
33483             
33484             Roo.each(pattern, function(i){
33485                 format.push(i.size);
33486             }, this);
33487             
33488             Roo.each(standard, function(s){
33489                 
33490                 if(String(s) != String(format)){
33491                     return;
33492                 }
33493                 
33494                 match = true;
33495                 return false;
33496                 
33497             }, this);
33498             
33499             if(!match && length == 1){
33500                 return;
33501             }
33502             
33503             if(!match){
33504                 filterPattern(box, length - 1);
33505                 return;
33506             }
33507                 
33508             queue.push(pattern);
33509
33510             box = box.slice(length, box.length);
33511
33512             filterPattern(box, 4);
33513
33514             return;
33515             
33516         }
33517         
33518         Roo.each(boxes, function(box, k){
33519             
33520             if(!box.length){
33521                 return;
33522             }
33523             
33524             if(box.length == 1){
33525                 queue.push(box);
33526                 return;
33527             }
33528             
33529             filterPattern(box, 4);
33530             
33531         }, this);
33532         
33533         
33534         var prune = [];
33535         
33536         var pos = this.el.getBox(true);
33537         
33538         var minX = pos.x;
33539         
33540         var maxX = pos.right - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
33541         
33542         var hit_end = false;
33543         
33544         Roo.each(queue, function(box){
33545             
33546             if(hit_end){
33547                 
33548                 Roo.each(box, function(b){
33549                 
33550                     b.el.setVisibilityMode(Roo.Element.DISPLAY);
33551                     b.el.hide();
33552
33553                 }, this);
33554
33555                 return;
33556             }
33557             
33558             var mx = 0;
33559             
33560             Roo.each(box, function(b){
33561                 
33562                 b.el.setVisibilityMode(Roo.Element.DISPLAY);
33563                 b.el.show();
33564
33565                 mx = Math.max(mx, b.x);
33566                 
33567             }, this);
33568             
33569             maxX = maxX - this.unitWidth * mx - this.gutter * (mx - 1) - this.padWidth;
33570             
33571             if(maxX < minX){
33572                 
33573                 Roo.each(box, function(b){
33574                 
33575                     b.el.setVisibilityMode(Roo.Element.DISPLAY);
33576                     b.el.hide();
33577                     
33578                 }, this);
33579                 
33580                 hit_end = true;
33581                 
33582                 return;
33583             }
33584             
33585             prune.push(box);
33586             
33587         }, this);
33588         
33589         this._processHorizontalLayoutQueue( prune, eItems, isInstant );
33590     },
33591     
33592     /** Sets position of item in DOM
33593     * @param {Element} item
33594     * @param {Number} x - horizontal position
33595     * @param {Number} y - vertical position
33596     * @param {Boolean} isInstant - disables transitions
33597     */
33598     _processVerticalLayoutQueue : function( queue, isInstant )
33599     {
33600         var pos = this.el.getBox(true);
33601         var x = pos.x;
33602         var y = pos.y;
33603         var maxY = [];
33604         
33605         for (var i = 0; i < this.cols; i++){
33606             maxY[i] = pos.y;
33607         }
33608         
33609         Roo.each(queue, function(box, k){
33610             
33611             var col = k % this.cols;
33612             
33613             Roo.each(box, function(b,kk){
33614                 
33615                 b.el.position('absolute');
33616                 
33617                 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
33618                 var height = Math.floor(this.unitHeight * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
33619                 
33620                 if(b.size == 'md-left' || b.size == 'md-right'){
33621                     width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
33622                     height = Math.floor(this.unitHeight * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
33623                 }
33624                 
33625                 b.el.setWidth(width);
33626                 b.el.setHeight(height);
33627                 // iframe?
33628                 b.el.select('iframe',true).setSize(width,height);
33629                 
33630             }, this);
33631             
33632             for (var i = 0; i < this.cols; i++){
33633                 
33634                 if(maxY[i] < maxY[col]){
33635                     col = i;
33636                     continue;
33637                 }
33638                 
33639                 col = Math.min(col, i);
33640                 
33641             }
33642             
33643             x = pos.x + col * (this.colWidth + this.padWidth);
33644             
33645             y = maxY[col];
33646             
33647             var positions = [];
33648             
33649             switch (box.length){
33650                 case 1 :
33651                     positions = this.getVerticalOneBoxColPositions(x, y, box);
33652                     break;
33653                 case 2 :
33654                     positions = this.getVerticalTwoBoxColPositions(x, y, box);
33655                     break;
33656                 case 3 :
33657                     positions = this.getVerticalThreeBoxColPositions(x, y, box);
33658                     break;
33659                 case 4 :
33660                     positions = this.getVerticalFourBoxColPositions(x, y, box);
33661                     break;
33662                 default :
33663                     break;
33664             }
33665             
33666             Roo.each(box, function(b,kk){
33667                 
33668                 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
33669                 
33670                 var sz = b.el.getSize();
33671                 
33672                 maxY[col] = Math.max(maxY[col], positions[kk].y + sz.height + this.padWidth);
33673                 
33674             }, this);
33675             
33676         }, this);
33677         
33678         var mY = 0;
33679         
33680         for (var i = 0; i < this.cols; i++){
33681             mY = Math.max(mY, maxY[i]);
33682         }
33683         
33684         this.el.setHeight(mY - pos.y);
33685         
33686     },
33687     
33688 //    _processVerticalAlternativeLayoutQueue : function( items, isInstant )
33689 //    {
33690 //        var pos = this.el.getBox(true);
33691 //        var x = pos.x;
33692 //        var y = pos.y;
33693 //        var maxX = pos.right;
33694 //        
33695 //        var maxHeight = 0;
33696 //        
33697 //        Roo.each(items, function(item, k){
33698 //            
33699 //            var c = k % 2;
33700 //            
33701 //            item.el.position('absolute');
33702 //                
33703 //            var width = Math.floor(this.colWidth + item.el.getPadding('lr'));
33704 //
33705 //            item.el.setWidth(width);
33706 //
33707 //            var height = Math.floor(this.colWidth * item.y / item.x + item.el.getPadding('tb'));
33708 //
33709 //            item.el.setHeight(height);
33710 //            
33711 //            if(c == 0){
33712 //                item.el.setXY([x, y], isInstant ? false : true);
33713 //            } else {
33714 //                item.el.setXY([maxX - width, y], isInstant ? false : true);
33715 //            }
33716 //            
33717 //            y = y + height + this.alternativePadWidth;
33718 //            
33719 //            maxHeight = maxHeight + height + this.alternativePadWidth;
33720 //            
33721 //        }, this);
33722 //        
33723 //        this.el.setHeight(maxHeight);
33724 //        
33725 //    },
33726     
33727     _processHorizontalLayoutQueue : function( queue, eItems, isInstant )
33728     {
33729         var pos = this.el.getBox(true);
33730         
33731         var minX = pos.x;
33732         var minY = pos.y;
33733         
33734         var maxX = pos.right;
33735         
33736         this._processHorizontalEndItem(eItems, maxX, minX, minY, isInstant);
33737         
33738         var maxX = maxX - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
33739         
33740         Roo.each(queue, function(box, k){
33741             
33742             Roo.each(box, function(b, kk){
33743                 
33744                 b.el.position('absolute');
33745                 
33746                 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
33747                 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
33748                 
33749                 if(b.size == 'md-left' || b.size == 'md-right'){
33750                     width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
33751                     height = Math.floor(this.unitWidth * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
33752                 }
33753                 
33754                 b.el.setWidth(width);
33755                 b.el.setHeight(height);
33756                 
33757             }, this);
33758             
33759             if(!box.length){
33760                 return;
33761             }
33762             
33763             var positions = [];
33764             
33765             switch (box.length){
33766                 case 1 :
33767                     positions = this.getHorizontalOneBoxColPositions(maxX, minY, box);
33768                     break;
33769                 case 2 :
33770                     positions = this.getHorizontalTwoBoxColPositions(maxX, minY, box);
33771                     break;
33772                 case 3 :
33773                     positions = this.getHorizontalThreeBoxColPositions(maxX, minY, box);
33774                     break;
33775                 case 4 :
33776                     positions = this.getHorizontalFourBoxColPositions(maxX, minY, box);
33777                     break;
33778                 default :
33779                     break;
33780             }
33781             
33782             Roo.each(box, function(b,kk){
33783                 
33784                 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
33785                 
33786                 maxX = Math.min(maxX, positions[kk].x - this.padWidth);
33787                 
33788             }, this);
33789             
33790         }, this);
33791         
33792     },
33793     
33794     _processHorizontalEndItem : function(eItems, maxX, minX, minY, isInstant)
33795     {
33796         Roo.each(eItems, function(b,k){
33797             
33798             b.size = (k == 0) ? 'sm' : 'xs';
33799             b.x = (k == 0) ? 2 : 1;
33800             b.y = (k == 0) ? 2 : 1;
33801             
33802             b.el.position('absolute');
33803             
33804             var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
33805                 
33806             b.el.setWidth(width);
33807             
33808             var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
33809             
33810             b.el.setHeight(height);
33811             
33812         }, this);
33813
33814         var positions = [];
33815         
33816         positions.push({
33817             x : maxX - this.unitWidth * 2 - this.gutter,
33818             y : minY
33819         });
33820         
33821         positions.push({
33822             x : maxX - this.unitWidth,
33823             y : minY + (this.unitWidth + this.gutter) * 2
33824         });
33825         
33826         positions.push({
33827             x : maxX - this.unitWidth * 3 - this.gutter * 2,
33828             y : minY
33829         });
33830         
33831         Roo.each(eItems, function(b,k){
33832             
33833             b.el.setXY([positions[k].x, positions[k].y], isInstant ? false : true);
33834
33835         }, this);
33836         
33837     },
33838     
33839     getVerticalOneBoxColPositions : function(x, y, box)
33840     {
33841         var pos = [];
33842         
33843         var rand = Math.floor(Math.random() * ((4 - box[0].x)));
33844         
33845         if(box[0].size == 'md-left'){
33846             rand = 0;
33847         }
33848         
33849         if(box[0].size == 'md-right'){
33850             rand = 1;
33851         }
33852         
33853         pos.push({
33854             x : x + (this.unitWidth + this.gutter) * rand,
33855             y : y
33856         });
33857         
33858         return pos;
33859     },
33860     
33861     getVerticalTwoBoxColPositions : function(x, y, box)
33862     {
33863         var pos = [];
33864         
33865         if(box[0].size == 'xs'){
33866             
33867             pos.push({
33868                 x : x,
33869                 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[1].y))
33870             });
33871
33872             pos.push({
33873                 x : x + (this.unitWidth + this.gutter) * (3 - box[1].x),
33874                 y : y
33875             });
33876             
33877             return pos;
33878             
33879         }
33880         
33881         pos.push({
33882             x : x,
33883             y : y
33884         });
33885
33886         pos.push({
33887             x : x + (this.unitWidth + this.gutter) * 2,
33888             y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[0].y))
33889         });
33890         
33891         return pos;
33892         
33893     },
33894     
33895     getVerticalThreeBoxColPositions : function(x, y, box)
33896     {
33897         var pos = [];
33898         
33899         if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
33900             
33901             pos.push({
33902                 x : x,
33903                 y : y
33904             });
33905
33906             pos.push({
33907                 x : x + (this.unitWidth + this.gutter) * 1,
33908                 y : y
33909             });
33910             
33911             pos.push({
33912                 x : x + (this.unitWidth + this.gutter) * 2,
33913                 y : y
33914             });
33915             
33916             return pos;
33917             
33918         }
33919         
33920         if(box[0].size == 'xs' && box[1].size == 'xs'){
33921             
33922             pos.push({
33923                 x : x,
33924                 y : y
33925             });
33926
33927             pos.push({
33928                 x : x,
33929                 y : y + ((this.unitHeight + this.gutter) * (box[2].y - 1))
33930             });
33931             
33932             pos.push({
33933                 x : x + (this.unitWidth + this.gutter) * 1,
33934                 y : y
33935             });
33936             
33937             return pos;
33938             
33939         }
33940         
33941         pos.push({
33942             x : x,
33943             y : y
33944         });
33945
33946         pos.push({
33947             x : x + (this.unitWidth + this.gutter) * 2,
33948             y : y
33949         });
33950
33951         pos.push({
33952             x : x + (this.unitWidth + this.gutter) * 2,
33953             y : y + (this.unitHeight + this.gutter) * (box[0].y - 1)
33954         });
33955             
33956         return pos;
33957         
33958     },
33959     
33960     getVerticalFourBoxColPositions : function(x, y, box)
33961     {
33962         var pos = [];
33963         
33964         if(box[0].size == 'xs'){
33965             
33966             pos.push({
33967                 x : x,
33968                 y : y
33969             });
33970
33971             pos.push({
33972                 x : x,
33973                 y : y + (this.unitHeight + this.gutter) * 1
33974             });
33975             
33976             pos.push({
33977                 x : x,
33978                 y : y + (this.unitHeight + this.gutter) * 2
33979             });
33980             
33981             pos.push({
33982                 x : x + (this.unitWidth + this.gutter) * 1,
33983                 y : y
33984             });
33985             
33986             return pos;
33987             
33988         }
33989         
33990         pos.push({
33991             x : x,
33992             y : y
33993         });
33994
33995         pos.push({
33996             x : x + (this.unitWidth + this.gutter) * 2,
33997             y : y
33998         });
33999
34000         pos.push({
34001             x : x + (this.unitHeightunitWidth + this.gutter) * 2,
34002             y : y + (this.unitHeight + this.gutter) * 1
34003         });
34004
34005         pos.push({
34006             x : x + (this.unitWidth + this.gutter) * 2,
34007             y : y + (this.unitWidth + this.gutter) * 2
34008         });
34009
34010         return pos;
34011         
34012     },
34013     
34014     getHorizontalOneBoxColPositions : function(maxX, minY, box)
34015     {
34016         var pos = [];
34017         
34018         if(box[0].size == 'md-left'){
34019             pos.push({
34020                 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
34021                 y : minY
34022             });
34023             
34024             return pos;
34025         }
34026         
34027         if(box[0].size == 'md-right'){
34028             pos.push({
34029                 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
34030                 y : minY + (this.unitWidth + this.gutter) * 1
34031             });
34032             
34033             return pos;
34034         }
34035         
34036         var rand = Math.floor(Math.random() * (4 - box[0].y));
34037         
34038         pos.push({
34039             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34040             y : minY + (this.unitWidth + this.gutter) * rand
34041         });
34042         
34043         return pos;
34044         
34045     },
34046     
34047     getHorizontalTwoBoxColPositions : function(maxX, minY, box)
34048     {
34049         var pos = [];
34050         
34051         if(box[0].size == 'xs'){
34052             
34053             pos.push({
34054                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34055                 y : minY
34056             });
34057
34058             pos.push({
34059                 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34060                 y : minY + (this.unitWidth + this.gutter) * (3 - box[1].y)
34061             });
34062             
34063             return pos;
34064             
34065         }
34066         
34067         pos.push({
34068             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34069             y : minY
34070         });
34071
34072         pos.push({
34073             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34074             y : minY + (this.unitWidth + this.gutter) * 2
34075         });
34076         
34077         return pos;
34078         
34079     },
34080     
34081     getHorizontalThreeBoxColPositions : function(maxX, minY, box)
34082     {
34083         var pos = [];
34084         
34085         if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
34086             
34087             pos.push({
34088                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34089                 y : minY
34090             });
34091
34092             pos.push({
34093                 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34094                 y : minY + (this.unitWidth + this.gutter) * 1
34095             });
34096             
34097             pos.push({
34098                 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
34099                 y : minY + (this.unitWidth + this.gutter) * 2
34100             });
34101             
34102             return pos;
34103             
34104         }
34105         
34106         if(box[0].size == 'xs' && box[1].size == 'xs'){
34107             
34108             pos.push({
34109                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34110                 y : minY
34111             });
34112
34113             pos.push({
34114                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34115                 y : minY
34116             });
34117             
34118             pos.push({
34119                 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
34120                 y : minY + (this.unitWidth + this.gutter) * 1
34121             });
34122             
34123             return pos;
34124             
34125         }
34126         
34127         pos.push({
34128             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34129             y : minY
34130         });
34131
34132         pos.push({
34133             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34134             y : minY + (this.unitWidth + this.gutter) * 2
34135         });
34136
34137         pos.push({
34138             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
34139             y : minY + (this.unitWidth + this.gutter) * 2
34140         });
34141             
34142         return pos;
34143         
34144     },
34145     
34146     getHorizontalFourBoxColPositions : function(maxX, minY, box)
34147     {
34148         var pos = [];
34149         
34150         if(box[0].size == 'xs'){
34151             
34152             pos.push({
34153                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34154                 y : minY
34155             });
34156
34157             pos.push({
34158                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34159                 y : minY
34160             });
34161             
34162             pos.push({
34163                 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),
34164                 y : minY
34165             });
34166             
34167             pos.push({
34168                 x : maxX - this.unitWidth * box[3].x - this.gutter * (box[3].x - 1),
34169                 y : minY + (this.unitWidth + this.gutter) * 1
34170             });
34171             
34172             return pos;
34173             
34174         }
34175         
34176         pos.push({
34177             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34178             y : minY
34179         });
34180         
34181         pos.push({
34182             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34183             y : minY + (this.unitWidth + this.gutter) * 2
34184         });
34185         
34186         pos.push({
34187             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
34188             y : minY + (this.unitWidth + this.gutter) * 2
34189         });
34190         
34191         pos.push({
34192             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),
34193             y : minY + (this.unitWidth + this.gutter) * 2
34194         });
34195
34196         return pos;
34197         
34198     },
34199     
34200     /**
34201     * remove a Masonry Brick
34202     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to remove
34203     */
34204     removeBrick : function(brick_id)
34205     {
34206         if (!brick_id) {
34207             return;
34208         }
34209         
34210         for (var i = 0; i<this.bricks.length; i++) {
34211             if (this.bricks[i].id == brick_id) {
34212                 this.bricks.splice(i,1);
34213                 this.el.dom.removeChild(Roo.get(brick_id).dom);
34214                 this.initial();
34215             }
34216         }
34217     },
34218     
34219     /**
34220     * adds a Masonry Brick
34221     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
34222     */
34223     addBrick : function(cfg)
34224     {
34225         var cn = new Roo.bootstrap.MasonryBrick(cfg);
34226         //this.register(cn);
34227         cn.parentId = this.id;
34228         cn.render(this.el);
34229         return cn;
34230     },
34231     
34232     /**
34233     * register a Masonry Brick
34234     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
34235     */
34236     
34237     register : function(brick)
34238     {
34239         this.bricks.push(brick);
34240         brick.masonryId = this.id;
34241     },
34242     
34243     /**
34244     * clear all the Masonry Brick
34245     */
34246     clearAll : function()
34247     {
34248         this.bricks = [];
34249         //this.getChildContainer().dom.innerHTML = "";
34250         this.el.dom.innerHTML = '';
34251     },
34252     
34253     getSelected : function()
34254     {
34255         if (!this.selectedBrick) {
34256             return false;
34257         }
34258         
34259         return this.selectedBrick;
34260     }
34261 });
34262
34263 Roo.apply(Roo.bootstrap.LayoutMasonry, {
34264     
34265     groups: {},
34266      /**
34267     * register a Masonry Layout
34268     * @param {Roo.bootstrap.LayoutMasonry} the masonry layout to add
34269     */
34270     
34271     register : function(layout)
34272     {
34273         this.groups[layout.id] = layout;
34274     },
34275     /**
34276     * fetch a  Masonry Layout based on the masonry layout ID
34277     * @param {string} the masonry layout to add
34278     * @returns {Roo.bootstrap.LayoutMasonry} the masonry layout
34279     */
34280     
34281     get: function(layout_id) {
34282         if (typeof(this.groups[layout_id]) == 'undefined') {
34283             return false;
34284         }
34285         return this.groups[layout_id] ;
34286     }
34287     
34288     
34289     
34290 });
34291
34292  
34293
34294  /**
34295  *
34296  * This is based on 
34297  * http://masonry.desandro.com
34298  *
34299  * The idea is to render all the bricks based on vertical width...
34300  *
34301  * The original code extends 'outlayer' - we might need to use that....
34302  * 
34303  */
34304
34305
34306 /**
34307  * @class Roo.bootstrap.LayoutMasonryAuto
34308  * @extends Roo.bootstrap.Component
34309  * Bootstrap Layout Masonry class
34310  * 
34311  * @constructor
34312  * Create a new Element
34313  * @param {Object} config The config object
34314  */
34315
34316 Roo.bootstrap.LayoutMasonryAuto = function(config){
34317     Roo.bootstrap.LayoutMasonryAuto.superclass.constructor.call(this, config);
34318 };
34319
34320 Roo.extend(Roo.bootstrap.LayoutMasonryAuto, Roo.bootstrap.Component,  {
34321     
34322       /**
34323      * @cfg {Boolean} isFitWidth  - resize the width..
34324      */   
34325     isFitWidth : false,  // options..
34326     /**
34327      * @cfg {Boolean} isOriginLeft = left align?
34328      */   
34329     isOriginLeft : true,
34330     /**
34331      * @cfg {Boolean} isOriginTop = top align?
34332      */   
34333     isOriginTop : false,
34334     /**
34335      * @cfg {Boolean} isLayoutInstant = no animation?
34336      */   
34337     isLayoutInstant : false, // needed?
34338     /**
34339      * @cfg {Boolean} isResizingContainer = not sure if this is used..
34340      */   
34341     isResizingContainer : true,
34342     /**
34343      * @cfg {Number} columnWidth  width of the columns 
34344      */   
34345     
34346     columnWidth : 0,
34347     
34348     /**
34349      * @cfg {Number} maxCols maximum number of columns
34350      */   
34351     
34352     maxCols: 0,
34353     /**
34354      * @cfg {Number} padHeight padding below box..
34355      */   
34356     
34357     padHeight : 10, 
34358     
34359     /**
34360      * @cfg {Boolean} isAutoInitial defalut true
34361      */   
34362     
34363     isAutoInitial : true, 
34364     
34365     // private?
34366     gutter : 0,
34367     
34368     containerWidth: 0,
34369     initialColumnWidth : 0,
34370     currentSize : null,
34371     
34372     colYs : null, // array.
34373     maxY : 0,
34374     padWidth: 10,
34375     
34376     
34377     tag: 'div',
34378     cls: '',
34379     bricks: null, //CompositeElement
34380     cols : 0, // array?
34381     // element : null, // wrapped now this.el
34382     _isLayoutInited : null, 
34383     
34384     
34385     getAutoCreate : function(){
34386         
34387         var cfg = {
34388             tag: this.tag,
34389             cls: 'blog-masonary-wrapper ' + this.cls,
34390             cn : {
34391                 cls : 'mas-boxes masonary'
34392             }
34393         };
34394         
34395         return cfg;
34396     },
34397     
34398     getChildContainer: function( )
34399     {
34400         if (this.boxesEl) {
34401             return this.boxesEl;
34402         }
34403         
34404         this.boxesEl = this.el.select('.mas-boxes').first();
34405         
34406         return this.boxesEl;
34407     },
34408     
34409     
34410     initEvents : function()
34411     {
34412         var _this = this;
34413         
34414         if(this.isAutoInitial){
34415             Roo.log('hook children rendered');
34416             this.on('childrenrendered', function() {
34417                 Roo.log('children rendered');
34418                 _this.initial();
34419             } ,this);
34420         }
34421         
34422     },
34423     
34424     initial : function()
34425     {
34426         this.reloadItems();
34427
34428         this.currentSize = this.el.getBox(true);
34429
34430         /// was window resize... - let's see if this works..
34431         Roo.EventManager.onWindowResize(this.resize, this); 
34432
34433         if(!this.isAutoInitial){
34434             this.layout();
34435             return;
34436         }
34437         
34438         this.layout.defer(500,this);
34439     },
34440     
34441     reloadItems: function()
34442     {
34443         this.bricks = this.el.select('.masonry-brick', true);
34444         
34445         this.bricks.each(function(b) {
34446             //Roo.log(b.getSize());
34447             if (!b.attr('originalwidth')) {
34448                 b.attr('originalwidth',  b.getSize().width);
34449             }
34450             
34451         });
34452         
34453         Roo.log(this.bricks.elements.length);
34454     },
34455     
34456     resize : function()
34457     {
34458         Roo.log('resize');
34459         var cs = this.el.getBox(true);
34460         
34461         if (this.currentSize.width == cs.width && this.currentSize.x == cs.x ) {
34462             Roo.log("no change in with or X");
34463             return;
34464         }
34465         this.currentSize = cs;
34466         this.layout();
34467     },
34468     
34469     layout : function()
34470     {
34471          Roo.log('layout');
34472         this._resetLayout();
34473         //this._manageStamps();
34474       
34475         // don't animate first layout
34476         var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
34477         this.layoutItems( isInstant );
34478       
34479         // flag for initalized
34480         this._isLayoutInited = true;
34481     },
34482     
34483     layoutItems : function( isInstant )
34484     {
34485         //var items = this._getItemsForLayout( this.items );
34486         // original code supports filtering layout items.. we just ignore it..
34487         
34488         this._layoutItems( this.bricks , isInstant );
34489       
34490         this._postLayout();
34491     },
34492     _layoutItems : function ( items , isInstant)
34493     {
34494        //this.fireEvent( 'layout', this, items );
34495     
34496
34497         if ( !items || !items.elements.length ) {
34498           // no items, emit event with empty array
34499             return;
34500         }
34501
34502         var queue = [];
34503         items.each(function(item) {
34504             Roo.log("layout item");
34505             Roo.log(item);
34506             // get x/y object from method
34507             var position = this._getItemLayoutPosition( item );
34508             // enqueue
34509             position.item = item;
34510             position.isInstant = isInstant; // || item.isLayoutInstant; << not set yet...
34511             queue.push( position );
34512         }, this);
34513       
34514         this._processLayoutQueue( queue );
34515     },
34516     /** Sets position of item in DOM
34517     * @param {Element} item
34518     * @param {Number} x - horizontal position
34519     * @param {Number} y - vertical position
34520     * @param {Boolean} isInstant - disables transitions
34521     */
34522     _processLayoutQueue : function( queue )
34523     {
34524         for ( var i=0, len = queue.length; i < len; i++ ) {
34525             var obj = queue[i];
34526             obj.item.position('absolute');
34527             obj.item.setXY([obj.x,obj.y], obj.isInstant ? false : true);
34528         }
34529     },
34530       
34531     
34532     /**
34533     * Any logic you want to do after each layout,
34534     * i.e. size the container
34535     */
34536     _postLayout : function()
34537     {
34538         this.resizeContainer();
34539     },
34540     
34541     resizeContainer : function()
34542     {
34543         if ( !this.isResizingContainer ) {
34544             return;
34545         }
34546         var size = this._getContainerSize();
34547         if ( size ) {
34548             this.el.setSize(size.width,size.height);
34549             this.boxesEl.setSize(size.width,size.height);
34550         }
34551     },
34552     
34553     
34554     
34555     _resetLayout : function()
34556     {
34557         //this.getSize();  // -- does not really do anything.. it probably applies left/right etc. to obuject but not used
34558         this.colWidth = this.el.getWidth();
34559         //this.gutter = this.el.getWidth(); 
34560         
34561         this.measureColumns();
34562
34563         // reset column Y
34564         var i = this.cols;
34565         this.colYs = [];
34566         while (i--) {
34567             this.colYs.push( 0 );
34568         }
34569     
34570         this.maxY = 0;
34571     },
34572
34573     measureColumns : function()
34574     {
34575         this.getContainerWidth();
34576       // if columnWidth is 0, default to outerWidth of first item
34577         if ( !this.columnWidth ) {
34578             var firstItem = this.bricks.first();
34579             Roo.log(firstItem);
34580             this.columnWidth  = this.containerWidth;
34581             if (firstItem && firstItem.attr('originalwidth') ) {
34582                 this.columnWidth = 1* (firstItem.attr('originalwidth') || firstItem.getWidth());
34583             }
34584             // columnWidth fall back to item of first element
34585             Roo.log("set column width?");
34586                         this.initialColumnWidth = this.columnWidth  ;
34587
34588             // if first elem has no width, default to size of container
34589             
34590         }
34591         
34592         
34593         if (this.initialColumnWidth) {
34594             this.columnWidth = this.initialColumnWidth;
34595         }
34596         
34597         
34598             
34599         // column width is fixed at the top - however if container width get's smaller we should
34600         // reduce it...
34601         
34602         // this bit calcs how man columns..
34603             
34604         var columnWidth = this.columnWidth += this.gutter;
34605       
34606         // calculate columns
34607         var containerWidth = this.containerWidth + this.gutter;
34608         
34609         var cols = (containerWidth - this.padWidth) / (columnWidth - this.padWidth);
34610         // fix rounding errors, typically with gutters
34611         var excess = columnWidth - containerWidth % columnWidth;
34612         
34613         
34614         // if overshoot is less than a pixel, round up, otherwise floor it
34615         var mathMethod = excess && excess < 1 ? 'round' : 'floor';
34616         cols = Math[ mathMethod ]( cols );
34617         this.cols = Math.max( cols, 1 );
34618         this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
34619         
34620          // padding positioning..
34621         var totalColWidth = this.cols * this.columnWidth;
34622         var padavail = this.containerWidth - totalColWidth;
34623         // so for 2 columns - we need 3 'pads'
34624         
34625         var padNeeded = (1+this.cols) * this.padWidth;
34626         
34627         var padExtra = Math.floor((padavail - padNeeded) / this.cols);
34628         
34629         this.columnWidth += padExtra
34630         //this.padWidth = Math.floor(padavail /  ( this.cols));
34631         
34632         // adjust colum width so that padding is fixed??
34633         
34634         // we have 3 columns ... total = width * 3
34635         // we have X left over... that should be used by 
34636         
34637         //if (this.expandC) {
34638             
34639         //}
34640         
34641         
34642         
34643     },
34644     
34645     getContainerWidth : function()
34646     {
34647        /* // container is parent if fit width
34648         var container = this.isFitWidth ? this.element.parentNode : this.element;
34649         // check that this.size and size are there
34650         // IE8 triggers resize on body size change, so they might not be
34651         
34652         var size = getSize( container );  //FIXME
34653         this.containerWidth = size && size.innerWidth; //FIXME
34654         */
34655          
34656         this.containerWidth = this.el.getBox(true).width;  //maybe use getComputedWidth
34657         
34658     },
34659     
34660     _getItemLayoutPosition : function( item )  // what is item?
34661     {
34662         // we resize the item to our columnWidth..
34663       
34664         item.setWidth(this.columnWidth);
34665         item.autoBoxAdjust  = false;
34666         
34667         var sz = item.getSize();
34668  
34669         // how many columns does this brick span
34670         var remainder = this.containerWidth % this.columnWidth;
34671         
34672         var mathMethod = remainder && remainder < 1 ? 'round' : 'ceil';
34673         // round if off by 1 pixel, otherwise use ceil
34674         var colSpan = Math[ mathMethod ]( sz.width  / this.columnWidth );
34675         colSpan = Math.min( colSpan, this.cols );
34676         
34677         // normally this should be '1' as we dont' currently allow multi width columns..
34678         
34679         var colGroup = this._getColGroup( colSpan );
34680         // get the minimum Y value from the columns
34681         var minimumY = Math.min.apply( Math, colGroup );
34682         Roo.log([ 'setHeight',  minimumY, sz.height, setHeight ]);
34683         
34684         var shortColIndex = colGroup.indexOf(  minimumY ); // broken on ie8..?? probably...
34685          
34686         // position the brick
34687         var position = {
34688             x: this.currentSize.x + (this.padWidth /2) + ((this.columnWidth + this.padWidth )* shortColIndex),
34689             y: this.currentSize.y + minimumY + this.padHeight
34690         };
34691         
34692         Roo.log(position);
34693         // apply setHeight to necessary columns
34694         var setHeight = minimumY + sz.height + this.padHeight;
34695         //Roo.log([ 'setHeight',  minimumY, sz.height, setHeight ]);
34696         
34697         var setSpan = this.cols + 1 - colGroup.length;
34698         for ( var i = 0; i < setSpan; i++ ) {
34699           this.colYs[ shortColIndex + i ] = setHeight ;
34700         }
34701       
34702         return position;
34703     },
34704     
34705     /**
34706      * @param {Number} colSpan - number of columns the element spans
34707      * @returns {Array} colGroup
34708      */
34709     _getColGroup : function( colSpan )
34710     {
34711         if ( colSpan < 2 ) {
34712           // if brick spans only one column, use all the column Ys
34713           return this.colYs;
34714         }
34715       
34716         var colGroup = [];
34717         // how many different places could this brick fit horizontally
34718         var groupCount = this.cols + 1 - colSpan;
34719         // for each group potential horizontal position
34720         for ( var i = 0; i < groupCount; i++ ) {
34721           // make an array of colY values for that one group
34722           var groupColYs = this.colYs.slice( i, i + colSpan );
34723           // and get the max value of the array
34724           colGroup[i] = Math.max.apply( Math, groupColYs );
34725         }
34726         return colGroup;
34727     },
34728     /*
34729     _manageStamp : function( stamp )
34730     {
34731         var stampSize =  stamp.getSize();
34732         var offset = stamp.getBox();
34733         // get the columns that this stamp affects
34734         var firstX = this.isOriginLeft ? offset.x : offset.right;
34735         var lastX = firstX + stampSize.width;
34736         var firstCol = Math.floor( firstX / this.columnWidth );
34737         firstCol = Math.max( 0, firstCol );
34738         
34739         var lastCol = Math.floor( lastX / this.columnWidth );
34740         // lastCol should not go over if multiple of columnWidth #425
34741         lastCol -= lastX % this.columnWidth ? 0 : 1;
34742         lastCol = Math.min( this.cols - 1, lastCol );
34743         
34744         // set colYs to bottom of the stamp
34745         var stampMaxY = ( this.isOriginTop ? offset.y : offset.bottom ) +
34746             stampSize.height;
34747             
34748         for ( var i = firstCol; i <= lastCol; i++ ) {
34749           this.colYs[i] = Math.max( stampMaxY, this.colYs[i] );
34750         }
34751     },
34752     */
34753     
34754     _getContainerSize : function()
34755     {
34756         this.maxY = Math.max.apply( Math, this.colYs );
34757         var size = {
34758             height: this.maxY
34759         };
34760       
34761         if ( this.isFitWidth ) {
34762             size.width = this._getContainerFitWidth();
34763         }
34764       
34765         return size;
34766     },
34767     
34768     _getContainerFitWidth : function()
34769     {
34770         var unusedCols = 0;
34771         // count unused columns
34772         var i = this.cols;
34773         while ( --i ) {
34774           if ( this.colYs[i] !== 0 ) {
34775             break;
34776           }
34777           unusedCols++;
34778         }
34779         // fit container to columns that have been used
34780         return ( this.cols - unusedCols ) * this.columnWidth - this.gutter;
34781     },
34782     
34783     needsResizeLayout : function()
34784     {
34785         var previousWidth = this.containerWidth;
34786         this.getContainerWidth();
34787         return previousWidth !== this.containerWidth;
34788     }
34789  
34790 });
34791
34792  
34793
34794  /*
34795  * - LGPL
34796  *
34797  * element
34798  * 
34799  */
34800
34801 /**
34802  * @class Roo.bootstrap.MasonryBrick
34803  * @extends Roo.bootstrap.Component
34804  * Bootstrap MasonryBrick class
34805  * 
34806  * @constructor
34807  * Create a new MasonryBrick
34808  * @param {Object} config The config object
34809  */
34810
34811 Roo.bootstrap.MasonryBrick = function(config){
34812     
34813     Roo.bootstrap.MasonryBrick.superclass.constructor.call(this, config);
34814     
34815     Roo.bootstrap.MasonryBrick.register(this);
34816     
34817     this.addEvents({
34818         // raw events
34819         /**
34820          * @event click
34821          * When a MasonryBrick is clcik
34822          * @param {Roo.bootstrap.MasonryBrick} this
34823          * @param {Roo.EventObject} e
34824          */
34825         "click" : true
34826     });
34827 };
34828
34829 Roo.extend(Roo.bootstrap.MasonryBrick, Roo.bootstrap.Component,  {
34830     
34831     /**
34832      * @cfg {String} title
34833      */   
34834     title : '',
34835     /**
34836      * @cfg {String} html
34837      */   
34838     html : '',
34839     /**
34840      * @cfg {String} bgimage
34841      */   
34842     bgimage : '',
34843     /**
34844      * @cfg {String} videourl
34845      */   
34846     videourl : '',
34847     /**
34848      * @cfg {String} cls
34849      */   
34850     cls : '',
34851     /**
34852      * @cfg {String} href
34853      */   
34854     href : '',
34855     /**
34856      * @cfg {String} size (xs|sm|md|md-left|md-right|tall|wide)
34857      */   
34858     size : 'xs',
34859     
34860     /**
34861      * @cfg {String} placetitle (center|bottom)
34862      */   
34863     placetitle : '',
34864     
34865     /**
34866      * @cfg {Boolean} isFitContainer defalut true
34867      */   
34868     isFitContainer : true, 
34869     
34870     /**
34871      * @cfg {Boolean} preventDefault defalut false
34872      */   
34873     preventDefault : false, 
34874     
34875     /**
34876      * @cfg {Boolean} inverse defalut false
34877      */   
34878     maskInverse : false, 
34879     
34880     getAutoCreate : function()
34881     {
34882         if(!this.isFitContainer){
34883             return this.getSplitAutoCreate();
34884         }
34885         
34886         var cls = 'masonry-brick masonry-brick-full';
34887         
34888         if(this.href.length){
34889             cls += ' masonry-brick-link';
34890         }
34891         
34892         if(this.bgimage.length){
34893             cls += ' masonry-brick-image';
34894         }
34895         
34896         if(this.maskInverse){
34897             cls += ' mask-inverse';
34898         }
34899         
34900         if(!this.html.length && !this.maskInverse && !this.videourl.length){
34901             cls += ' enable-mask';
34902         }
34903         
34904         if(this.size){
34905             cls += ' masonry-' + this.size + '-brick';
34906         }
34907         
34908         if(this.placetitle.length){
34909             
34910             switch (this.placetitle) {
34911                 case 'center' :
34912                     cls += ' masonry-center-title';
34913                     break;
34914                 case 'bottom' :
34915                     cls += ' masonry-bottom-title';
34916                     break;
34917                 default:
34918                     break;
34919             }
34920             
34921         } else {
34922             if(!this.html.length && !this.bgimage.length){
34923                 cls += ' masonry-center-title';
34924             }
34925
34926             if(!this.html.length && this.bgimage.length){
34927                 cls += ' masonry-bottom-title';
34928             }
34929         }
34930         
34931         if(this.cls){
34932             cls += ' ' + this.cls;
34933         }
34934         
34935         var cfg = {
34936             tag: (this.href.length) ? 'a' : 'div',
34937             cls: cls,
34938             cn: [
34939                 {
34940                     tag: 'div',
34941                     cls: 'masonry-brick-mask'
34942                 },
34943                 {
34944                     tag: 'div',
34945                     cls: 'masonry-brick-paragraph',
34946                     cn: []
34947                 }
34948             ]
34949         };
34950         
34951         if(this.href.length){
34952             cfg.href = this.href;
34953         }
34954         
34955         var cn = cfg.cn[1].cn;
34956         
34957         if(this.title.length){
34958             cn.push({
34959                 tag: 'h4',
34960                 cls: 'masonry-brick-title',
34961                 html: this.title
34962             });
34963         }
34964         
34965         if(this.html.length){
34966             cn.push({
34967                 tag: 'p',
34968                 cls: 'masonry-brick-text',
34969                 html: this.html
34970             });
34971         }
34972         
34973         if (!this.title.length && !this.html.length) {
34974             cfg.cn[1].cls += ' hide';
34975         }
34976         
34977         if(this.bgimage.length){
34978             cfg.cn.push({
34979                 tag: 'img',
34980                 cls: 'masonry-brick-image-view',
34981                 src: this.bgimage
34982             });
34983         }
34984         
34985         if(this.videourl.length){
34986             var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
34987             // youtube support only?
34988             cfg.cn.push({
34989                 tag: 'iframe',
34990                 cls: 'masonry-brick-image-view',
34991                 src: vurl,
34992                 frameborder : 0,
34993                 allowfullscreen : true
34994             });
34995         }
34996         
34997         return cfg;
34998         
34999     },
35000     
35001     getSplitAutoCreate : function()
35002     {
35003         var cls = 'masonry-brick masonry-brick-split';
35004         
35005         if(this.href.length){
35006             cls += ' masonry-brick-link';
35007         }
35008         
35009         if(this.bgimage.length){
35010             cls += ' masonry-brick-image';
35011         }
35012         
35013         if(this.size){
35014             cls += ' masonry-' + this.size + '-brick';
35015         }
35016         
35017         switch (this.placetitle) {
35018             case 'center' :
35019                 cls += ' masonry-center-title';
35020                 break;
35021             case 'bottom' :
35022                 cls += ' masonry-bottom-title';
35023                 break;
35024             default:
35025                 if(!this.bgimage.length){
35026                     cls += ' masonry-center-title';
35027                 }
35028
35029                 if(this.bgimage.length){
35030                     cls += ' masonry-bottom-title';
35031                 }
35032                 break;
35033         }
35034         
35035         if(this.cls){
35036             cls += ' ' + this.cls;
35037         }
35038         
35039         var cfg = {
35040             tag: (this.href.length) ? 'a' : 'div',
35041             cls: cls,
35042             cn: [
35043                 {
35044                     tag: 'div',
35045                     cls: 'masonry-brick-split-head',
35046                     cn: [
35047                         {
35048                             tag: 'div',
35049                             cls: 'masonry-brick-paragraph',
35050                             cn: []
35051                         }
35052                     ]
35053                 },
35054                 {
35055                     tag: 'div',
35056                     cls: 'masonry-brick-split-body',
35057                     cn: []
35058                 }
35059             ]
35060         };
35061         
35062         if(this.href.length){
35063             cfg.href = this.href;
35064         }
35065         
35066         if(this.title.length){
35067             cfg.cn[0].cn[0].cn.push({
35068                 tag: 'h4',
35069                 cls: 'masonry-brick-title',
35070                 html: this.title
35071             });
35072         }
35073         
35074         if(this.html.length){
35075             cfg.cn[1].cn.push({
35076                 tag: 'p',
35077                 cls: 'masonry-brick-text',
35078                 html: this.html
35079             });
35080         }
35081
35082         if(this.bgimage.length){
35083             cfg.cn[0].cn.push({
35084                 tag: 'img',
35085                 cls: 'masonry-brick-image-view',
35086                 src: this.bgimage
35087             });
35088         }
35089         
35090         if(this.videourl.length){
35091             var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
35092             // youtube support only?
35093             cfg.cn[0].cn.cn.push({
35094                 tag: 'iframe',
35095                 cls: 'masonry-brick-image-view',
35096                 src: vurl,
35097                 frameborder : 0,
35098                 allowfullscreen : true
35099             });
35100         }
35101         
35102         return cfg;
35103     },
35104     
35105     initEvents: function() 
35106     {
35107         switch (this.size) {
35108             case 'xs' :
35109                 this.x = 1;
35110                 this.y = 1;
35111                 break;
35112             case 'sm' :
35113                 this.x = 2;
35114                 this.y = 2;
35115                 break;
35116             case 'md' :
35117             case 'md-left' :
35118             case 'md-right' :
35119                 this.x = 3;
35120                 this.y = 3;
35121                 break;
35122             case 'tall' :
35123                 this.x = 2;
35124                 this.y = 3;
35125                 break;
35126             case 'wide' :
35127                 this.x = 3;
35128                 this.y = 2;
35129                 break;
35130             case 'wide-thin' :
35131                 this.x = 3;
35132                 this.y = 1;
35133                 break;
35134                         
35135             default :
35136                 break;
35137         }
35138         
35139         if(Roo.isTouch){
35140             this.el.on('touchstart', this.onTouchStart, this);
35141             this.el.on('touchmove', this.onTouchMove, this);
35142             this.el.on('touchend', this.onTouchEnd, this);
35143             this.el.on('contextmenu', this.onContextMenu, this);
35144         } else {
35145             this.el.on('mouseenter'  ,this.enter, this);
35146             this.el.on('mouseleave', this.leave, this);
35147             this.el.on('click', this.onClick, this);
35148         }
35149         
35150         if (typeof(this.parent().bricks) == 'object' && this.parent().bricks != null) {
35151             this.parent().bricks.push(this);   
35152         }
35153         
35154     },
35155     
35156     onClick: function(e, el)
35157     {
35158         var time = this.endTimer - this.startTimer;
35159         // Roo.log(e.preventDefault());
35160         if(Roo.isTouch){
35161             if(time > 1000){
35162                 e.preventDefault();
35163                 return;
35164             }
35165         }
35166         
35167         if(!this.preventDefault){
35168             return;
35169         }
35170         
35171         e.preventDefault();
35172         
35173         if (this.activeClass != '') {
35174             this.selectBrick();
35175         }
35176         
35177         this.fireEvent('click', this, e);
35178     },
35179     
35180     enter: function(e, el)
35181     {
35182         e.preventDefault();
35183         
35184         if(!this.isFitContainer || this.maskInverse || this.videourl.length){
35185             return;
35186         }
35187         
35188         if(this.bgimage.length && this.html.length){
35189             this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
35190         }
35191     },
35192     
35193     leave: function(e, el)
35194     {
35195         e.preventDefault();
35196         
35197         if(!this.isFitContainer || this.maskInverse  || this.videourl.length){
35198             return;
35199         }
35200         
35201         if(this.bgimage.length && this.html.length){
35202             this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
35203         }
35204     },
35205     
35206     onTouchStart: function(e, el)
35207     {
35208 //        e.preventDefault();
35209         
35210         this.touchmoved = false;
35211         
35212         if(!this.isFitContainer){
35213             return;
35214         }
35215         
35216         if(!this.bgimage.length || !this.html.length){
35217             return;
35218         }
35219         
35220         this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
35221         
35222         this.timer = new Date().getTime();
35223         
35224     },
35225     
35226     onTouchMove: function(e, el)
35227     {
35228         this.touchmoved = true;
35229     },
35230     
35231     onContextMenu : function(e,el)
35232     {
35233         e.preventDefault();
35234         e.stopPropagation();
35235         return false;
35236     },
35237     
35238     onTouchEnd: function(e, el)
35239     {
35240 //        e.preventDefault();
35241         
35242         if((new Date().getTime() - this.timer > 1000) || !this.href.length || this.touchmoved){
35243         
35244             this.leave(e,el);
35245             
35246             return;
35247         }
35248         
35249         if(!this.bgimage.length || !this.html.length){
35250             
35251             if(this.href.length){
35252                 window.location.href = this.href;
35253             }
35254             
35255             return;
35256         }
35257         
35258         if(!this.isFitContainer){
35259             return;
35260         }
35261         
35262         this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
35263         
35264         window.location.href = this.href;
35265     },
35266     
35267     //selection on single brick only
35268     selectBrick : function() {
35269         
35270         if (!this.parentId) {
35271             return;
35272         }
35273         
35274         var m = Roo.bootstrap.LayoutMasonry.get(this.parentId);
35275         var index = m.selectedBrick.indexOf(this.id);
35276         
35277         if ( index > -1) {
35278             m.selectedBrick.splice(index,1);
35279             this.el.removeClass(this.activeClass);
35280             return;
35281         }
35282         
35283         for(var i = 0; i < m.selectedBrick.length; i++) {
35284             var b = Roo.bootstrap.MasonryBrick.get(m.selectedBrick[i]);
35285             b.el.removeClass(b.activeClass);
35286         }
35287         
35288         m.selectedBrick = [];
35289         
35290         m.selectedBrick.push(this.id);
35291         this.el.addClass(this.activeClass);
35292         return;
35293     },
35294     
35295     isSelected : function(){
35296         return this.el.hasClass(this.activeClass);
35297         
35298     }
35299 });
35300
35301 Roo.apply(Roo.bootstrap.MasonryBrick, {
35302     
35303     //groups: {},
35304     groups : new Roo.util.MixedCollection(false, function(o) { return o.el.id; }),
35305      /**
35306     * register a Masonry Brick
35307     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
35308     */
35309     
35310     register : function(brick)
35311     {
35312         //this.groups[brick.id] = brick;
35313         this.groups.add(brick.id, brick);
35314     },
35315     /**
35316     * fetch a  masonry brick based on the masonry brick ID
35317     * @param {string} the masonry brick to add
35318     * @returns {Roo.bootstrap.MasonryBrick} the masonry brick
35319     */
35320     
35321     get: function(brick_id) 
35322     {
35323         // if (typeof(this.groups[brick_id]) == 'undefined') {
35324         //     return false;
35325         // }
35326         // return this.groups[brick_id] ;
35327         
35328         if(this.groups.key(brick_id)) {
35329             return this.groups.key(brick_id);
35330         }
35331         
35332         return false;
35333     }
35334     
35335     
35336     
35337 });
35338
35339  /*
35340  * - LGPL
35341  *
35342  * element
35343  * 
35344  */
35345
35346 /**
35347  * @class Roo.bootstrap.Brick
35348  * @extends Roo.bootstrap.Component
35349  * Bootstrap Brick class
35350  * 
35351  * @constructor
35352  * Create a new Brick
35353  * @param {Object} config The config object
35354  */
35355
35356 Roo.bootstrap.Brick = function(config){
35357     Roo.bootstrap.Brick.superclass.constructor.call(this, config);
35358     
35359     this.addEvents({
35360         // raw events
35361         /**
35362          * @event click
35363          * When a Brick is click
35364          * @param {Roo.bootstrap.Brick} this
35365          * @param {Roo.EventObject} e
35366          */
35367         "click" : true
35368     });
35369 };
35370
35371 Roo.extend(Roo.bootstrap.Brick, Roo.bootstrap.Component,  {
35372     
35373     /**
35374      * @cfg {String} title
35375      */   
35376     title : '',
35377     /**
35378      * @cfg {String} html
35379      */   
35380     html : '',
35381     /**
35382      * @cfg {String} bgimage
35383      */   
35384     bgimage : '',
35385     /**
35386      * @cfg {String} cls
35387      */   
35388     cls : '',
35389     /**
35390      * @cfg {String} href
35391      */   
35392     href : '',
35393     /**
35394      * @cfg {String} video
35395      */   
35396     video : '',
35397     /**
35398      * @cfg {Boolean} square
35399      */   
35400     square : true,
35401     
35402     getAutoCreate : function()
35403     {
35404         var cls = 'roo-brick';
35405         
35406         if(this.href.length){
35407             cls += ' roo-brick-link';
35408         }
35409         
35410         if(this.bgimage.length){
35411             cls += ' roo-brick-image';
35412         }
35413         
35414         if(!this.html.length && !this.bgimage.length){
35415             cls += ' roo-brick-center-title';
35416         }
35417         
35418         if(!this.html.length && this.bgimage.length){
35419             cls += ' roo-brick-bottom-title';
35420         }
35421         
35422         if(this.cls){
35423             cls += ' ' + this.cls;
35424         }
35425         
35426         var cfg = {
35427             tag: (this.href.length) ? 'a' : 'div',
35428             cls: cls,
35429             cn: [
35430                 {
35431                     tag: 'div',
35432                     cls: 'roo-brick-paragraph',
35433                     cn: []
35434                 }
35435             ]
35436         };
35437         
35438         if(this.href.length){
35439             cfg.href = this.href;
35440         }
35441         
35442         var cn = cfg.cn[0].cn;
35443         
35444         if(this.title.length){
35445             cn.push({
35446                 tag: 'h4',
35447                 cls: 'roo-brick-title',
35448                 html: this.title
35449             });
35450         }
35451         
35452         if(this.html.length){
35453             cn.push({
35454                 tag: 'p',
35455                 cls: 'roo-brick-text',
35456                 html: this.html
35457             });
35458         } else {
35459             cn.cls += ' hide';
35460         }
35461         
35462         if(this.bgimage.length){
35463             cfg.cn.push({
35464                 tag: 'img',
35465                 cls: 'roo-brick-image-view',
35466                 src: this.bgimage
35467             });
35468         }
35469         
35470         return cfg;
35471     },
35472     
35473     initEvents: function() 
35474     {
35475         if(this.title.length || this.html.length){
35476             this.el.on('mouseenter'  ,this.enter, this);
35477             this.el.on('mouseleave', this.leave, this);
35478         }
35479         
35480         Roo.EventManager.onWindowResize(this.resize, this); 
35481         
35482         if(this.bgimage.length){
35483             this.imageEl = this.el.select('.roo-brick-image-view', true).first();
35484             this.imageEl.on('load', this.onImageLoad, this);
35485             return;
35486         }
35487         
35488         this.resize();
35489     },
35490     
35491     onImageLoad : function()
35492     {
35493         this.resize();
35494     },
35495     
35496     resize : function()
35497     {
35498         var paragraph = this.el.select('.roo-brick-paragraph', true).first();
35499         
35500         paragraph.setHeight(paragraph.getWidth() + paragraph.getPadding('tb'));
35501         
35502         if(this.bgimage.length){
35503             var image = this.el.select('.roo-brick-image-view', true).first();
35504             
35505             image.setWidth(paragraph.getWidth());
35506             
35507             if(this.square){
35508                 image.setHeight(paragraph.getWidth());
35509             }
35510             
35511             this.el.setHeight(image.getHeight());
35512             paragraph.setHeight(image.getHeight());
35513             
35514         }
35515         
35516     },
35517     
35518     enter: function(e, el)
35519     {
35520         e.preventDefault();
35521         
35522         if(this.bgimage.length){
35523             this.el.select('.roo-brick-paragraph', true).first().setOpacity(0.9, true);
35524             this.el.select('.roo-brick-image-view', true).first().setOpacity(0.1, true);
35525         }
35526     },
35527     
35528     leave: function(e, el)
35529     {
35530         e.preventDefault();
35531         
35532         if(this.bgimage.length){
35533             this.el.select('.roo-brick-paragraph', true).first().setOpacity(0, true);
35534             this.el.select('.roo-brick-image-view', true).first().setOpacity(1, true);
35535         }
35536     }
35537     
35538 });
35539
35540  
35541
35542  /*
35543  * - LGPL
35544  *
35545  * Number field 
35546  */
35547
35548 /**
35549  * @class Roo.bootstrap.NumberField
35550  * @extends Roo.bootstrap.Input
35551  * Bootstrap NumberField class
35552  * 
35553  * 
35554  * 
35555  * 
35556  * @constructor
35557  * Create a new NumberField
35558  * @param {Object} config The config object
35559  */
35560
35561 Roo.bootstrap.NumberField = function(config){
35562     Roo.bootstrap.NumberField.superclass.constructor.call(this, config);
35563 };
35564
35565 Roo.extend(Roo.bootstrap.NumberField, Roo.bootstrap.Input, {
35566     
35567     /**
35568      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
35569      */
35570     allowDecimals : true,
35571     /**
35572      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
35573      */
35574     decimalSeparator : ".",
35575     /**
35576      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
35577      */
35578     decimalPrecision : 2,
35579     /**
35580      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
35581      */
35582     allowNegative : true,
35583     
35584     /**
35585      * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
35586      */
35587     allowZero: true,
35588     /**
35589      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
35590      */
35591     minValue : Number.NEGATIVE_INFINITY,
35592     /**
35593      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
35594      */
35595     maxValue : Number.MAX_VALUE,
35596     /**
35597      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
35598      */
35599     minText : "The minimum value for this field is {0}",
35600     /**
35601      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
35602      */
35603     maxText : "The maximum value for this field is {0}",
35604     /**
35605      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
35606      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
35607      */
35608     nanText : "{0} is not a valid number",
35609     /**
35610      * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
35611      */
35612     thousandsDelimiter : false,
35613     /**
35614      * @cfg {String} valueAlign alignment of value
35615      */
35616     valueAlign : "left",
35617
35618     getAutoCreate : function()
35619     {
35620         var hiddenInput = {
35621             tag: 'input',
35622             type: 'hidden',
35623             id: Roo.id(),
35624             cls: 'hidden-number-input'
35625         };
35626         
35627         if (this.name) {
35628             hiddenInput.name = this.name;
35629         }
35630         
35631         this.name = '';
35632         
35633         var cfg = Roo.bootstrap.NumberField.superclass.getAutoCreate.call(this);
35634         
35635         this.name = hiddenInput.name;
35636         
35637         if(cfg.cn.length > 0) {
35638             cfg.cn.push(hiddenInput);
35639         }
35640         
35641         return cfg;
35642     },
35643
35644     // private
35645     initEvents : function()
35646     {   
35647         Roo.bootstrap.NumberField.superclass.initEvents.call(this);
35648         
35649         var allowed = "0123456789";
35650         
35651         if(this.allowDecimals){
35652             allowed += this.decimalSeparator;
35653         }
35654         
35655         if(this.allowNegative){
35656             allowed += "-";
35657         }
35658         
35659         if(this.thousandsDelimiter) {
35660             allowed += ",";
35661         }
35662         
35663         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
35664         
35665         var keyPress = function(e){
35666             
35667             var k = e.getKey();
35668             
35669             var c = e.getCharCode();
35670             
35671             if(
35672                     (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
35673                     allowed.indexOf(String.fromCharCode(c)) === -1
35674             ){
35675                 e.stopEvent();
35676                 return;
35677             }
35678             
35679             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
35680                 return;
35681             }
35682             
35683             if(allowed.indexOf(String.fromCharCode(c)) === -1){
35684                 e.stopEvent();
35685             }
35686         };
35687         
35688         this.el.on("keypress", keyPress, this);
35689     },
35690     
35691     validateValue : function(value)
35692     {
35693         
35694         if(!Roo.bootstrap.NumberField.superclass.validateValue.call(this, value)){
35695             return false;
35696         }
35697         
35698         var num = this.parseValue(value);
35699         
35700         if(isNaN(num)){
35701             this.markInvalid(String.format(this.nanText, value));
35702             return false;
35703         }
35704         
35705         if(num < this.minValue){
35706             this.markInvalid(String.format(this.minText, this.minValue));
35707             return false;
35708         }
35709         
35710         if(num > this.maxValue){
35711             this.markInvalid(String.format(this.maxText, this.maxValue));
35712             return false;
35713         }
35714         
35715         return true;
35716     },
35717
35718     getValue : function()
35719     {
35720         var v = this.hiddenEl().getValue();
35721         
35722         return this.fixPrecision(this.parseValue(v));
35723     },
35724
35725     parseValue : function(value)
35726     {
35727         if(this.thousandsDelimiter) {
35728             value += "";
35729             r = new RegExp(",", "g");
35730             value = value.replace(r, "");
35731         }
35732         
35733         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
35734         return isNaN(value) ? '' : value;
35735     },
35736
35737     fixPrecision : function(value)
35738     {
35739         if(this.thousandsDelimiter) {
35740             value += "";
35741             r = new RegExp(",", "g");
35742             value = value.replace(r, "");
35743         }
35744         
35745         var nan = isNaN(value);
35746         
35747         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
35748             return nan ? '' : value;
35749         }
35750         return parseFloat(value).toFixed(this.decimalPrecision);
35751     },
35752
35753     setValue : function(v)
35754     {
35755         v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
35756         
35757         this.value = v;
35758         
35759         if(this.rendered){
35760             
35761             this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
35762             
35763             this.inputEl().dom.value = (v == '') ? '' :
35764                 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
35765             
35766             if(!this.allowZero && v === '0') {
35767                 this.hiddenEl().dom.value = '';
35768                 this.inputEl().dom.value = '';
35769             }
35770             
35771             this.validate();
35772         }
35773     },
35774
35775     decimalPrecisionFcn : function(v)
35776     {
35777         return Math.floor(v);
35778     },
35779
35780     beforeBlur : function()
35781     {
35782         var v = this.parseValue(this.getRawValue());
35783         
35784         if(v || v === 0 || v === ''){
35785             this.setValue(v);
35786         }
35787     },
35788     
35789     hiddenEl : function()
35790     {
35791         return this.el.select('input.hidden-number-input',true).first();
35792     }
35793     
35794 });
35795
35796  
35797
35798 /*
35799 * Licence: LGPL
35800 */
35801
35802 /**
35803  * @class Roo.bootstrap.DocumentSlider
35804  * @extends Roo.bootstrap.Component
35805  * Bootstrap DocumentSlider class
35806  * 
35807  * @constructor
35808  * Create a new DocumentViewer
35809  * @param {Object} config The config object
35810  */
35811
35812 Roo.bootstrap.DocumentSlider = function(config){
35813     Roo.bootstrap.DocumentSlider.superclass.constructor.call(this, config);
35814     
35815     this.files = [];
35816     
35817     this.addEvents({
35818         /**
35819          * @event initial
35820          * Fire after initEvent
35821          * @param {Roo.bootstrap.DocumentSlider} this
35822          */
35823         "initial" : true,
35824         /**
35825          * @event update
35826          * Fire after update
35827          * @param {Roo.bootstrap.DocumentSlider} this
35828          */
35829         "update" : true,
35830         /**
35831          * @event click
35832          * Fire after click
35833          * @param {Roo.bootstrap.DocumentSlider} this
35834          */
35835         "click" : true
35836     });
35837 };
35838
35839 Roo.extend(Roo.bootstrap.DocumentSlider, Roo.bootstrap.Component,  {
35840     
35841     files : false,
35842     
35843     indicator : 0,
35844     
35845     getAutoCreate : function()
35846     {
35847         var cfg = {
35848             tag : 'div',
35849             cls : 'roo-document-slider',
35850             cn : [
35851                 {
35852                     tag : 'div',
35853                     cls : 'roo-document-slider-header',
35854                     cn : [
35855                         {
35856                             tag : 'div',
35857                             cls : 'roo-document-slider-header-title'
35858                         }
35859                     ]
35860                 },
35861                 {
35862                     tag : 'div',
35863                     cls : 'roo-document-slider-body',
35864                     cn : [
35865                         {
35866                             tag : 'div',
35867                             cls : 'roo-document-slider-prev',
35868                             cn : [
35869                                 {
35870                                     tag : 'i',
35871                                     cls : 'fa fa-chevron-left'
35872                                 }
35873                             ]
35874                         },
35875                         {
35876                             tag : 'div',
35877                             cls : 'roo-document-slider-thumb',
35878                             cn : [
35879                                 {
35880                                     tag : 'img',
35881                                     cls : 'roo-document-slider-image'
35882                                 }
35883                             ]
35884                         },
35885                         {
35886                             tag : 'div',
35887                             cls : 'roo-document-slider-next',
35888                             cn : [
35889                                 {
35890                                     tag : 'i',
35891                                     cls : 'fa fa-chevron-right'
35892                                 }
35893                             ]
35894                         }
35895                     ]
35896                 }
35897             ]
35898         };
35899         
35900         return cfg;
35901     },
35902     
35903     initEvents : function()
35904     {
35905         this.headerEl = this.el.select('.roo-document-slider-header', true).first();
35906         this.headerEl.setVisibilityMode(Roo.Element.DISPLAY);
35907         
35908         this.titleEl = this.el.select('.roo-document-slider-header .roo-document-slider-header-title', true).first();
35909         this.titleEl.setVisibilityMode(Roo.Element.DISPLAY);
35910         
35911         this.bodyEl = this.el.select('.roo-document-slider-body', true).first();
35912         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
35913         
35914         this.thumbEl = this.el.select('.roo-document-slider-thumb', true).first();
35915         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
35916         
35917         this.imageEl = this.el.select('.roo-document-slider-image', true).first();
35918         this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
35919         
35920         this.prevIndicator = this.el.select('.roo-document-slider-prev i', true).first();
35921         this.prevIndicator.setVisibilityMode(Roo.Element.DISPLAY);
35922         
35923         this.nextIndicator = this.el.select('.roo-document-slider-next i', true).first();
35924         this.nextIndicator.setVisibilityMode(Roo.Element.DISPLAY);
35925         
35926         this.thumbEl.on('click', this.onClick, this);
35927         
35928         this.prevIndicator.on('click', this.prev, this);
35929         
35930         this.nextIndicator.on('click', this.next, this);
35931         
35932     },
35933     
35934     initial : function()
35935     {
35936         if(this.files.length){
35937             this.indicator = 1;
35938             this.update()
35939         }
35940         
35941         this.fireEvent('initial', this);
35942     },
35943     
35944     update : function()
35945     {
35946         this.imageEl.attr('src', this.files[this.indicator - 1]);
35947         
35948         this.titleEl.dom.innerHTML = String.format('{0} / {1}', this.indicator, this.files.length);
35949         
35950         this.prevIndicator.show();
35951         
35952         if(this.indicator == 1){
35953             this.prevIndicator.hide();
35954         }
35955         
35956         this.nextIndicator.show();
35957         
35958         if(this.indicator == this.files.length){
35959             this.nextIndicator.hide();
35960         }
35961         
35962         this.thumbEl.scrollTo('top');
35963         
35964         this.fireEvent('update', this);
35965     },
35966     
35967     onClick : function(e)
35968     {
35969         e.preventDefault();
35970         
35971         this.fireEvent('click', this);
35972     },
35973     
35974     prev : function(e)
35975     {
35976         e.preventDefault();
35977         
35978         this.indicator = Math.max(1, this.indicator - 1);
35979         
35980         this.update();
35981     },
35982     
35983     next : function(e)
35984     {
35985         e.preventDefault();
35986         
35987         this.indicator = Math.min(this.files.length, this.indicator + 1);
35988         
35989         this.update();
35990     }
35991 });
35992 /*
35993  * - LGPL
35994  *
35995  * RadioSet
35996  *
35997  *
35998  */
35999
36000 /**
36001  * @class Roo.bootstrap.RadioSet
36002  * @extends Roo.bootstrap.Input
36003  * Bootstrap RadioSet class
36004  * @cfg {String} indicatorpos (left|right) default left
36005  * @cfg {Boolean} inline (true|false) inline the element (default true)
36006  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the radio
36007  * @constructor
36008  * Create a new RadioSet
36009  * @param {Object} config The config object
36010  */
36011
36012 Roo.bootstrap.RadioSet = function(config){
36013     
36014     Roo.bootstrap.RadioSet.superclass.constructor.call(this, config);
36015     
36016     this.radioes = [];
36017     
36018     Roo.bootstrap.RadioSet.register(this);
36019     
36020     this.addEvents({
36021         /**
36022         * @event check
36023         * Fires when the element is checked or unchecked.
36024         * @param {Roo.bootstrap.RadioSet} this This radio
36025         * @param {Roo.bootstrap.Radio} item The checked item
36026         */
36027        check : true,
36028        /**
36029         * @event click
36030         * Fires when the element is click.
36031         * @param {Roo.bootstrap.RadioSet} this This radio set
36032         * @param {Roo.bootstrap.Radio} item The checked item
36033         * @param {Roo.EventObject} e The event object
36034         */
36035        click : true
36036     });
36037     
36038 };
36039
36040 Roo.extend(Roo.bootstrap.RadioSet, Roo.bootstrap.Input,  {
36041
36042     radioes : false,
36043     
36044     inline : true,
36045     
36046     weight : '',
36047     
36048     indicatorpos : 'left',
36049     
36050     getAutoCreate : function()
36051     {
36052         var label = {
36053             tag : 'label',
36054             cls : 'roo-radio-set-label',
36055             cn : [
36056                 {
36057                     tag : 'span',
36058                     html : this.fieldLabel
36059                 }
36060             ]
36061         };
36062         if (Roo.bootstrap.version == 3) {
36063             
36064             
36065             if(this.indicatorpos == 'left'){
36066                 label.cn.unshift({
36067                     tag : 'i',
36068                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
36069                     tooltip : 'This field is required'
36070                 });
36071             } else {
36072                 label.cn.push({
36073                     tag : 'i',
36074                     cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
36075                     tooltip : 'This field is required'
36076                 });
36077             }
36078         }
36079         var items = {
36080             tag : 'div',
36081             cls : 'roo-radio-set-items'
36082         };
36083         
36084         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
36085         
36086         if (align === 'left' && this.fieldLabel.length) {
36087             
36088             items = {
36089                 cls : "roo-radio-set-right", 
36090                 cn: [
36091                     items
36092                 ]
36093             };
36094             
36095             if(this.labelWidth > 12){
36096                 label.style = "width: " + this.labelWidth + 'px';
36097             }
36098             
36099             if(this.labelWidth < 13 && this.labelmd == 0){
36100                 this.labelmd = this.labelWidth;
36101             }
36102             
36103             if(this.labellg > 0){
36104                 label.cls += ' col-lg-' + this.labellg;
36105                 items.cls += ' col-lg-' + (12 - this.labellg);
36106             }
36107             
36108             if(this.labelmd > 0){
36109                 label.cls += ' col-md-' + this.labelmd;
36110                 items.cls += ' col-md-' + (12 - this.labelmd);
36111             }
36112             
36113             if(this.labelsm > 0){
36114                 label.cls += ' col-sm-' + this.labelsm;
36115                 items.cls += ' col-sm-' + (12 - this.labelsm);
36116             }
36117             
36118             if(this.labelxs > 0){
36119                 label.cls += ' col-xs-' + this.labelxs;
36120                 items.cls += ' col-xs-' + (12 - this.labelxs);
36121             }
36122         }
36123         
36124         var cfg = {
36125             tag : 'div',
36126             cls : 'roo-radio-set',
36127             cn : [
36128                 {
36129                     tag : 'input',
36130                     cls : 'roo-radio-set-input',
36131                     type : 'hidden',
36132                     name : this.name,
36133                     value : this.value ? this.value :  ''
36134                 },
36135                 label,
36136                 items
36137             ]
36138         };
36139         
36140         if(this.weight.length){
36141             cfg.cls += ' roo-radio-' + this.weight;
36142         }
36143         
36144         if(this.inline) {
36145             cfg.cls += ' roo-radio-set-inline';
36146         }
36147         
36148         var settings=this;
36149         ['xs','sm','md','lg'].map(function(size){
36150             if (settings[size]) {
36151                 cfg.cls += ' col-' + size + '-' + settings[size];
36152             }
36153         });
36154         
36155         return cfg;
36156         
36157     },
36158
36159     initEvents : function()
36160     {
36161         this.labelEl = this.el.select('.roo-radio-set-label', true).first();
36162         this.labelEl.setVisibilityMode(Roo.Element.DISPLAY);
36163         
36164         if(!this.fieldLabel.length){
36165             this.labelEl.hide();
36166         }
36167         
36168         this.itemsEl = this.el.select('.roo-radio-set-items', true).first();
36169         this.itemsEl.setVisibilityMode(Roo.Element.DISPLAY);
36170         
36171         this.indicator = this.indicatorEl();
36172         
36173         if(this.indicator){
36174             this.indicator.addClass('invisible');
36175         }
36176         
36177         this.originalValue = this.getValue();
36178         
36179     },
36180     
36181     inputEl: function ()
36182     {
36183         return this.el.select('.roo-radio-set-input', true).first();
36184     },
36185     
36186     getChildContainer : function()
36187     {
36188         return this.itemsEl;
36189     },
36190     
36191     register : function(item)
36192     {
36193         this.radioes.push(item);
36194         
36195     },
36196     
36197     validate : function()
36198     {   
36199         if(this.getVisibilityEl().hasClass('hidden')){
36200             return true;
36201         }
36202         
36203         var valid = false;
36204         
36205         Roo.each(this.radioes, function(i){
36206             if(!i.checked){
36207                 return;
36208             }
36209             
36210             valid = true;
36211             return false;
36212         });
36213         
36214         if(this.allowBlank) {
36215             return true;
36216         }
36217         
36218         if(this.disabled || valid){
36219             this.markValid();
36220             return true;
36221         }
36222         
36223         this.markInvalid();
36224         return false;
36225         
36226     },
36227     
36228     markValid : function()
36229     {
36230         if(this.labelEl.isVisible(true) && this.indicatorEl()){
36231             this.indicatorEl().removeClass('visible');
36232             this.indicatorEl().addClass('invisible');
36233         }
36234         
36235         
36236         if (Roo.bootstrap.version == 3) {
36237             this.el.removeClass([this.invalidClass, this.validClass]);
36238             this.el.addClass(this.validClass);
36239         } else {
36240             this.el.removeClass(['is-invalid','is-valid']);
36241             this.el.addClass(['is-valid']);
36242         }
36243         this.fireEvent('valid', this);
36244     },
36245     
36246     markInvalid : function(msg)
36247     {
36248         if(this.allowBlank || this.disabled){
36249             return;
36250         }
36251         
36252         if(this.labelEl.isVisible(true) && this.indicatorEl()){
36253             this.indicatorEl().removeClass('invisible');
36254             this.indicatorEl().addClass('visible');
36255         }
36256         if (Roo.bootstrap.version == 3) {
36257             this.el.removeClass([this.invalidClass, this.validClass]);
36258             this.el.addClass(this.invalidClass);
36259         } else {
36260             this.el.removeClass(['is-invalid','is-valid']);
36261             this.el.addClass(['is-invalid']);
36262         }
36263         
36264         this.fireEvent('invalid', this, msg);
36265         
36266     },
36267     
36268     setValue : function(v, suppressEvent)
36269     {   
36270         if(this.value === v){
36271             return;
36272         }
36273         
36274         this.value = v;
36275         
36276         if(this.rendered){
36277             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
36278         }
36279         
36280         Roo.each(this.radioes, function(i){
36281             i.checked = false;
36282             i.el.removeClass('checked');
36283         });
36284         
36285         Roo.each(this.radioes, function(i){
36286             
36287             if(i.value === v || i.value.toString() === v.toString()){
36288                 i.checked = true;
36289                 i.el.addClass('checked');
36290                 
36291                 if(suppressEvent !== true){
36292                     this.fireEvent('check', this, i);
36293                 }
36294                 
36295                 return false;
36296             }
36297             
36298         }, this);
36299         
36300         this.validate();
36301     },
36302     
36303     clearInvalid : function(){
36304         
36305         if(!this.el || this.preventMark){
36306             return;
36307         }
36308         
36309         this.el.removeClass([this.invalidClass]);
36310         
36311         this.fireEvent('valid', this);
36312     }
36313     
36314 });
36315
36316 Roo.apply(Roo.bootstrap.RadioSet, {
36317     
36318     groups: {},
36319     
36320     register : function(set)
36321     {
36322         this.groups[set.name] = set;
36323     },
36324     
36325     get: function(name) 
36326     {
36327         if (typeof(this.groups[name]) == 'undefined') {
36328             return false;
36329         }
36330         
36331         return this.groups[name] ;
36332     }
36333     
36334 });
36335 /*
36336  * Based on:
36337  * Ext JS Library 1.1.1
36338  * Copyright(c) 2006-2007, Ext JS, LLC.
36339  *
36340  * Originally Released Under LGPL - original licence link has changed is not relivant.
36341  *
36342  * Fork - LGPL
36343  * <script type="text/javascript">
36344  */
36345
36346
36347 /**
36348  * @class Roo.bootstrap.SplitBar
36349  * @extends Roo.util.Observable
36350  * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
36351  * <br><br>
36352  * Usage:
36353  * <pre><code>
36354 var split = new Roo.bootstrap.SplitBar("elementToDrag", "elementToSize",
36355                    Roo.bootstrap.SplitBar.HORIZONTAL, Roo.bootstrap.SplitBar.LEFT);
36356 split.setAdapter(new Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter("container"));
36357 split.minSize = 100;
36358 split.maxSize = 600;
36359 split.animate = true;
36360 split.on('moved', splitterMoved);
36361 </code></pre>
36362  * @constructor
36363  * Create a new SplitBar
36364  * @config {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar. 
36365  * @config {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged 
36366  * @config {Number} orientation (optional) Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
36367  * @config {Number} placement (optional) Either Roo.bootstrap.SplitBar.LEFT or Roo.bootstrap.SplitBar.RIGHT for horizontal or  
36368                         Roo.bootstrap.SplitBar.TOP or Roo.bootstrap.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
36369                         position of the SplitBar).
36370  */
36371 Roo.bootstrap.SplitBar = function(cfg){
36372     
36373     /** @private */
36374     
36375     //{
36376     //  dragElement : elm
36377     //  resizingElement: el,
36378         // optional..
36379     //    orientation : Either Roo.bootstrap.SplitBar.HORIZONTAL
36380     //    placement : Roo.bootstrap.SplitBar.LEFT  ,
36381         // existingProxy ???
36382     //}
36383     
36384     this.el = Roo.get(cfg.dragElement, true);
36385     this.el.dom.unselectable = "on";
36386     /** @private */
36387     this.resizingEl = Roo.get(cfg.resizingElement, true);
36388
36389     /**
36390      * @private
36391      * The orientation of the split. Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
36392      * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
36393      * @type Number
36394      */
36395     this.orientation = cfg.orientation || Roo.bootstrap.SplitBar.HORIZONTAL;
36396     
36397     /**
36398      * The minimum size of the resizing element. (Defaults to 0)
36399      * @type Number
36400      */
36401     this.minSize = 0;
36402     
36403     /**
36404      * The maximum size of the resizing element. (Defaults to 2000)
36405      * @type Number
36406      */
36407     this.maxSize = 2000;
36408     
36409     /**
36410      * Whether to animate the transition to the new size
36411      * @type Boolean
36412      */
36413     this.animate = false;
36414     
36415     /**
36416      * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
36417      * @type Boolean
36418      */
36419     this.useShim = false;
36420     
36421     /** @private */
36422     this.shim = null;
36423     
36424     if(!cfg.existingProxy){
36425         /** @private */
36426         this.proxy = Roo.bootstrap.SplitBar.createProxy(this.orientation);
36427     }else{
36428         this.proxy = Roo.get(cfg.existingProxy).dom;
36429     }
36430     /** @private */
36431     this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
36432     
36433     /** @private */
36434     this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
36435     
36436     /** @private */
36437     this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
36438     
36439     /** @private */
36440     this.dragSpecs = {};
36441     
36442     /**
36443      * @private The adapter to use to positon and resize elements
36444      */
36445     this.adapter = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
36446     this.adapter.init(this);
36447     
36448     if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
36449         /** @private */
36450         this.placement = cfg.placement || (this.el.getX() > this.resizingEl.getX() ? Roo.bootstrap.SplitBar.LEFT : Roo.bootstrap.SplitBar.RIGHT);
36451         this.el.addClass("roo-splitbar-h");
36452     }else{
36453         /** @private */
36454         this.placement = cfg.placement || (this.el.getY() > this.resizingEl.getY() ? Roo.bootstrap.SplitBar.TOP : Roo.bootstrap.SplitBar.BOTTOM);
36455         this.el.addClass("roo-splitbar-v");
36456     }
36457     
36458     this.addEvents({
36459         /**
36460          * @event resize
36461          * Fires when the splitter is moved (alias for {@link #event-moved})
36462          * @param {Roo.bootstrap.SplitBar} this
36463          * @param {Number} newSize the new width or height
36464          */
36465         "resize" : true,
36466         /**
36467          * @event moved
36468          * Fires when the splitter is moved
36469          * @param {Roo.bootstrap.SplitBar} this
36470          * @param {Number} newSize the new width or height
36471          */
36472         "moved" : true,
36473         /**
36474          * @event beforeresize
36475          * Fires before the splitter is dragged
36476          * @param {Roo.bootstrap.SplitBar} this
36477          */
36478         "beforeresize" : true,
36479
36480         "beforeapply" : true
36481     });
36482
36483     Roo.util.Observable.call(this);
36484 };
36485
36486 Roo.extend(Roo.bootstrap.SplitBar, Roo.util.Observable, {
36487     onStartProxyDrag : function(x, y){
36488         this.fireEvent("beforeresize", this);
36489         if(!this.overlay){
36490             var o = Roo.DomHelper.insertFirst(document.body,  {cls: "roo-drag-overlay", html: "&#160;"}, true);
36491             o.unselectable();
36492             o.enableDisplayMode("block");
36493             // all splitbars share the same overlay
36494             Roo.bootstrap.SplitBar.prototype.overlay = o;
36495         }
36496         this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
36497         this.overlay.show();
36498         Roo.get(this.proxy).setDisplayed("block");
36499         var size = this.adapter.getElementSize(this);
36500         this.activeMinSize = this.getMinimumSize();;
36501         this.activeMaxSize = this.getMaximumSize();;
36502         var c1 = size - this.activeMinSize;
36503         var c2 = Math.max(this.activeMaxSize - size, 0);
36504         if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
36505             this.dd.resetConstraints();
36506             this.dd.setXConstraint(
36507                 this.placement == Roo.bootstrap.SplitBar.LEFT ? c1 : c2, 
36508                 this.placement == Roo.bootstrap.SplitBar.LEFT ? c2 : c1
36509             );
36510             this.dd.setYConstraint(0, 0);
36511         }else{
36512             this.dd.resetConstraints();
36513             this.dd.setXConstraint(0, 0);
36514             this.dd.setYConstraint(
36515                 this.placement == Roo.bootstrap.SplitBar.TOP ? c1 : c2, 
36516                 this.placement == Roo.bootstrap.SplitBar.TOP ? c2 : c1
36517             );
36518          }
36519         this.dragSpecs.startSize = size;
36520         this.dragSpecs.startPoint = [x, y];
36521         Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
36522     },
36523     
36524     /** 
36525      * @private Called after the drag operation by the DDProxy
36526      */
36527     onEndProxyDrag : function(e){
36528         Roo.get(this.proxy).setDisplayed(false);
36529         var endPoint = Roo.lib.Event.getXY(e);
36530         if(this.overlay){
36531             this.overlay.hide();
36532         }
36533         var newSize;
36534         if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
36535             newSize = this.dragSpecs.startSize + 
36536                 (this.placement == Roo.bootstrap.SplitBar.LEFT ?
36537                     endPoint[0] - this.dragSpecs.startPoint[0] :
36538                     this.dragSpecs.startPoint[0] - endPoint[0]
36539                 );
36540         }else{
36541             newSize = this.dragSpecs.startSize + 
36542                 (this.placement == Roo.bootstrap.SplitBar.TOP ?
36543                     endPoint[1] - this.dragSpecs.startPoint[1] :
36544                     this.dragSpecs.startPoint[1] - endPoint[1]
36545                 );
36546         }
36547         newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
36548         if(newSize != this.dragSpecs.startSize){
36549             if(this.fireEvent('beforeapply', this, newSize) !== false){
36550                 this.adapter.setElementSize(this, newSize);
36551                 this.fireEvent("moved", this, newSize);
36552                 this.fireEvent("resize", this, newSize);
36553             }
36554         }
36555     },
36556     
36557     /**
36558      * Get the adapter this SplitBar uses
36559      * @return The adapter object
36560      */
36561     getAdapter : function(){
36562         return this.adapter;
36563     },
36564     
36565     /**
36566      * Set the adapter this SplitBar uses
36567      * @param {Object} adapter A SplitBar adapter object
36568      */
36569     setAdapter : function(adapter){
36570         this.adapter = adapter;
36571         this.adapter.init(this);
36572     },
36573     
36574     /**
36575      * Gets the minimum size for the resizing element
36576      * @return {Number} The minimum size
36577      */
36578     getMinimumSize : function(){
36579         return this.minSize;
36580     },
36581     
36582     /**
36583      * Sets the minimum size for the resizing element
36584      * @param {Number} minSize The minimum size
36585      */
36586     setMinimumSize : function(minSize){
36587         this.minSize = minSize;
36588     },
36589     
36590     /**
36591      * Gets the maximum size for the resizing element
36592      * @return {Number} The maximum size
36593      */
36594     getMaximumSize : function(){
36595         return this.maxSize;
36596     },
36597     
36598     /**
36599      * Sets the maximum size for the resizing element
36600      * @param {Number} maxSize The maximum size
36601      */
36602     setMaximumSize : function(maxSize){
36603         this.maxSize = maxSize;
36604     },
36605     
36606     /**
36607      * Sets the initialize size for the resizing element
36608      * @param {Number} size The initial size
36609      */
36610     setCurrentSize : function(size){
36611         var oldAnimate = this.animate;
36612         this.animate = false;
36613         this.adapter.setElementSize(this, size);
36614         this.animate = oldAnimate;
36615     },
36616     
36617     /**
36618      * Destroy this splitbar. 
36619      * @param {Boolean} removeEl True to remove the element
36620      */
36621     destroy : function(removeEl){
36622         if(this.shim){
36623             this.shim.remove();
36624         }
36625         this.dd.unreg();
36626         this.proxy.parentNode.removeChild(this.proxy);
36627         if(removeEl){
36628             this.el.remove();
36629         }
36630     }
36631 });
36632
36633 /**
36634  * @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.
36635  */
36636 Roo.bootstrap.SplitBar.createProxy = function(dir){
36637     var proxy = new Roo.Element(document.createElement("div"));
36638     proxy.unselectable();
36639     var cls = 'roo-splitbar-proxy';
36640     proxy.addClass(cls + ' ' + (dir == Roo.bootstrap.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
36641     document.body.appendChild(proxy.dom);
36642     return proxy.dom;
36643 };
36644
36645 /** 
36646  * @class Roo.bootstrap.SplitBar.BasicLayoutAdapter
36647  * Default Adapter. It assumes the splitter and resizing element are not positioned
36648  * elements and only gets/sets the width of the element. Generally used for table based layouts.
36649  */
36650 Roo.bootstrap.SplitBar.BasicLayoutAdapter = function(){
36651 };
36652
36653 Roo.bootstrap.SplitBar.BasicLayoutAdapter.prototype = {
36654     // do nothing for now
36655     init : function(s){
36656     
36657     },
36658     /**
36659      * Called before drag operations to get the current size of the resizing element. 
36660      * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
36661      */
36662      getElementSize : function(s){
36663         if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
36664             return s.resizingEl.getWidth();
36665         }else{
36666             return s.resizingEl.getHeight();
36667         }
36668     },
36669     
36670     /**
36671      * Called after drag operations to set the size of the resizing element.
36672      * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
36673      * @param {Number} newSize The new size to set
36674      * @param {Function} onComplete A function to be invoked when resizing is complete
36675      */
36676     setElementSize : function(s, newSize, onComplete){
36677         if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
36678             if(!s.animate){
36679                 s.resizingEl.setWidth(newSize);
36680                 if(onComplete){
36681                     onComplete(s, newSize);
36682                 }
36683             }else{
36684                 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
36685             }
36686         }else{
36687             
36688             if(!s.animate){
36689                 s.resizingEl.setHeight(newSize);
36690                 if(onComplete){
36691                     onComplete(s, newSize);
36692                 }
36693             }else{
36694                 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
36695             }
36696         }
36697     }
36698 };
36699
36700 /** 
36701  *@class Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter
36702  * @extends Roo.bootstrap.SplitBar.BasicLayoutAdapter
36703  * Adapter that  moves the splitter element to align with the resized sizing element. 
36704  * Used with an absolute positioned SplitBar.
36705  * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
36706  * document.body, make sure you assign an id to the body element.
36707  */
36708 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter = function(container){
36709     this.basic = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
36710     this.container = Roo.get(container);
36711 };
36712
36713 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter.prototype = {
36714     init : function(s){
36715         this.basic.init(s);
36716     },
36717     
36718     getElementSize : function(s){
36719         return this.basic.getElementSize(s);
36720     },
36721     
36722     setElementSize : function(s, newSize, onComplete){
36723         this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
36724     },
36725     
36726     moveSplitter : function(s){
36727         var yes = Roo.bootstrap.SplitBar;
36728         switch(s.placement){
36729             case yes.LEFT:
36730                 s.el.setX(s.resizingEl.getRight());
36731                 break;
36732             case yes.RIGHT:
36733                 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
36734                 break;
36735             case yes.TOP:
36736                 s.el.setY(s.resizingEl.getBottom());
36737                 break;
36738             case yes.BOTTOM:
36739                 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
36740                 break;
36741         }
36742     }
36743 };
36744
36745 /**
36746  * Orientation constant - Create a vertical SplitBar
36747  * @static
36748  * @type Number
36749  */
36750 Roo.bootstrap.SplitBar.VERTICAL = 1;
36751
36752 /**
36753  * Orientation constant - Create a horizontal SplitBar
36754  * @static
36755  * @type Number
36756  */
36757 Roo.bootstrap.SplitBar.HORIZONTAL = 2;
36758
36759 /**
36760  * Placement constant - The resizing element is to the left of the splitter element
36761  * @static
36762  * @type Number
36763  */
36764 Roo.bootstrap.SplitBar.LEFT = 1;
36765
36766 /**
36767  * Placement constant - The resizing element is to the right of the splitter element
36768  * @static
36769  * @type Number
36770  */
36771 Roo.bootstrap.SplitBar.RIGHT = 2;
36772
36773 /**
36774  * Placement constant - The resizing element is positioned above the splitter element
36775  * @static
36776  * @type Number
36777  */
36778 Roo.bootstrap.SplitBar.TOP = 3;
36779
36780 /**
36781  * Placement constant - The resizing element is positioned under splitter element
36782  * @static
36783  * @type Number
36784  */
36785 Roo.bootstrap.SplitBar.BOTTOM = 4;
36786 Roo.namespace("Roo.bootstrap.layout");/*
36787  * Based on:
36788  * Ext JS Library 1.1.1
36789  * Copyright(c) 2006-2007, Ext JS, LLC.
36790  *
36791  * Originally Released Under LGPL - original licence link has changed is not relivant.
36792  *
36793  * Fork - LGPL
36794  * <script type="text/javascript">
36795  */
36796
36797 /**
36798  * @class Roo.bootstrap.layout.Manager
36799  * @extends Roo.bootstrap.Component
36800  * Base class for layout managers.
36801  */
36802 Roo.bootstrap.layout.Manager = function(config)
36803 {
36804     Roo.bootstrap.layout.Manager.superclass.constructor.call(this,config);
36805
36806
36807
36808
36809
36810     /** false to disable window resize monitoring @type Boolean */
36811     this.monitorWindowResize = true;
36812     this.regions = {};
36813     this.addEvents({
36814         /**
36815          * @event layout
36816          * Fires when a layout is performed.
36817          * @param {Roo.LayoutManager} this
36818          */
36819         "layout" : true,
36820         /**
36821          * @event regionresized
36822          * Fires when the user resizes a region.
36823          * @param {Roo.LayoutRegion} region The resized region
36824          * @param {Number} newSize The new size (width for east/west, height for north/south)
36825          */
36826         "regionresized" : true,
36827         /**
36828          * @event regioncollapsed
36829          * Fires when a region is collapsed.
36830          * @param {Roo.LayoutRegion} region The collapsed region
36831          */
36832         "regioncollapsed" : true,
36833         /**
36834          * @event regionexpanded
36835          * Fires when a region is expanded.
36836          * @param {Roo.LayoutRegion} region The expanded region
36837          */
36838         "regionexpanded" : true
36839     });
36840     this.updating = false;
36841
36842     if (config.el) {
36843         this.el = Roo.get(config.el);
36844         this.initEvents();
36845     }
36846
36847 };
36848
36849 Roo.extend(Roo.bootstrap.layout.Manager, Roo.bootstrap.Component, {
36850
36851
36852     regions : null,
36853
36854     monitorWindowResize : true,
36855
36856
36857     updating : false,
36858
36859
36860     onRender : function(ct, position)
36861     {
36862         if(!this.el){
36863             this.el = Roo.get(ct);
36864             this.initEvents();
36865         }
36866         //this.fireEvent('render',this);
36867     },
36868
36869
36870     initEvents: function()
36871     {
36872
36873
36874         // ie scrollbar fix
36875         if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
36876             document.body.scroll = "no";
36877         }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
36878             this.el.position('relative');
36879         }
36880         this.id = this.el.id;
36881         this.el.addClass("roo-layout-container");
36882         Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
36883         if(this.el.dom != document.body ) {
36884             this.el.on('resize', this.layout,this);
36885             this.el.on('show', this.layout,this);
36886         }
36887
36888     },
36889
36890     /**
36891      * Returns true if this layout is currently being updated
36892      * @return {Boolean}
36893      */
36894     isUpdating : function(){
36895         return this.updating;
36896     },
36897
36898     /**
36899      * Suspend the LayoutManager from doing auto-layouts while
36900      * making multiple add or remove calls
36901      */
36902     beginUpdate : function(){
36903         this.updating = true;
36904     },
36905
36906     /**
36907      * Restore auto-layouts and optionally disable the manager from performing a layout
36908      * @param {Boolean} noLayout true to disable a layout update
36909      */
36910     endUpdate : function(noLayout){
36911         this.updating = false;
36912         if(!noLayout){
36913             this.layout();
36914         }
36915     },
36916
36917     layout: function(){
36918         // abstract...
36919     },
36920
36921     onRegionResized : function(region, newSize){
36922         this.fireEvent("regionresized", region, newSize);
36923         this.layout();
36924     },
36925
36926     onRegionCollapsed : function(region){
36927         this.fireEvent("regioncollapsed", region);
36928     },
36929
36930     onRegionExpanded : function(region){
36931         this.fireEvent("regionexpanded", region);
36932     },
36933
36934     /**
36935      * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
36936      * performs box-model adjustments.
36937      * @return {Object} The size as an object {width: (the width), height: (the height)}
36938      */
36939     getViewSize : function()
36940     {
36941         var size;
36942         if(this.el.dom != document.body){
36943             size = this.el.getSize();
36944         }else{
36945             size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
36946         }
36947         size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
36948         size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
36949         return size;
36950     },
36951
36952     /**
36953      * Returns the Element this layout is bound to.
36954      * @return {Roo.Element}
36955      */
36956     getEl : function(){
36957         return this.el;
36958     },
36959
36960     /**
36961      * Returns the specified region.
36962      * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
36963      * @return {Roo.LayoutRegion}
36964      */
36965     getRegion : function(target){
36966         return this.regions[target.toLowerCase()];
36967     },
36968
36969     onWindowResize : function(){
36970         if(this.monitorWindowResize){
36971             this.layout();
36972         }
36973     }
36974 });
36975 /*
36976  * Based on:
36977  * Ext JS Library 1.1.1
36978  * Copyright(c) 2006-2007, Ext JS, LLC.
36979  *
36980  * Originally Released Under LGPL - original licence link has changed is not relivant.
36981  *
36982  * Fork - LGPL
36983  * <script type="text/javascript">
36984  */
36985 /**
36986  * @class Roo.bootstrap.layout.Border
36987  * @extends Roo.bootstrap.layout.Manager
36988  * This class represents a common layout manager used in desktop applications. For screenshots and more details,
36989  * please see: examples/bootstrap/nested.html<br><br>
36990  
36991 <b>The container the layout is rendered into can be either the body element or any other element.
36992 If it is not the body element, the container needs to either be an absolute positioned element,
36993 or you will need to add "position:relative" to the css of the container.  You will also need to specify
36994 the container size if it is not the body element.</b>
36995
36996 * @constructor
36997 * Create a new Border
36998 * @param {Object} config Configuration options
36999  */
37000 Roo.bootstrap.layout.Border = function(config){
37001     config = config || {};
37002     Roo.bootstrap.layout.Border.superclass.constructor.call(this, config);
37003     
37004     
37005     
37006     Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
37007         if(config[region]){
37008             config[region].region = region;
37009             this.addRegion(config[region]);
37010         }
37011     },this);
37012     
37013 };
37014
37015 Roo.bootstrap.layout.Border.regions =  ["north","south","east","west","center"];
37016
37017 Roo.extend(Roo.bootstrap.layout.Border, Roo.bootstrap.layout.Manager, {
37018     
37019     parent : false, // this might point to a 'nest' or a ???
37020     
37021     /**
37022      * Creates and adds a new region if it doesn't already exist.
37023      * @param {String} target The target region key (north, south, east, west or center).
37024      * @param {Object} config The regions config object
37025      * @return {BorderLayoutRegion} The new region
37026      */
37027     addRegion : function(config)
37028     {
37029         if(!this.regions[config.region]){
37030             var r = this.factory(config);
37031             this.bindRegion(r);
37032         }
37033         return this.regions[config.region];
37034     },
37035
37036     // private (kinda)
37037     bindRegion : function(r){
37038         this.regions[r.config.region] = r;
37039         
37040         r.on("visibilitychange",    this.layout, this);
37041         r.on("paneladded",          this.layout, this);
37042         r.on("panelremoved",        this.layout, this);
37043         r.on("invalidated",         this.layout, this);
37044         r.on("resized",             this.onRegionResized, this);
37045         r.on("collapsed",           this.onRegionCollapsed, this);
37046         r.on("expanded",            this.onRegionExpanded, this);
37047     },
37048
37049     /**
37050      * Performs a layout update.
37051      */
37052     layout : function()
37053     {
37054         if(this.updating) {
37055             return;
37056         }
37057         
37058         // render all the rebions if they have not been done alreayd?
37059         Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
37060             if(this.regions[region] && !this.regions[region].bodyEl){
37061                 this.regions[region].onRender(this.el)
37062             }
37063         },this);
37064         
37065         var size = this.getViewSize();
37066         var w = size.width;
37067         var h = size.height;
37068         var centerW = w;
37069         var centerH = h;
37070         var centerY = 0;
37071         var centerX = 0;
37072         //var x = 0, y = 0;
37073
37074         var rs = this.regions;
37075         var north = rs["north"];
37076         var south = rs["south"]; 
37077         var west = rs["west"];
37078         var east = rs["east"];
37079         var center = rs["center"];
37080         //if(this.hideOnLayout){ // not supported anymore
37081             //c.el.setStyle("display", "none");
37082         //}
37083         if(north && north.isVisible()){
37084             var b = north.getBox();
37085             var m = north.getMargins();
37086             b.width = w - (m.left+m.right);
37087             b.x = m.left;
37088             b.y = m.top;
37089             centerY = b.height + b.y + m.bottom;
37090             centerH -= centerY;
37091             north.updateBox(this.safeBox(b));
37092         }
37093         if(south && south.isVisible()){
37094             var b = south.getBox();
37095             var m = south.getMargins();
37096             b.width = w - (m.left+m.right);
37097             b.x = m.left;
37098             var totalHeight = (b.height + m.top + m.bottom);
37099             b.y = h - totalHeight + m.top;
37100             centerH -= totalHeight;
37101             south.updateBox(this.safeBox(b));
37102         }
37103         if(west && west.isVisible()){
37104             var b = west.getBox();
37105             var m = west.getMargins();
37106             b.height = centerH - (m.top+m.bottom);
37107             b.x = m.left;
37108             b.y = centerY + m.top;
37109             var totalWidth = (b.width + m.left + m.right);
37110             centerX += totalWidth;
37111             centerW -= totalWidth;
37112             west.updateBox(this.safeBox(b));
37113         }
37114         if(east && east.isVisible()){
37115             var b = east.getBox();
37116             var m = east.getMargins();
37117             b.height = centerH - (m.top+m.bottom);
37118             var totalWidth = (b.width + m.left + m.right);
37119             b.x = w - totalWidth + m.left;
37120             b.y = centerY + m.top;
37121             centerW -= totalWidth;
37122             east.updateBox(this.safeBox(b));
37123         }
37124         if(center){
37125             var m = center.getMargins();
37126             var centerBox = {
37127                 x: centerX + m.left,
37128                 y: centerY + m.top,
37129                 width: centerW - (m.left+m.right),
37130                 height: centerH - (m.top+m.bottom)
37131             };
37132             //if(this.hideOnLayout){
37133                 //center.el.setStyle("display", "block");
37134             //}
37135             center.updateBox(this.safeBox(centerBox));
37136         }
37137         this.el.repaint();
37138         this.fireEvent("layout", this);
37139     },
37140
37141     // private
37142     safeBox : function(box){
37143         box.width = Math.max(0, box.width);
37144         box.height = Math.max(0, box.height);
37145         return box;
37146     },
37147
37148     /**
37149      * Adds a ContentPanel (or subclass) to this layout.
37150      * @param {String} target The target region key (north, south, east, west or center).
37151      * @param {Roo.ContentPanel} panel The panel to add
37152      * @return {Roo.ContentPanel} The added panel
37153      */
37154     add : function(target, panel){
37155          
37156         target = target.toLowerCase();
37157         return this.regions[target].add(panel);
37158     },
37159
37160     /**
37161      * Remove a ContentPanel (or subclass) to this layout.
37162      * @param {String} target The target region key (north, south, east, west or center).
37163      * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
37164      * @return {Roo.ContentPanel} The removed panel
37165      */
37166     remove : function(target, panel){
37167         target = target.toLowerCase();
37168         return this.regions[target].remove(panel);
37169     },
37170
37171     /**
37172      * Searches all regions for a panel with the specified id
37173      * @param {String} panelId
37174      * @return {Roo.ContentPanel} The panel or null if it wasn't found
37175      */
37176     findPanel : function(panelId){
37177         var rs = this.regions;
37178         for(var target in rs){
37179             if(typeof rs[target] != "function"){
37180                 var p = rs[target].getPanel(panelId);
37181                 if(p){
37182                     return p;
37183                 }
37184             }
37185         }
37186         return null;
37187     },
37188
37189     /**
37190      * Searches all regions for a panel with the specified id and activates (shows) it.
37191      * @param {String/ContentPanel} panelId The panels id or the panel itself
37192      * @return {Roo.ContentPanel} The shown panel or null
37193      */
37194     showPanel : function(panelId) {
37195       var rs = this.regions;
37196       for(var target in rs){
37197          var r = rs[target];
37198          if(typeof r != "function"){
37199             if(r.hasPanel(panelId)){
37200                return r.showPanel(panelId);
37201             }
37202          }
37203       }
37204       return null;
37205    },
37206
37207    /**
37208      * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
37209      * @param {Roo.state.Provider} provider (optional) An alternate state provider
37210      */
37211    /*
37212     restoreState : function(provider){
37213         if(!provider){
37214             provider = Roo.state.Manager;
37215         }
37216         var sm = new Roo.LayoutStateManager();
37217         sm.init(this, provider);
37218     },
37219 */
37220  
37221  
37222     /**
37223      * Adds a xtype elements to the layout.
37224      * <pre><code>
37225
37226 layout.addxtype({
37227        xtype : 'ContentPanel',
37228        region: 'west',
37229        items: [ .... ]
37230    }
37231 );
37232
37233 layout.addxtype({
37234         xtype : 'NestedLayoutPanel',
37235         region: 'west',
37236         layout: {
37237            center: { },
37238            west: { }   
37239         },
37240         items : [ ... list of content panels or nested layout panels.. ]
37241    }
37242 );
37243 </code></pre>
37244      * @param {Object} cfg Xtype definition of item to add.
37245      */
37246     addxtype : function(cfg)
37247     {
37248         // basically accepts a pannel...
37249         // can accept a layout region..!?!?
37250         //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
37251         
37252         
37253         // theory?  children can only be panels??
37254         
37255         //if (!cfg.xtype.match(/Panel$/)) {
37256         //    return false;
37257         //}
37258         var ret = false;
37259         
37260         if (typeof(cfg.region) == 'undefined') {
37261             Roo.log("Failed to add Panel, region was not set");
37262             Roo.log(cfg);
37263             return false;
37264         }
37265         var region = cfg.region;
37266         delete cfg.region;
37267         
37268           
37269         var xitems = [];
37270         if (cfg.items) {
37271             xitems = cfg.items;
37272             delete cfg.items;
37273         }
37274         var nb = false;
37275         
37276         if ( region == 'center') {
37277             Roo.log("Center: " + cfg.title);
37278         }
37279         
37280         
37281         switch(cfg.xtype) 
37282         {
37283             case 'Content':  // ContentPanel (el, cfg)
37284             case 'Scroll':  // ContentPanel (el, cfg)
37285             case 'View': 
37286                 cfg.autoCreate = cfg.autoCreate || true;
37287                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
37288                 //} else {
37289                 //    var el = this.el.createChild();
37290                 //    ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
37291                 //}
37292                 
37293                 this.add(region, ret);
37294                 break;
37295             
37296             /*
37297             case 'TreePanel': // our new panel!
37298                 cfg.el = this.el.createChild();
37299                 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
37300                 this.add(region, ret);
37301                 break;
37302             */
37303             
37304             case 'Nest': 
37305                 // create a new Layout (which is  a Border Layout...
37306                 
37307                 var clayout = cfg.layout;
37308                 clayout.el  = this.el.createChild();
37309                 clayout.items   = clayout.items  || [];
37310                 
37311                 delete cfg.layout;
37312                 
37313                 // replace this exitems with the clayout ones..
37314                 xitems = clayout.items;
37315                  
37316                 // force background off if it's in center...
37317                 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
37318                     cfg.background = false;
37319                 }
37320                 cfg.layout  = new Roo.bootstrap.layout.Border(clayout);
37321                 
37322                 
37323                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
37324                 //console.log('adding nested layout panel '  + cfg.toSource());
37325                 this.add(region, ret);
37326                 nb = {}; /// find first...
37327                 break;
37328             
37329             case 'Grid':
37330                 
37331                 // needs grid and region
37332                 
37333                 //var el = this.getRegion(region).el.createChild();
37334                 /*
37335                  *var el = this.el.createChild();
37336                 // create the grid first...
37337                 cfg.grid.container = el;
37338                 cfg.grid = new cfg.grid.xns[cfg.grid.xtype](cfg.grid);
37339                 */
37340                 
37341                 if (region == 'center' && this.active ) {
37342                     cfg.background = false;
37343                 }
37344                 
37345                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
37346                 
37347                 this.add(region, ret);
37348                 /*
37349                 if (cfg.background) {
37350                     // render grid on panel activation (if panel background)
37351                     ret.on('activate', function(gp) {
37352                         if (!gp.grid.rendered) {
37353                     //        gp.grid.render(el);
37354                         }
37355                     });
37356                 } else {
37357                   //  cfg.grid.render(el);
37358                 }
37359                 */
37360                 break;
37361            
37362            
37363             case 'Border': // it can get called on it'self... - might need to check if this is fixed?
37364                 // it was the old xcomponent building that caused this before.
37365                 // espeically if border is the top element in the tree.
37366                 ret = this;
37367                 break; 
37368                 
37369                     
37370                 
37371                 
37372                 
37373             default:
37374                 /*
37375                 if (typeof(Roo[cfg.xtype]) != 'undefined') {
37376                     
37377                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
37378                     this.add(region, ret);
37379                 } else {
37380                 */
37381                     Roo.log(cfg);
37382                     throw "Can not add '" + cfg.xtype + "' to Border";
37383                     return null;
37384              
37385                                 
37386              
37387         }
37388         this.beginUpdate();
37389         // add children..
37390         var region = '';
37391         var abn = {};
37392         Roo.each(xitems, function(i)  {
37393             region = nb && i.region ? i.region : false;
37394             
37395             var add = ret.addxtype(i);
37396            
37397             if (region) {
37398                 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
37399                 if (!i.background) {
37400                     abn[region] = nb[region] ;
37401                 }
37402             }
37403             
37404         });
37405         this.endUpdate();
37406
37407         // make the last non-background panel active..
37408         //if (nb) { Roo.log(abn); }
37409         if (nb) {
37410             
37411             for(var r in abn) {
37412                 region = this.getRegion(r);
37413                 if (region) {
37414                     // tried using nb[r], but it does not work..
37415                      
37416                     region.showPanel(abn[r]);
37417                    
37418                 }
37419             }
37420         }
37421         return ret;
37422         
37423     },
37424     
37425     
37426 // private
37427     factory : function(cfg)
37428     {
37429         
37430         var validRegions = Roo.bootstrap.layout.Border.regions;
37431
37432         var target = cfg.region;
37433         cfg.mgr = this;
37434         
37435         var r = Roo.bootstrap.layout;
37436         Roo.log(target);
37437         switch(target){
37438             case "north":
37439                 return new r.North(cfg);
37440             case "south":
37441                 return new r.South(cfg);
37442             case "east":
37443                 return new r.East(cfg);
37444             case "west":
37445                 return new r.West(cfg);
37446             case "center":
37447                 return new r.Center(cfg);
37448         }
37449         throw 'Layout region "'+target+'" not supported.';
37450     }
37451     
37452     
37453 });
37454  /*
37455  * Based on:
37456  * Ext JS Library 1.1.1
37457  * Copyright(c) 2006-2007, Ext JS, LLC.
37458  *
37459  * Originally Released Under LGPL - original licence link has changed is not relivant.
37460  *
37461  * Fork - LGPL
37462  * <script type="text/javascript">
37463  */
37464  
37465 /**
37466  * @class Roo.bootstrap.layout.Basic
37467  * @extends Roo.util.Observable
37468  * This class represents a lightweight region in a layout manager. This region does not move dom nodes
37469  * and does not have a titlebar, tabs or any other features. All it does is size and position 
37470  * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
37471  * @cfg {Roo.bootstrap.layout.Manager}   mgr The manager
37472  * @cfg {string}   region  the region that it inhabits..
37473  * @cfg {bool}   skipConfig skip config?
37474  * 
37475
37476  */
37477 Roo.bootstrap.layout.Basic = function(config){
37478     
37479     this.mgr = config.mgr;
37480     
37481     this.position = config.region;
37482     
37483     var skipConfig = config.skipConfig;
37484     
37485     this.events = {
37486         /**
37487          * @scope Roo.BasicLayoutRegion
37488          */
37489         
37490         /**
37491          * @event beforeremove
37492          * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
37493          * @param {Roo.LayoutRegion} this
37494          * @param {Roo.ContentPanel} panel The panel
37495          * @param {Object} e The cancel event object
37496          */
37497         "beforeremove" : true,
37498         /**
37499          * @event invalidated
37500          * Fires when the layout for this region is changed.
37501          * @param {Roo.LayoutRegion} this
37502          */
37503         "invalidated" : true,
37504         /**
37505          * @event visibilitychange
37506          * Fires when this region is shown or hidden 
37507          * @param {Roo.LayoutRegion} this
37508          * @param {Boolean} visibility true or false
37509          */
37510         "visibilitychange" : true,
37511         /**
37512          * @event paneladded
37513          * Fires when a panel is added. 
37514          * @param {Roo.LayoutRegion} this
37515          * @param {Roo.ContentPanel} panel The panel
37516          */
37517         "paneladded" : true,
37518         /**
37519          * @event panelremoved
37520          * Fires when a panel is removed. 
37521          * @param {Roo.LayoutRegion} this
37522          * @param {Roo.ContentPanel} panel The panel
37523          */
37524         "panelremoved" : true,
37525         /**
37526          * @event beforecollapse
37527          * Fires when this region before collapse.
37528          * @param {Roo.LayoutRegion} this
37529          */
37530         "beforecollapse" : true,
37531         /**
37532          * @event collapsed
37533          * Fires when this region is collapsed.
37534          * @param {Roo.LayoutRegion} this
37535          */
37536         "collapsed" : true,
37537         /**
37538          * @event expanded
37539          * Fires when this region is expanded.
37540          * @param {Roo.LayoutRegion} this
37541          */
37542         "expanded" : true,
37543         /**
37544          * @event slideshow
37545          * Fires when this region is slid into view.
37546          * @param {Roo.LayoutRegion} this
37547          */
37548         "slideshow" : true,
37549         /**
37550          * @event slidehide
37551          * Fires when this region slides out of view. 
37552          * @param {Roo.LayoutRegion} this
37553          */
37554         "slidehide" : true,
37555         /**
37556          * @event panelactivated
37557          * Fires when a panel is activated. 
37558          * @param {Roo.LayoutRegion} this
37559          * @param {Roo.ContentPanel} panel The activated panel
37560          */
37561         "panelactivated" : true,
37562         /**
37563          * @event resized
37564          * Fires when the user resizes this region. 
37565          * @param {Roo.LayoutRegion} this
37566          * @param {Number} newSize The new size (width for east/west, height for north/south)
37567          */
37568         "resized" : true
37569     };
37570     /** A collection of panels in this region. @type Roo.util.MixedCollection */
37571     this.panels = new Roo.util.MixedCollection();
37572     this.panels.getKey = this.getPanelId.createDelegate(this);
37573     this.box = null;
37574     this.activePanel = null;
37575     // ensure listeners are added...
37576     
37577     if (config.listeners || config.events) {
37578         Roo.bootstrap.layout.Basic.superclass.constructor.call(this, {
37579             listeners : config.listeners || {},
37580             events : config.events || {}
37581         });
37582     }
37583     
37584     if(skipConfig !== true){
37585         this.applyConfig(config);
37586     }
37587 };
37588
37589 Roo.extend(Roo.bootstrap.layout.Basic, Roo.util.Observable,
37590 {
37591     getPanelId : function(p){
37592         return p.getId();
37593     },
37594     
37595     applyConfig : function(config){
37596         this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
37597         this.config = config;
37598         
37599     },
37600     
37601     /**
37602      * Resizes the region to the specified size. For vertical regions (west, east) this adjusts 
37603      * the width, for horizontal (north, south) the height.
37604      * @param {Number} newSize The new width or height
37605      */
37606     resizeTo : function(newSize){
37607         var el = this.el ? this.el :
37608                  (this.activePanel ? this.activePanel.getEl() : null);
37609         if(el){
37610             switch(this.position){
37611                 case "east":
37612                 case "west":
37613                     el.setWidth(newSize);
37614                     this.fireEvent("resized", this, newSize);
37615                 break;
37616                 case "north":
37617                 case "south":
37618                     el.setHeight(newSize);
37619                     this.fireEvent("resized", this, newSize);
37620                 break;                
37621             }
37622         }
37623     },
37624     
37625     getBox : function(){
37626         return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
37627     },
37628     
37629     getMargins : function(){
37630         return this.margins;
37631     },
37632     
37633     updateBox : function(box){
37634         this.box = box;
37635         var el = this.activePanel.getEl();
37636         el.dom.style.left = box.x + "px";
37637         el.dom.style.top = box.y + "px";
37638         this.activePanel.setSize(box.width, box.height);
37639     },
37640     
37641     /**
37642      * Returns the container element for this region.
37643      * @return {Roo.Element}
37644      */
37645     getEl : function(){
37646         return this.activePanel;
37647     },
37648     
37649     /**
37650      * Returns true if this region is currently visible.
37651      * @return {Boolean}
37652      */
37653     isVisible : function(){
37654         return this.activePanel ? true : false;
37655     },
37656     
37657     setActivePanel : function(panel){
37658         panel = this.getPanel(panel);
37659         if(this.activePanel && this.activePanel != panel){
37660             this.activePanel.setActiveState(false);
37661             this.activePanel.getEl().setLeftTop(-10000,-10000);
37662         }
37663         this.activePanel = panel;
37664         panel.setActiveState(true);
37665         if(this.box){
37666             panel.setSize(this.box.width, this.box.height);
37667         }
37668         this.fireEvent("panelactivated", this, panel);
37669         this.fireEvent("invalidated");
37670     },
37671     
37672     /**
37673      * Show the specified panel.
37674      * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
37675      * @return {Roo.ContentPanel} The shown panel or null
37676      */
37677     showPanel : function(panel){
37678         panel = this.getPanel(panel);
37679         if(panel){
37680             this.setActivePanel(panel);
37681         }
37682         return panel;
37683     },
37684     
37685     /**
37686      * Get the active panel for this region.
37687      * @return {Roo.ContentPanel} The active panel or null
37688      */
37689     getActivePanel : function(){
37690         return this.activePanel;
37691     },
37692     
37693     /**
37694      * Add the passed ContentPanel(s)
37695      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
37696      * @return {Roo.ContentPanel} The panel added (if only one was added)
37697      */
37698     add : function(panel){
37699         if(arguments.length > 1){
37700             for(var i = 0, len = arguments.length; i < len; i++) {
37701                 this.add(arguments[i]);
37702             }
37703             return null;
37704         }
37705         if(this.hasPanel(panel)){
37706             this.showPanel(panel);
37707             return panel;
37708         }
37709         var el = panel.getEl();
37710         if(el.dom.parentNode != this.mgr.el.dom){
37711             this.mgr.el.dom.appendChild(el.dom);
37712         }
37713         if(panel.setRegion){
37714             panel.setRegion(this);
37715         }
37716         this.panels.add(panel);
37717         el.setStyle("position", "absolute");
37718         if(!panel.background){
37719             this.setActivePanel(panel);
37720             if(this.config.initialSize && this.panels.getCount()==1){
37721                 this.resizeTo(this.config.initialSize);
37722             }
37723         }
37724         this.fireEvent("paneladded", this, panel);
37725         return panel;
37726     },
37727     
37728     /**
37729      * Returns true if the panel is in this region.
37730      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
37731      * @return {Boolean}
37732      */
37733     hasPanel : function(panel){
37734         if(typeof panel == "object"){ // must be panel obj
37735             panel = panel.getId();
37736         }
37737         return this.getPanel(panel) ? true : false;
37738     },
37739     
37740     /**
37741      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
37742      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
37743      * @param {Boolean} preservePanel Overrides the config preservePanel option
37744      * @return {Roo.ContentPanel} The panel that was removed
37745      */
37746     remove : function(panel, preservePanel){
37747         panel = this.getPanel(panel);
37748         if(!panel){
37749             return null;
37750         }
37751         var e = {};
37752         this.fireEvent("beforeremove", this, panel, e);
37753         if(e.cancel === true){
37754             return null;
37755         }
37756         var panelId = panel.getId();
37757         this.panels.removeKey(panelId);
37758         return panel;
37759     },
37760     
37761     /**
37762      * Returns the panel specified or null if it's not in this region.
37763      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
37764      * @return {Roo.ContentPanel}
37765      */
37766     getPanel : function(id){
37767         if(typeof id == "object"){ // must be panel obj
37768             return id;
37769         }
37770         return this.panels.get(id);
37771     },
37772     
37773     /**
37774      * Returns this regions position (north/south/east/west/center).
37775      * @return {String} 
37776      */
37777     getPosition: function(){
37778         return this.position;    
37779     }
37780 });/*
37781  * Based on:
37782  * Ext JS Library 1.1.1
37783  * Copyright(c) 2006-2007, Ext JS, LLC.
37784  *
37785  * Originally Released Under LGPL - original licence link has changed is not relivant.
37786  *
37787  * Fork - LGPL
37788  * <script type="text/javascript">
37789  */
37790  
37791 /**
37792  * @class Roo.bootstrap.layout.Region
37793  * @extends Roo.bootstrap.layout.Basic
37794  * This class represents a region in a layout manager.
37795  
37796  * @cfg {Object}    margins         Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
37797  * @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})
37798  * @cfg {String}    tabPosition     (top|bottom) "top" or "bottom" (defaults to "bottom")
37799  * @cfg {Boolean}   alwaysShowTabs  True to always display tabs even when there is only 1 panel (defaults to false)
37800  * @cfg {Boolean}   autoScroll      True to enable overflow scrolling (defaults to false)
37801  * @cfg {Boolean}   titlebar        True to display a title bar (defaults to true)
37802  * @cfg {String}    title           The title for the region (overrides panel titles)
37803  * @cfg {Boolean}   animate         True to animate expand/collapse (defaults to false)
37804  * @cfg {Boolean}   autoHide        False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
37805  * @cfg {Boolean}   preservePanels  True to preserve removed panels so they can be readded later (defaults to false)
37806  * @cfg {Boolean}   closeOnTab      True to place the close icon on the tabs instead of the region titlebar (defaults to false)
37807  * @cfg {Boolean}   hideTabs        True to hide the tab strip (defaults to false)
37808  * @cfg {Boolean}   resizeTabs      True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
37809  *                      the space available, similar to FireFox 1.5 tabs (defaults to false)
37810  * @cfg {Number}    minTabWidth     The minimum tab width (defaults to 40)
37811  * @cfg {Number}    preferredTabWidth The preferred tab width (defaults to 150)
37812  * @cfg {String}    overflow       (hidden|visible) if you have menus in the region, then you need to set this to visible.
37813
37814  * @cfg {Boolean}   hidden          True to start the region hidden (defaults to false)
37815  * @cfg {Boolean}   hideWhenEmpty   True to hide the region when it has no panels
37816  * @cfg {Boolean}   disableTabTips  True to disable tab tooltips
37817  * @cfg {Number}    width           For East/West panels
37818  * @cfg {Number}    height          For North/South panels
37819  * @cfg {Boolean}   split           To show the splitter
37820  * @cfg {Boolean}   toolbar         xtype configuration for a toolbar - shows on right of tabbar
37821  * 
37822  * @cfg {string}   cls             Extra CSS classes to add to region
37823  * 
37824  * @cfg {Roo.bootstrap.layout.Manager}   mgr The manager
37825  * @cfg {string}   region  the region that it inhabits..
37826  *
37827
37828  * @xxxcfg {Boolean}   collapsible     DISABLED False to disable collapsing (defaults to true)
37829  * @xxxcfg {Boolean}   collapsed       DISABLED True to set the initial display to collapsed (defaults to false)
37830
37831  * @xxxcfg {String}    collapsedTitle  DISABLED Optional string message to display in the collapsed block of a north or south region
37832  * @xxxxcfg {Boolean}   floatable       DISABLED False to disable floating (defaults to true)
37833  * @xxxxcfg {Boolean}   showPin         True to show a pin button NOT SUPPORTED YET
37834  */
37835 Roo.bootstrap.layout.Region = function(config)
37836 {
37837     this.applyConfig(config);
37838
37839     var mgr = config.mgr;
37840     var pos = config.region;
37841     config.skipConfig = true;
37842     Roo.bootstrap.layout.Region.superclass.constructor.call(this, config);
37843     
37844     if (mgr.el) {
37845         this.onRender(mgr.el);   
37846     }
37847      
37848     this.visible = true;
37849     this.collapsed = false;
37850     this.unrendered_panels = [];
37851 };
37852
37853 Roo.extend(Roo.bootstrap.layout.Region, Roo.bootstrap.layout.Basic, {
37854
37855     position: '', // set by wrapper (eg. north/south etc..)
37856     unrendered_panels : null,  // unrendered panels.
37857     
37858     tabPosition : false,
37859     
37860     mgr: false, // points to 'Border'
37861     
37862     
37863     createBody : function(){
37864         /** This region's body element 
37865         * @type Roo.Element */
37866         this.bodyEl = this.el.createChild({
37867                 tag: "div",
37868                 cls: "roo-layout-panel-body tab-content" // bootstrap added...
37869         });
37870     },
37871
37872     onRender: function(ctr, pos)
37873     {
37874         var dh = Roo.DomHelper;
37875         /** This region's container element 
37876         * @type Roo.Element */
37877         this.el = dh.append(ctr.dom, {
37878                 tag: "div",
37879                 cls: (this.config.cls || '') + " roo-layout-region roo-layout-panel roo-layout-panel-" + this.position
37880             }, true);
37881         /** This region's title element 
37882         * @type Roo.Element */
37883     
37884         this.titleEl = dh.append(this.el.dom,  {
37885                 tag: "div",
37886                 unselectable: "on",
37887                 cls: "roo-unselectable roo-layout-panel-hd breadcrumb roo-layout-title-" + this.position,
37888                 children:[
37889                     {tag: "span", cls: "roo-unselectable roo-layout-panel-hd-text", unselectable: "on", html: "&#160;"},
37890                     {tag: "div", cls: "roo-unselectable roo-layout-panel-hd-tools", unselectable: "on"}
37891                 ]
37892             }, true);
37893         
37894         this.titleEl.enableDisplayMode();
37895         /** This region's title text element 
37896         * @type HTMLElement */
37897         this.titleTextEl = this.titleEl.dom.firstChild;
37898         this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
37899         /*
37900         this.closeBtn = this.createTool(this.tools.dom, "roo-layout-close");
37901         this.closeBtn.enableDisplayMode();
37902         this.closeBtn.on("click", this.closeClicked, this);
37903         this.closeBtn.hide();
37904     */
37905         this.createBody(this.config);
37906         if(this.config.hideWhenEmpty){
37907             this.hide();
37908             this.on("paneladded", this.validateVisibility, this);
37909             this.on("panelremoved", this.validateVisibility, this);
37910         }
37911         if(this.autoScroll){
37912             this.bodyEl.setStyle("overflow", "auto");
37913         }else{
37914             this.bodyEl.setStyle("overflow", this.config.overflow || 'hidden');
37915         }
37916         //if(c.titlebar !== false){
37917             if((!this.config.titlebar && !this.config.title) || this.config.titlebar === false){
37918                 this.titleEl.hide();
37919             }else{
37920                 this.titleEl.show();
37921                 if(this.config.title){
37922                     this.titleTextEl.innerHTML = this.config.title;
37923                 }
37924             }
37925         //}
37926         if(this.config.collapsed){
37927             this.collapse(true);
37928         }
37929         if(this.config.hidden){
37930             this.hide();
37931         }
37932         
37933         if (this.unrendered_panels && this.unrendered_panels.length) {
37934             for (var i =0;i< this.unrendered_panels.length; i++) {
37935                 this.add(this.unrendered_panels[i]);
37936             }
37937             this.unrendered_panels = null;
37938             
37939         }
37940         
37941     },
37942     
37943     applyConfig : function(c)
37944     {
37945         /*
37946          *if(c.collapsible && this.position != "center" && !this.collapsedEl){
37947             var dh = Roo.DomHelper;
37948             if(c.titlebar !== false){
37949                 this.collapseBtn = this.createTool(this.tools.dom, "roo-layout-collapse-"+this.position);
37950                 this.collapseBtn.on("click", this.collapse, this);
37951                 this.collapseBtn.enableDisplayMode();
37952                 /*
37953                 if(c.showPin === true || this.showPin){
37954                     this.stickBtn = this.createTool(this.tools.dom, "roo-layout-stick");
37955                     this.stickBtn.enableDisplayMode();
37956                     this.stickBtn.on("click", this.expand, this);
37957                     this.stickBtn.hide();
37958                 }
37959                 
37960             }
37961             */
37962             /** This region's collapsed element
37963             * @type Roo.Element */
37964             /*
37965              *
37966             this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
37967                 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
37968             ]}, true);
37969             
37970             if(c.floatable !== false){
37971                this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
37972                this.collapsedEl.on("click", this.collapseClick, this);
37973             }
37974
37975             if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
37976                 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
37977                    id: "message", unselectable: "on", style:{"float":"left"}});
37978                this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
37979              }
37980             this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
37981             this.expandBtn.on("click", this.expand, this);
37982             
37983         }
37984         
37985         if(this.collapseBtn){
37986             this.collapseBtn.setVisible(c.collapsible == true);
37987         }
37988         
37989         this.cmargins = c.cmargins || this.cmargins ||
37990                          (this.position == "west" || this.position == "east" ?
37991                              {top: 0, left: 2, right:2, bottom: 0} :
37992                              {top: 2, left: 0, right:0, bottom: 2});
37993         */
37994         this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
37995         
37996         
37997         this.tabPosition = [ 'top','bottom', 'west'].indexOf(c.tabPosition) > -1 ? c.tabPosition : "top";
37998         
37999         this.autoScroll = c.autoScroll || false;
38000         
38001         
38002        
38003         
38004         this.duration = c.duration || .30;
38005         this.slideDuration = c.slideDuration || .45;
38006         this.config = c;
38007        
38008     },
38009     /**
38010      * Returns true if this region is currently visible.
38011      * @return {Boolean}
38012      */
38013     isVisible : function(){
38014         return this.visible;
38015     },
38016
38017     /**
38018      * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
38019      * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&amp;#160;")
38020      */
38021     //setCollapsedTitle : function(title){
38022     //    title = title || "&#160;";
38023      //   if(this.collapsedTitleTextEl){
38024       //      this.collapsedTitleTextEl.innerHTML = title;
38025        // }
38026     //},
38027
38028     getBox : function(){
38029         var b;
38030       //  if(!this.collapsed){
38031             b = this.el.getBox(false, true);
38032        // }else{
38033           //  b = this.collapsedEl.getBox(false, true);
38034         //}
38035         return b;
38036     },
38037
38038     getMargins : function(){
38039         return this.margins;
38040         //return this.collapsed ? this.cmargins : this.margins;
38041     },
38042 /*
38043     highlight : function(){
38044         this.el.addClass("x-layout-panel-dragover");
38045     },
38046
38047     unhighlight : function(){
38048         this.el.removeClass("x-layout-panel-dragover");
38049     },
38050 */
38051     updateBox : function(box)
38052     {
38053         if (!this.bodyEl) {
38054             return; // not rendered yet..
38055         }
38056         
38057         this.box = box;
38058         if(!this.collapsed){
38059             this.el.dom.style.left = box.x + "px";
38060             this.el.dom.style.top = box.y + "px";
38061             this.updateBody(box.width, box.height);
38062         }else{
38063             this.collapsedEl.dom.style.left = box.x + "px";
38064             this.collapsedEl.dom.style.top = box.y + "px";
38065             this.collapsedEl.setSize(box.width, box.height);
38066         }
38067         if(this.tabs){
38068             this.tabs.autoSizeTabs();
38069         }
38070     },
38071
38072     updateBody : function(w, h)
38073     {
38074         if(w !== null){
38075             this.el.setWidth(w);
38076             w -= this.el.getBorderWidth("rl");
38077             if(this.config.adjustments){
38078                 w += this.config.adjustments[0];
38079             }
38080         }
38081         if(h !== null && h > 0){
38082             this.el.setHeight(h);
38083             h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
38084             h -= this.el.getBorderWidth("tb");
38085             if(this.config.adjustments){
38086                 h += this.config.adjustments[1];
38087             }
38088             this.bodyEl.setHeight(h);
38089             if(this.tabs){
38090                 h = this.tabs.syncHeight(h);
38091             }
38092         }
38093         if(this.panelSize){
38094             w = w !== null ? w : this.panelSize.width;
38095             h = h !== null ? h : this.panelSize.height;
38096         }
38097         if(this.activePanel){
38098             var el = this.activePanel.getEl();
38099             w = w !== null ? w : el.getWidth();
38100             h = h !== null ? h : el.getHeight();
38101             this.panelSize = {width: w, height: h};
38102             this.activePanel.setSize(w, h);
38103         }
38104         if(Roo.isIE && this.tabs){
38105             this.tabs.el.repaint();
38106         }
38107     },
38108
38109     /**
38110      * Returns the container element for this region.
38111      * @return {Roo.Element}
38112      */
38113     getEl : function(){
38114         return this.el;
38115     },
38116
38117     /**
38118      * Hides this region.
38119      */
38120     hide : function(){
38121         //if(!this.collapsed){
38122             this.el.dom.style.left = "-2000px";
38123             this.el.hide();
38124         //}else{
38125          //   this.collapsedEl.dom.style.left = "-2000px";
38126          //   this.collapsedEl.hide();
38127        // }
38128         this.visible = false;
38129         this.fireEvent("visibilitychange", this, false);
38130     },
38131
38132     /**
38133      * Shows this region if it was previously hidden.
38134      */
38135     show : function(){
38136         //if(!this.collapsed){
38137             this.el.show();
38138         //}else{
38139         //    this.collapsedEl.show();
38140        // }
38141         this.visible = true;
38142         this.fireEvent("visibilitychange", this, true);
38143     },
38144 /*
38145     closeClicked : function(){
38146         if(this.activePanel){
38147             this.remove(this.activePanel);
38148         }
38149     },
38150
38151     collapseClick : function(e){
38152         if(this.isSlid){
38153            e.stopPropagation();
38154            this.slideIn();
38155         }else{
38156            e.stopPropagation();
38157            this.slideOut();
38158         }
38159     },
38160 */
38161     /**
38162      * Collapses this region.
38163      * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
38164      */
38165     /*
38166     collapse : function(skipAnim, skipCheck = false){
38167         if(this.collapsed) {
38168             return;
38169         }
38170         
38171         if(skipCheck || this.fireEvent("beforecollapse", this) != false){
38172             
38173             this.collapsed = true;
38174             if(this.split){
38175                 this.split.el.hide();
38176             }
38177             if(this.config.animate && skipAnim !== true){
38178                 this.fireEvent("invalidated", this);
38179                 this.animateCollapse();
38180             }else{
38181                 this.el.setLocation(-20000,-20000);
38182                 this.el.hide();
38183                 this.collapsedEl.show();
38184                 this.fireEvent("collapsed", this);
38185                 this.fireEvent("invalidated", this);
38186             }
38187         }
38188         
38189     },
38190 */
38191     animateCollapse : function(){
38192         // overridden
38193     },
38194
38195     /**
38196      * Expands this region if it was previously collapsed.
38197      * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
38198      * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
38199      */
38200     /*
38201     expand : function(e, skipAnim){
38202         if(e) {
38203             e.stopPropagation();
38204         }
38205         if(!this.collapsed || this.el.hasActiveFx()) {
38206             return;
38207         }
38208         if(this.isSlid){
38209             this.afterSlideIn();
38210             skipAnim = true;
38211         }
38212         this.collapsed = false;
38213         if(this.config.animate && skipAnim !== true){
38214             this.animateExpand();
38215         }else{
38216             this.el.show();
38217             if(this.split){
38218                 this.split.el.show();
38219             }
38220             this.collapsedEl.setLocation(-2000,-2000);
38221             this.collapsedEl.hide();
38222             this.fireEvent("invalidated", this);
38223             this.fireEvent("expanded", this);
38224         }
38225     },
38226 */
38227     animateExpand : function(){
38228         // overridden
38229     },
38230
38231     initTabs : function()
38232     {
38233         //this.bodyEl.setStyle("overflow", "hidden"); -- this is set in render?
38234         
38235         var ts = new Roo.bootstrap.panel.Tabs({
38236             el: this.bodyEl.dom,
38237             region : this,
38238             tabPosition: this.tabPosition ? this.tabPosition  : 'top',
38239             disableTooltips: this.config.disableTabTips,
38240             toolbar : this.config.toolbar
38241         });
38242         
38243         if(this.config.hideTabs){
38244             ts.stripWrap.setDisplayed(false);
38245         }
38246         this.tabs = ts;
38247         ts.resizeTabs = this.config.resizeTabs === true;
38248         ts.minTabWidth = this.config.minTabWidth || 40;
38249         ts.maxTabWidth = this.config.maxTabWidth || 250;
38250         ts.preferredTabWidth = this.config.preferredTabWidth || 150;
38251         ts.monitorResize = false;
38252         //ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden"); // this is set in render?
38253         ts.bodyEl.addClass('roo-layout-tabs-body');
38254         this.panels.each(this.initPanelAsTab, this);
38255     },
38256
38257     initPanelAsTab : function(panel){
38258         var ti = this.tabs.addTab(
38259             panel.getEl().id,
38260             panel.getTitle(),
38261             null,
38262             this.config.closeOnTab && panel.isClosable(),
38263             panel.tpl
38264         );
38265         if(panel.tabTip !== undefined){
38266             ti.setTooltip(panel.tabTip);
38267         }
38268         ti.on("activate", function(){
38269               this.setActivePanel(panel);
38270         }, this);
38271         
38272         if(this.config.closeOnTab){
38273             ti.on("beforeclose", function(t, e){
38274                 e.cancel = true;
38275                 this.remove(panel);
38276             }, this);
38277         }
38278         
38279         panel.tabItem = ti;
38280         
38281         return ti;
38282     },
38283
38284     updatePanelTitle : function(panel, title)
38285     {
38286         if(this.activePanel == panel){
38287             this.updateTitle(title);
38288         }
38289         if(this.tabs){
38290             var ti = this.tabs.getTab(panel.getEl().id);
38291             ti.setText(title);
38292             if(panel.tabTip !== undefined){
38293                 ti.setTooltip(panel.tabTip);
38294             }
38295         }
38296     },
38297
38298     updateTitle : function(title){
38299         if(this.titleTextEl && !this.config.title){
38300             this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : "&#160;");
38301         }
38302     },
38303
38304     setActivePanel : function(panel)
38305     {
38306         panel = this.getPanel(panel);
38307         if(this.activePanel && this.activePanel != panel){
38308             if(this.activePanel.setActiveState(false) === false){
38309                 return;
38310             }
38311         }
38312         this.activePanel = panel;
38313         panel.setActiveState(true);
38314         if(this.panelSize){
38315             panel.setSize(this.panelSize.width, this.panelSize.height);
38316         }
38317         if(this.closeBtn){
38318             this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
38319         }
38320         this.updateTitle(panel.getTitle());
38321         if(this.tabs){
38322             this.fireEvent("invalidated", this);
38323         }
38324         this.fireEvent("panelactivated", this, panel);
38325     },
38326
38327     /**
38328      * Shows the specified panel.
38329      * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
38330      * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
38331      */
38332     showPanel : function(panel)
38333     {
38334         panel = this.getPanel(panel);
38335         if(panel){
38336             if(this.tabs){
38337                 var tab = this.tabs.getTab(panel.getEl().id);
38338                 if(tab.isHidden()){
38339                     this.tabs.unhideTab(tab.id);
38340                 }
38341                 tab.activate();
38342             }else{
38343                 this.setActivePanel(panel);
38344             }
38345         }
38346         return panel;
38347     },
38348
38349     /**
38350      * Get the active panel for this region.
38351      * @return {Roo.ContentPanel} The active panel or null
38352      */
38353     getActivePanel : function(){
38354         return this.activePanel;
38355     },
38356
38357     validateVisibility : function(){
38358         if(this.panels.getCount() < 1){
38359             this.updateTitle("&#160;");
38360             this.closeBtn.hide();
38361             this.hide();
38362         }else{
38363             if(!this.isVisible()){
38364                 this.show();
38365             }
38366         }
38367     },
38368
38369     /**
38370      * Adds the passed ContentPanel(s) to this region.
38371      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
38372      * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
38373      */
38374     add : function(panel)
38375     {
38376         if(arguments.length > 1){
38377             for(var i = 0, len = arguments.length; i < len; i++) {
38378                 this.add(arguments[i]);
38379             }
38380             return null;
38381         }
38382         
38383         // if we have not been rendered yet, then we can not really do much of this..
38384         if (!this.bodyEl) {
38385             this.unrendered_panels.push(panel);
38386             return panel;
38387         }
38388         
38389         
38390         
38391         
38392         if(this.hasPanel(panel)){
38393             this.showPanel(panel);
38394             return panel;
38395         }
38396         panel.setRegion(this);
38397         this.panels.add(panel);
38398        /* if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
38399             // sinle panel - no tab...?? would it not be better to render it with the tabs,
38400             // and hide them... ???
38401             this.bodyEl.dom.appendChild(panel.getEl().dom);
38402             if(panel.background !== true){
38403                 this.setActivePanel(panel);
38404             }
38405             this.fireEvent("paneladded", this, panel);
38406             return panel;
38407         }
38408         */
38409         if(!this.tabs){
38410             this.initTabs();
38411         }else{
38412             this.initPanelAsTab(panel);
38413         }
38414         
38415         
38416         if(panel.background !== true){
38417             this.tabs.activate(panel.getEl().id);
38418         }
38419         this.fireEvent("paneladded", this, panel);
38420         return panel;
38421     },
38422
38423     /**
38424      * Hides the tab for the specified panel.
38425      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
38426      */
38427     hidePanel : function(panel){
38428         if(this.tabs && (panel = this.getPanel(panel))){
38429             this.tabs.hideTab(panel.getEl().id);
38430         }
38431     },
38432
38433     /**
38434      * Unhides the tab for a previously hidden panel.
38435      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
38436      */
38437     unhidePanel : function(panel){
38438         if(this.tabs && (panel = this.getPanel(panel))){
38439             this.tabs.unhideTab(panel.getEl().id);
38440         }
38441     },
38442
38443     clearPanels : function(){
38444         while(this.panels.getCount() > 0){
38445              this.remove(this.panels.first());
38446         }
38447     },
38448
38449     /**
38450      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
38451      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
38452      * @param {Boolean} preservePanel Overrides the config preservePanel option
38453      * @return {Roo.ContentPanel} The panel that was removed
38454      */
38455     remove : function(panel, preservePanel)
38456     {
38457         panel = this.getPanel(panel);
38458         if(!panel){
38459             return null;
38460         }
38461         var e = {};
38462         this.fireEvent("beforeremove", this, panel, e);
38463         if(e.cancel === true){
38464             return null;
38465         }
38466         preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
38467         var panelId = panel.getId();
38468         this.panels.removeKey(panelId);
38469         if(preservePanel){
38470             document.body.appendChild(panel.getEl().dom);
38471         }
38472         if(this.tabs){
38473             this.tabs.removeTab(panel.getEl().id);
38474         }else if (!preservePanel){
38475             this.bodyEl.dom.removeChild(panel.getEl().dom);
38476         }
38477         if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
38478             var p = this.panels.first();
38479             var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
38480             tempEl.appendChild(p.getEl().dom);
38481             this.bodyEl.update("");
38482             this.bodyEl.dom.appendChild(p.getEl().dom);
38483             tempEl = null;
38484             this.updateTitle(p.getTitle());
38485             this.tabs = null;
38486             this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
38487             this.setActivePanel(p);
38488         }
38489         panel.setRegion(null);
38490         if(this.activePanel == panel){
38491             this.activePanel = null;
38492         }
38493         if(this.config.autoDestroy !== false && preservePanel !== true){
38494             try{panel.destroy();}catch(e){}
38495         }
38496         this.fireEvent("panelremoved", this, panel);
38497         return panel;
38498     },
38499
38500     /**
38501      * Returns the TabPanel component used by this region
38502      * @return {Roo.TabPanel}
38503      */
38504     getTabs : function(){
38505         return this.tabs;
38506     },
38507
38508     createTool : function(parentEl, className){
38509         var btn = Roo.DomHelper.append(parentEl, {
38510             tag: "div",
38511             cls: "x-layout-tools-button",
38512             children: [ {
38513                 tag: "div",
38514                 cls: "roo-layout-tools-button-inner " + className,
38515                 html: "&#160;"
38516             }]
38517         }, true);
38518         btn.addClassOnOver("roo-layout-tools-button-over");
38519         return btn;
38520     }
38521 });/*
38522  * Based on:
38523  * Ext JS Library 1.1.1
38524  * Copyright(c) 2006-2007, Ext JS, LLC.
38525  *
38526  * Originally Released Under LGPL - original licence link has changed is not relivant.
38527  *
38528  * Fork - LGPL
38529  * <script type="text/javascript">
38530  */
38531  
38532
38533
38534 /**
38535  * @class Roo.SplitLayoutRegion
38536  * @extends Roo.LayoutRegion
38537  * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
38538  */
38539 Roo.bootstrap.layout.Split = function(config){
38540     this.cursor = config.cursor;
38541     Roo.bootstrap.layout.Split.superclass.constructor.call(this, config);
38542 };
38543
38544 Roo.extend(Roo.bootstrap.layout.Split, Roo.bootstrap.layout.Region,
38545 {
38546     splitTip : "Drag to resize.",
38547     collapsibleSplitTip : "Drag to resize. Double click to hide.",
38548     useSplitTips : false,
38549
38550     applyConfig : function(config){
38551         Roo.bootstrap.layout.Split.superclass.applyConfig.call(this, config);
38552     },
38553     
38554     onRender : function(ctr,pos) {
38555         
38556         Roo.bootstrap.layout.Split.superclass.onRender.call(this, ctr,pos);
38557         if(!this.config.split){
38558             return;
38559         }
38560         if(!this.split){
38561             
38562             var splitEl = Roo.DomHelper.append(ctr.dom,  {
38563                             tag: "div",
38564                             id: this.el.id + "-split",
38565                             cls: "roo-layout-split roo-layout-split-"+this.position,
38566                             html: "&#160;"
38567             });
38568             /** The SplitBar for this region 
38569             * @type Roo.SplitBar */
38570             // does not exist yet...
38571             Roo.log([this.position, this.orientation]);
38572             
38573             this.split = new Roo.bootstrap.SplitBar({
38574                 dragElement : splitEl,
38575                 resizingElement: this.el,
38576                 orientation : this.orientation
38577             });
38578             
38579             this.split.on("moved", this.onSplitMove, this);
38580             this.split.useShim = this.config.useShim === true;
38581             this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
38582             if(this.useSplitTips){
38583                 this.split.el.dom.title = this.config.collapsible ? this.collapsibleSplitTip : this.splitTip;
38584             }
38585             //if(config.collapsible){
38586             //    this.split.el.on("dblclick", this.collapse,  this);
38587             //}
38588         }
38589         if(typeof this.config.minSize != "undefined"){
38590             this.split.minSize = this.config.minSize;
38591         }
38592         if(typeof this.config.maxSize != "undefined"){
38593             this.split.maxSize = this.config.maxSize;
38594         }
38595         if(this.config.hideWhenEmpty || this.config.hidden || this.config.collapsed){
38596             this.hideSplitter();
38597         }
38598         
38599     },
38600
38601     getHMaxSize : function(){
38602          var cmax = this.config.maxSize || 10000;
38603          var center = this.mgr.getRegion("center");
38604          return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
38605     },
38606
38607     getVMaxSize : function(){
38608          var cmax = this.config.maxSize || 10000;
38609          var center = this.mgr.getRegion("center");
38610          return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
38611     },
38612
38613     onSplitMove : function(split, newSize){
38614         this.fireEvent("resized", this, newSize);
38615     },
38616     
38617     /** 
38618      * Returns the {@link Roo.SplitBar} for this region.
38619      * @return {Roo.SplitBar}
38620      */
38621     getSplitBar : function(){
38622         return this.split;
38623     },
38624     
38625     hide : function(){
38626         this.hideSplitter();
38627         Roo.bootstrap.layout.Split.superclass.hide.call(this);
38628     },
38629
38630     hideSplitter : function(){
38631         if(this.split){
38632             this.split.el.setLocation(-2000,-2000);
38633             this.split.el.hide();
38634         }
38635     },
38636
38637     show : function(){
38638         if(this.split){
38639             this.split.el.show();
38640         }
38641         Roo.bootstrap.layout.Split.superclass.show.call(this);
38642     },
38643     
38644     beforeSlide: function(){
38645         if(Roo.isGecko){// firefox overflow auto bug workaround
38646             this.bodyEl.clip();
38647             if(this.tabs) {
38648                 this.tabs.bodyEl.clip();
38649             }
38650             if(this.activePanel){
38651                 this.activePanel.getEl().clip();
38652                 
38653                 if(this.activePanel.beforeSlide){
38654                     this.activePanel.beforeSlide();
38655                 }
38656             }
38657         }
38658     },
38659     
38660     afterSlide : function(){
38661         if(Roo.isGecko){// firefox overflow auto bug workaround
38662             this.bodyEl.unclip();
38663             if(this.tabs) {
38664                 this.tabs.bodyEl.unclip();
38665             }
38666             if(this.activePanel){
38667                 this.activePanel.getEl().unclip();
38668                 if(this.activePanel.afterSlide){
38669                     this.activePanel.afterSlide();
38670                 }
38671             }
38672         }
38673     },
38674
38675     initAutoHide : function(){
38676         if(this.autoHide !== false){
38677             if(!this.autoHideHd){
38678                 var st = new Roo.util.DelayedTask(this.slideIn, this);
38679                 this.autoHideHd = {
38680                     "mouseout": function(e){
38681                         if(!e.within(this.el, true)){
38682                             st.delay(500);
38683                         }
38684                     },
38685                     "mouseover" : function(e){
38686                         st.cancel();
38687                     },
38688                     scope : this
38689                 };
38690             }
38691             this.el.on(this.autoHideHd);
38692         }
38693     },
38694
38695     clearAutoHide : function(){
38696         if(this.autoHide !== false){
38697             this.el.un("mouseout", this.autoHideHd.mouseout);
38698             this.el.un("mouseover", this.autoHideHd.mouseover);
38699         }
38700     },
38701
38702     clearMonitor : function(){
38703         Roo.get(document).un("click", this.slideInIf, this);
38704     },
38705
38706     // these names are backwards but not changed for compat
38707     slideOut : function(){
38708         if(this.isSlid || this.el.hasActiveFx()){
38709             return;
38710         }
38711         this.isSlid = true;
38712         if(this.collapseBtn){
38713             this.collapseBtn.hide();
38714         }
38715         this.closeBtnState = this.closeBtn.getStyle('display');
38716         this.closeBtn.hide();
38717         if(this.stickBtn){
38718             this.stickBtn.show();
38719         }
38720         this.el.show();
38721         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
38722         this.beforeSlide();
38723         this.el.setStyle("z-index", 10001);
38724         this.el.slideIn(this.getSlideAnchor(), {
38725             callback: function(){
38726                 this.afterSlide();
38727                 this.initAutoHide();
38728                 Roo.get(document).on("click", this.slideInIf, this);
38729                 this.fireEvent("slideshow", this);
38730             },
38731             scope: this,
38732             block: true
38733         });
38734     },
38735
38736     afterSlideIn : function(){
38737         this.clearAutoHide();
38738         this.isSlid = false;
38739         this.clearMonitor();
38740         this.el.setStyle("z-index", "");
38741         if(this.collapseBtn){
38742             this.collapseBtn.show();
38743         }
38744         this.closeBtn.setStyle('display', this.closeBtnState);
38745         if(this.stickBtn){
38746             this.stickBtn.hide();
38747         }
38748         this.fireEvent("slidehide", this);
38749     },
38750
38751     slideIn : function(cb){
38752         if(!this.isSlid || this.el.hasActiveFx()){
38753             Roo.callback(cb);
38754             return;
38755         }
38756         this.isSlid = false;
38757         this.beforeSlide();
38758         this.el.slideOut(this.getSlideAnchor(), {
38759             callback: function(){
38760                 this.el.setLeftTop(-10000, -10000);
38761                 this.afterSlide();
38762                 this.afterSlideIn();
38763                 Roo.callback(cb);
38764             },
38765             scope: this,
38766             block: true
38767         });
38768     },
38769     
38770     slideInIf : function(e){
38771         if(!e.within(this.el)){
38772             this.slideIn();
38773         }
38774     },
38775
38776     animateCollapse : function(){
38777         this.beforeSlide();
38778         this.el.setStyle("z-index", 20000);
38779         var anchor = this.getSlideAnchor();
38780         this.el.slideOut(anchor, {
38781             callback : function(){
38782                 this.el.setStyle("z-index", "");
38783                 this.collapsedEl.slideIn(anchor, {duration:.3});
38784                 this.afterSlide();
38785                 this.el.setLocation(-10000,-10000);
38786                 this.el.hide();
38787                 this.fireEvent("collapsed", this);
38788             },
38789             scope: this,
38790             block: true
38791         });
38792     },
38793
38794     animateExpand : function(){
38795         this.beforeSlide();
38796         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
38797         this.el.setStyle("z-index", 20000);
38798         this.collapsedEl.hide({
38799             duration:.1
38800         });
38801         this.el.slideIn(this.getSlideAnchor(), {
38802             callback : function(){
38803                 this.el.setStyle("z-index", "");
38804                 this.afterSlide();
38805                 if(this.split){
38806                     this.split.el.show();
38807                 }
38808                 this.fireEvent("invalidated", this);
38809                 this.fireEvent("expanded", this);
38810             },
38811             scope: this,
38812             block: true
38813         });
38814     },
38815
38816     anchors : {
38817         "west" : "left",
38818         "east" : "right",
38819         "north" : "top",
38820         "south" : "bottom"
38821     },
38822
38823     sanchors : {
38824         "west" : "l",
38825         "east" : "r",
38826         "north" : "t",
38827         "south" : "b"
38828     },
38829
38830     canchors : {
38831         "west" : "tl-tr",
38832         "east" : "tr-tl",
38833         "north" : "tl-bl",
38834         "south" : "bl-tl"
38835     },
38836
38837     getAnchor : function(){
38838         return this.anchors[this.position];
38839     },
38840
38841     getCollapseAnchor : function(){
38842         return this.canchors[this.position];
38843     },
38844
38845     getSlideAnchor : function(){
38846         return this.sanchors[this.position];
38847     },
38848
38849     getAlignAdj : function(){
38850         var cm = this.cmargins;
38851         switch(this.position){
38852             case "west":
38853                 return [0, 0];
38854             break;
38855             case "east":
38856                 return [0, 0];
38857             break;
38858             case "north":
38859                 return [0, 0];
38860             break;
38861             case "south":
38862                 return [0, 0];
38863             break;
38864         }
38865     },
38866
38867     getExpandAdj : function(){
38868         var c = this.collapsedEl, cm = this.cmargins;
38869         switch(this.position){
38870             case "west":
38871                 return [-(cm.right+c.getWidth()+cm.left), 0];
38872             break;
38873             case "east":
38874                 return [cm.right+c.getWidth()+cm.left, 0];
38875             break;
38876             case "north":
38877                 return [0, -(cm.top+cm.bottom+c.getHeight())];
38878             break;
38879             case "south":
38880                 return [0, cm.top+cm.bottom+c.getHeight()];
38881             break;
38882         }
38883     }
38884 });/*
38885  * Based on:
38886  * Ext JS Library 1.1.1
38887  * Copyright(c) 2006-2007, Ext JS, LLC.
38888  *
38889  * Originally Released Under LGPL - original licence link has changed is not relivant.
38890  *
38891  * Fork - LGPL
38892  * <script type="text/javascript">
38893  */
38894 /*
38895  * These classes are private internal classes
38896  */
38897 Roo.bootstrap.layout.Center = function(config){
38898     config.region = "center";
38899     Roo.bootstrap.layout.Region.call(this, config);
38900     this.visible = true;
38901     this.minWidth = config.minWidth || 20;
38902     this.minHeight = config.minHeight || 20;
38903 };
38904
38905 Roo.extend(Roo.bootstrap.layout.Center, Roo.bootstrap.layout.Region, {
38906     hide : function(){
38907         // center panel can't be hidden
38908     },
38909     
38910     show : function(){
38911         // center panel can't be hidden
38912     },
38913     
38914     getMinWidth: function(){
38915         return this.minWidth;
38916     },
38917     
38918     getMinHeight: function(){
38919         return this.minHeight;
38920     }
38921 });
38922
38923
38924
38925
38926  
38927
38928
38929
38930
38931
38932
38933 Roo.bootstrap.layout.North = function(config)
38934 {
38935     config.region = 'north';
38936     config.cursor = 'n-resize';
38937     
38938     Roo.bootstrap.layout.Split.call(this, config);
38939     
38940     
38941     if(this.split){
38942         this.split.placement = Roo.bootstrap.SplitBar.TOP;
38943         this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
38944         this.split.el.addClass("roo-layout-split-v");
38945     }
38946     var size = config.initialSize || config.height;
38947     if(typeof size != "undefined"){
38948         this.el.setHeight(size);
38949     }
38950 };
38951 Roo.extend(Roo.bootstrap.layout.North, Roo.bootstrap.layout.Split,
38952 {
38953     orientation: Roo.bootstrap.SplitBar.VERTICAL,
38954     
38955     
38956     
38957     getBox : function(){
38958         if(this.collapsed){
38959             return this.collapsedEl.getBox();
38960         }
38961         var box = this.el.getBox();
38962         if(this.split){
38963             box.height += this.split.el.getHeight();
38964         }
38965         return box;
38966     },
38967     
38968     updateBox : function(box){
38969         if(this.split && !this.collapsed){
38970             box.height -= this.split.el.getHeight();
38971             this.split.el.setLeft(box.x);
38972             this.split.el.setTop(box.y+box.height);
38973             this.split.el.setWidth(box.width);
38974         }
38975         if(this.collapsed){
38976             this.updateBody(box.width, null);
38977         }
38978         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
38979     }
38980 });
38981
38982
38983
38984
38985
38986 Roo.bootstrap.layout.South = function(config){
38987     config.region = 'south';
38988     config.cursor = 's-resize';
38989     Roo.bootstrap.layout.Split.call(this, config);
38990     if(this.split){
38991         this.split.placement = Roo.bootstrap.SplitBar.BOTTOM;
38992         this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
38993         this.split.el.addClass("roo-layout-split-v");
38994     }
38995     var size = config.initialSize || config.height;
38996     if(typeof size != "undefined"){
38997         this.el.setHeight(size);
38998     }
38999 };
39000
39001 Roo.extend(Roo.bootstrap.layout.South, Roo.bootstrap.layout.Split, {
39002     orientation: Roo.bootstrap.SplitBar.VERTICAL,
39003     getBox : function(){
39004         if(this.collapsed){
39005             return this.collapsedEl.getBox();
39006         }
39007         var box = this.el.getBox();
39008         if(this.split){
39009             var sh = this.split.el.getHeight();
39010             box.height += sh;
39011             box.y -= sh;
39012         }
39013         return box;
39014     },
39015     
39016     updateBox : function(box){
39017         if(this.split && !this.collapsed){
39018             var sh = this.split.el.getHeight();
39019             box.height -= sh;
39020             box.y += sh;
39021             this.split.el.setLeft(box.x);
39022             this.split.el.setTop(box.y-sh);
39023             this.split.el.setWidth(box.width);
39024         }
39025         if(this.collapsed){
39026             this.updateBody(box.width, null);
39027         }
39028         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
39029     }
39030 });
39031
39032 Roo.bootstrap.layout.East = function(config){
39033     config.region = "east";
39034     config.cursor = "e-resize";
39035     Roo.bootstrap.layout.Split.call(this, config);
39036     if(this.split){
39037         this.split.placement = Roo.bootstrap.SplitBar.RIGHT;
39038         this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
39039         this.split.el.addClass("roo-layout-split-h");
39040     }
39041     var size = config.initialSize || config.width;
39042     if(typeof size != "undefined"){
39043         this.el.setWidth(size);
39044     }
39045 };
39046 Roo.extend(Roo.bootstrap.layout.East, Roo.bootstrap.layout.Split, {
39047     orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
39048     getBox : function(){
39049         if(this.collapsed){
39050             return this.collapsedEl.getBox();
39051         }
39052         var box = this.el.getBox();
39053         if(this.split){
39054             var sw = this.split.el.getWidth();
39055             box.width += sw;
39056             box.x -= sw;
39057         }
39058         return box;
39059     },
39060
39061     updateBox : function(box){
39062         if(this.split && !this.collapsed){
39063             var sw = this.split.el.getWidth();
39064             box.width -= sw;
39065             this.split.el.setLeft(box.x);
39066             this.split.el.setTop(box.y);
39067             this.split.el.setHeight(box.height);
39068             box.x += sw;
39069         }
39070         if(this.collapsed){
39071             this.updateBody(null, box.height);
39072         }
39073         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
39074     }
39075 });
39076
39077 Roo.bootstrap.layout.West = function(config){
39078     config.region = "west";
39079     config.cursor = "w-resize";
39080     
39081     Roo.bootstrap.layout.Split.call(this, config);
39082     if(this.split){
39083         this.split.placement = Roo.bootstrap.SplitBar.LEFT;
39084         this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
39085         this.split.el.addClass("roo-layout-split-h");
39086     }
39087     
39088 };
39089 Roo.extend(Roo.bootstrap.layout.West, Roo.bootstrap.layout.Split, {
39090     orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
39091     
39092     onRender: function(ctr, pos)
39093     {
39094         Roo.bootstrap.layout.West.superclass.onRender.call(this, ctr,pos);
39095         var size = this.config.initialSize || this.config.width;
39096         if(typeof size != "undefined"){
39097             this.el.setWidth(size);
39098         }
39099     },
39100     
39101     getBox : function(){
39102         if(this.collapsed){
39103             return this.collapsedEl.getBox();
39104         }
39105         var box = this.el.getBox();
39106         if(this.split){
39107             box.width += this.split.el.getWidth();
39108         }
39109         return box;
39110     },
39111     
39112     updateBox : function(box){
39113         if(this.split && !this.collapsed){
39114             var sw = this.split.el.getWidth();
39115             box.width -= sw;
39116             this.split.el.setLeft(box.x+box.width);
39117             this.split.el.setTop(box.y);
39118             this.split.el.setHeight(box.height);
39119         }
39120         if(this.collapsed){
39121             this.updateBody(null, box.height);
39122         }
39123         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
39124     }
39125 });Roo.namespace("Roo.bootstrap.panel");/*
39126  * Based on:
39127  * Ext JS Library 1.1.1
39128  * Copyright(c) 2006-2007, Ext JS, LLC.
39129  *
39130  * Originally Released Under LGPL - original licence link has changed is not relivant.
39131  *
39132  * Fork - LGPL
39133  * <script type="text/javascript">
39134  */
39135 /**
39136  * @class Roo.ContentPanel
39137  * @extends Roo.util.Observable
39138  * A basic ContentPanel element.
39139  * @cfg {Boolean}   fitToFrame    True for this panel to adjust its size to fit when the region resizes  (defaults to false)
39140  * @cfg {Boolean}   fitContainer   When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container  (defaults to false)
39141  * @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
39142  * @cfg {Boolean}   closable      True if the panel can be closed/removed
39143  * @cfg {Boolean}   background    True if the panel should not be activated when it is added (defaults to false)
39144  * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
39145  * @cfg {Toolbar}   toolbar       A toolbar for this panel
39146  * @cfg {Boolean} autoScroll    True to scroll overflow in this panel (use with {@link #fitToFrame})
39147  * @cfg {String} title          The title for this panel
39148  * @cfg {Array} adjustments     Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
39149  * @cfg {String} url            Calls {@link #setUrl} with this value
39150  * @cfg {String} region         (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
39151  * @cfg {String/Object} params  When used with {@link #url}, calls {@link #setUrl} with this value
39152  * @cfg {Boolean} loadOnce      When used with {@link #url}, calls {@link #setUrl} with this value
39153  * @cfg {String}    content        Raw content to fill content panel with (uses setContent on construction.)
39154  * @cfg {Boolean} badges render the badges
39155
39156  * @constructor
39157  * Create a new ContentPanel.
39158  * @param {String/HTMLElement/Roo.Element} el The container element for this panel
39159  * @param {String/Object} config A string to set only the title or a config object
39160  * @param {String} content (optional) Set the HTML content for this panel
39161  * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
39162  */
39163 Roo.bootstrap.panel.Content = function( config){
39164     
39165     this.tpl = config.tpl || false;
39166     
39167     var el = config.el;
39168     var content = config.content;
39169
39170     if(config.autoCreate){ // xtype is available if this is called from factory
39171         el = Roo.id();
39172     }
39173     this.el = Roo.get(el);
39174     if(!this.el && config && config.autoCreate){
39175         if(typeof config.autoCreate == "object"){
39176             if(!config.autoCreate.id){
39177                 config.autoCreate.id = config.id||el;
39178             }
39179             this.el = Roo.DomHelper.append(document.body,
39180                         config.autoCreate, true);
39181         }else{
39182             var elcfg =  {   tag: "div",
39183                             cls: "roo-layout-inactive-content",
39184                             id: config.id||el
39185                             };
39186             if (config.html) {
39187                 elcfg.html = config.html;
39188                 
39189             }
39190                         
39191             this.el = Roo.DomHelper.append(document.body, elcfg , true);
39192         }
39193     } 
39194     this.closable = false;
39195     this.loaded = false;
39196     this.active = false;
39197    
39198       
39199     if (config.toolbar && !config.toolbar.el && config.toolbar.xtype) {
39200         
39201         this.toolbar = new config.toolbar.xns[config.toolbar.xtype](config.toolbar);
39202         
39203         this.wrapEl = this.el; //this.el.wrap();
39204         var ti = [];
39205         if (config.toolbar.items) {
39206             ti = config.toolbar.items ;
39207             delete config.toolbar.items ;
39208         }
39209         
39210         var nitems = [];
39211         this.toolbar.render(this.wrapEl, 'before');
39212         for(var i =0;i < ti.length;i++) {
39213           //  Roo.log(['add child', items[i]]);
39214             nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
39215         }
39216         this.toolbar.items = nitems;
39217         this.toolbar.el.insertBefore(this.wrapEl.dom.firstChild);
39218         delete config.toolbar;
39219         
39220     }
39221     /*
39222     // xtype created footer. - not sure if will work as we normally have to render first..
39223     if (this.footer && !this.footer.el && this.footer.xtype) {
39224         if (!this.wrapEl) {
39225             this.wrapEl = this.el.wrap();
39226         }
39227     
39228         this.footer.container = this.wrapEl.createChild();
39229          
39230         this.footer = Roo.factory(this.footer, Roo);
39231         
39232     }
39233     */
39234     
39235      if(typeof config == "string"){
39236         this.title = config;
39237     }else{
39238         Roo.apply(this, config);
39239     }
39240     
39241     if(this.resizeEl){
39242         this.resizeEl = Roo.get(this.resizeEl, true);
39243     }else{
39244         this.resizeEl = this.el;
39245     }
39246     // handle view.xtype
39247     
39248  
39249     
39250     
39251     this.addEvents({
39252         /**
39253          * @event activate
39254          * Fires when this panel is activated. 
39255          * @param {Roo.ContentPanel} this
39256          */
39257         "activate" : true,
39258         /**
39259          * @event deactivate
39260          * Fires when this panel is activated. 
39261          * @param {Roo.ContentPanel} this
39262          */
39263         "deactivate" : true,
39264
39265         /**
39266          * @event resize
39267          * Fires when this panel is resized if fitToFrame is true.
39268          * @param {Roo.ContentPanel} this
39269          * @param {Number} width The width after any component adjustments
39270          * @param {Number} height The height after any component adjustments
39271          */
39272         "resize" : true,
39273         
39274          /**
39275          * @event render
39276          * Fires when this tab is created
39277          * @param {Roo.ContentPanel} this
39278          */
39279         "render" : true
39280         
39281         
39282         
39283     });
39284     
39285
39286     
39287     
39288     if(this.autoScroll){
39289         this.resizeEl.setStyle("overflow", "auto");
39290     } else {
39291         // fix randome scrolling
39292         //this.el.on('scroll', function() {
39293         //    Roo.log('fix random scolling');
39294         //    this.scrollTo('top',0); 
39295         //});
39296     }
39297     content = content || this.content;
39298     if(content){
39299         this.setContent(content);
39300     }
39301     if(config && config.url){
39302         this.setUrl(this.url, this.params, this.loadOnce);
39303     }
39304     
39305     
39306     
39307     Roo.bootstrap.panel.Content.superclass.constructor.call(this);
39308     
39309     if (this.view && typeof(this.view.xtype) != 'undefined') {
39310         this.view.el = this.el.appendChild(document.createElement("div"));
39311         this.view = Roo.factory(this.view); 
39312         this.view.render  &&  this.view.render(false, '');  
39313     }
39314     
39315     
39316     this.fireEvent('render', this);
39317 };
39318
39319 Roo.extend(Roo.bootstrap.panel.Content, Roo.bootstrap.Component, {
39320     
39321     tabTip : '',
39322     
39323     setRegion : function(region){
39324         this.region = region;
39325         this.setActiveClass(region && !this.background);
39326     },
39327     
39328     
39329     setActiveClass: function(state)
39330     {
39331         if(state){
39332            this.el.replaceClass("roo-layout-inactive-content", "roo-layout-active-content");
39333            this.el.setStyle('position','relative');
39334         }else{
39335            this.el.replaceClass("roo-layout-active-content", "roo-layout-inactive-content");
39336            this.el.setStyle('position', 'absolute');
39337         } 
39338     },
39339     
39340     /**
39341      * Returns the toolbar for this Panel if one was configured. 
39342      * @return {Roo.Toolbar} 
39343      */
39344     getToolbar : function(){
39345         return this.toolbar;
39346     },
39347     
39348     setActiveState : function(active)
39349     {
39350         this.active = active;
39351         this.setActiveClass(active);
39352         if(!active){
39353             if(this.fireEvent("deactivate", this) === false){
39354                 return false;
39355             }
39356             return true;
39357         }
39358         this.fireEvent("activate", this);
39359         return true;
39360     },
39361     /**
39362      * Updates this panel's element
39363      * @param {String} content The new content
39364      * @param {Boolean} loadScripts (optional) true to look for and process scripts
39365     */
39366     setContent : function(content, loadScripts){
39367         this.el.update(content, loadScripts);
39368     },
39369
39370     ignoreResize : function(w, h){
39371         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
39372             return true;
39373         }else{
39374             this.lastSize = {width: w, height: h};
39375             return false;
39376         }
39377     },
39378     /**
39379      * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
39380      * @return {Roo.UpdateManager} The UpdateManager
39381      */
39382     getUpdateManager : function(){
39383         return this.el.getUpdateManager();
39384     },
39385      /**
39386      * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
39387      * @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:
39388 <pre><code>
39389 panel.load({
39390     url: "your-url.php",
39391     params: {param1: "foo", param2: "bar"}, // or a URL encoded string
39392     callback: yourFunction,
39393     scope: yourObject, //(optional scope)
39394     discardUrl: false,
39395     nocache: false,
39396     text: "Loading...",
39397     timeout: 30,
39398     scripts: false
39399 });
39400 </code></pre>
39401      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
39402      * 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.
39403      * @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}
39404      * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
39405      * @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.
39406      * @return {Roo.ContentPanel} this
39407      */
39408     load : function(){
39409         var um = this.el.getUpdateManager();
39410         um.update.apply(um, arguments);
39411         return this;
39412     },
39413
39414
39415     /**
39416      * 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.
39417      * @param {String/Function} url The URL to load the content from or a function to call to get the URL
39418      * @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)
39419      * @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)
39420      * @return {Roo.UpdateManager} The UpdateManager
39421      */
39422     setUrl : function(url, params, loadOnce){
39423         if(this.refreshDelegate){
39424             this.removeListener("activate", this.refreshDelegate);
39425         }
39426         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
39427         this.on("activate", this.refreshDelegate);
39428         return this.el.getUpdateManager();
39429     },
39430     
39431     _handleRefresh : function(url, params, loadOnce){
39432         if(!loadOnce || !this.loaded){
39433             var updater = this.el.getUpdateManager();
39434             updater.update(url, params, this._setLoaded.createDelegate(this));
39435         }
39436     },
39437     
39438     _setLoaded : function(){
39439         this.loaded = true;
39440     }, 
39441     
39442     /**
39443      * Returns this panel's id
39444      * @return {String} 
39445      */
39446     getId : function(){
39447         return this.el.id;
39448     },
39449     
39450     /** 
39451      * Returns this panel's element - used by regiosn to add.
39452      * @return {Roo.Element} 
39453      */
39454     getEl : function(){
39455         return this.wrapEl || this.el;
39456     },
39457     
39458    
39459     
39460     adjustForComponents : function(width, height)
39461     {
39462         //Roo.log('adjustForComponents ');
39463         if(this.resizeEl != this.el){
39464             width -= this.el.getFrameWidth('lr');
39465             height -= this.el.getFrameWidth('tb');
39466         }
39467         if(this.toolbar){
39468             var te = this.toolbar.getEl();
39469             te.setWidth(width);
39470             height -= te.getHeight();
39471         }
39472         if(this.footer){
39473             var te = this.footer.getEl();
39474             te.setWidth(width);
39475             height -= te.getHeight();
39476         }
39477         
39478         
39479         if(this.adjustments){
39480             width += this.adjustments[0];
39481             height += this.adjustments[1];
39482         }
39483         return {"width": width, "height": height};
39484     },
39485     
39486     setSize : function(width, height){
39487         if(this.fitToFrame && !this.ignoreResize(width, height)){
39488             if(this.fitContainer && this.resizeEl != this.el){
39489                 this.el.setSize(width, height);
39490             }
39491             var size = this.adjustForComponents(width, height);
39492             this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
39493             this.fireEvent('resize', this, size.width, size.height);
39494         }
39495     },
39496     
39497     /**
39498      * Returns this panel's title
39499      * @return {String} 
39500      */
39501     getTitle : function(){
39502         
39503         if (typeof(this.title) != 'object') {
39504             return this.title;
39505         }
39506         
39507         var t = '';
39508         for (var k in this.title) {
39509             if (!this.title.hasOwnProperty(k)) {
39510                 continue;
39511             }
39512             
39513             if (k.indexOf('-') >= 0) {
39514                 var s = k.split('-');
39515                 for (var i = 0; i<s.length; i++) {
39516                     t += "<span class='visible-"+s[i]+"'>"+this.title[k]+"</span>";
39517                 }
39518             } else {
39519                 t += "<span class='visible-"+k+"'>"+this.title[k]+"</span>";
39520             }
39521         }
39522         return t;
39523     },
39524     
39525     /**
39526      * Set this panel's title
39527      * @param {String} title
39528      */
39529     setTitle : function(title){
39530         this.title = title;
39531         if(this.region){
39532             this.region.updatePanelTitle(this, title);
39533         }
39534     },
39535     
39536     /**
39537      * Returns true is this panel was configured to be closable
39538      * @return {Boolean} 
39539      */
39540     isClosable : function(){
39541         return this.closable;
39542     },
39543     
39544     beforeSlide : function(){
39545         this.el.clip();
39546         this.resizeEl.clip();
39547     },
39548     
39549     afterSlide : function(){
39550         this.el.unclip();
39551         this.resizeEl.unclip();
39552     },
39553     
39554     /**
39555      *   Force a content refresh from the URL specified in the {@link #setUrl} method.
39556      *   Will fail silently if the {@link #setUrl} method has not been called.
39557      *   This does not activate the panel, just updates its content.
39558      */
39559     refresh : function(){
39560         if(this.refreshDelegate){
39561            this.loaded = false;
39562            this.refreshDelegate();
39563         }
39564     },
39565     
39566     /**
39567      * Destroys this panel
39568      */
39569     destroy : function(){
39570         this.el.removeAllListeners();
39571         var tempEl = document.createElement("span");
39572         tempEl.appendChild(this.el.dom);
39573         tempEl.innerHTML = "";
39574         this.el.remove();
39575         this.el = null;
39576     },
39577     
39578     /**
39579      * form - if the content panel contains a form - this is a reference to it.
39580      * @type {Roo.form.Form}
39581      */
39582     form : false,
39583     /**
39584      * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
39585      *    This contains a reference to it.
39586      * @type {Roo.View}
39587      */
39588     view : false,
39589     
39590       /**
39591      * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
39592      * <pre><code>
39593
39594 layout.addxtype({
39595        xtype : 'Form',
39596        items: [ .... ]
39597    }
39598 );
39599
39600 </code></pre>
39601      * @param {Object} cfg Xtype definition of item to add.
39602      */
39603     
39604     
39605     getChildContainer: function () {
39606         return this.getEl();
39607     }
39608     
39609     
39610     /*
39611         var  ret = new Roo.factory(cfg);
39612         return ret;
39613         
39614         
39615         // add form..
39616         if (cfg.xtype.match(/^Form$/)) {
39617             
39618             var el;
39619             //if (this.footer) {
39620             //    el = this.footer.container.insertSibling(false, 'before');
39621             //} else {
39622                 el = this.el.createChild();
39623             //}
39624
39625             this.form = new  Roo.form.Form(cfg);
39626             
39627             
39628             if ( this.form.allItems.length) {
39629                 this.form.render(el.dom);
39630             }
39631             return this.form;
39632         }
39633         // should only have one of theses..
39634         if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
39635             // views.. should not be just added - used named prop 'view''
39636             
39637             cfg.el = this.el.appendChild(document.createElement("div"));
39638             // factory?
39639             
39640             var ret = new Roo.factory(cfg);
39641              
39642              ret.render && ret.render(false, ''); // render blank..
39643             this.view = ret;
39644             return ret;
39645         }
39646         return false;
39647     }
39648     \*/
39649 });
39650  
39651 /**
39652  * @class Roo.bootstrap.panel.Grid
39653  * @extends Roo.bootstrap.panel.Content
39654  * @constructor
39655  * Create a new GridPanel.
39656  * @cfg {Roo.bootstrap.Table} grid The grid for this panel
39657  * @param {Object} config A the config object
39658   
39659  */
39660
39661
39662
39663 Roo.bootstrap.panel.Grid = function(config)
39664 {
39665     
39666       
39667     this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
39668         {tag: "div", cls: "roo-layout-grid-wrapper roo-layout-inactive-content"}, true);
39669
39670     config.el = this.wrapper;
39671     //this.el = this.wrapper;
39672     
39673       if (config.container) {
39674         // ctor'ed from a Border/panel.grid
39675         
39676         
39677         this.wrapper.setStyle("overflow", "hidden");
39678         this.wrapper.addClass('roo-grid-container');
39679
39680     }
39681     
39682     
39683     if(config.toolbar){
39684         var tool_el = this.wrapper.createChild();    
39685         this.toolbar = Roo.factory(config.toolbar);
39686         var ti = [];
39687         if (config.toolbar.items) {
39688             ti = config.toolbar.items ;
39689             delete config.toolbar.items ;
39690         }
39691         
39692         var nitems = [];
39693         this.toolbar.render(tool_el);
39694         for(var i =0;i < ti.length;i++) {
39695           //  Roo.log(['add child', items[i]]);
39696             nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
39697         }
39698         this.toolbar.items = nitems;
39699         
39700         delete config.toolbar;
39701     }
39702     
39703     Roo.bootstrap.panel.Grid.superclass.constructor.call(this, config);
39704     config.grid.scrollBody = true;;
39705     config.grid.monitorWindowResize = false; // turn off autosizing
39706     config.grid.autoHeight = false;
39707     config.grid.autoWidth = false;
39708     
39709     this.grid = new config.grid.xns[config.grid.xtype](config.grid);
39710     
39711     if (config.background) {
39712         // render grid on panel activation (if panel background)
39713         this.on('activate', function(gp) {
39714             if (!gp.grid.rendered) {
39715                 gp.grid.render(this.wrapper);
39716                 gp.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");   
39717             }
39718         });
39719             
39720     } else {
39721         this.grid.render(this.wrapper);
39722         this.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");               
39723
39724     }
39725     //this.wrapper.dom.appendChild(config.grid.getGridEl().dom);
39726     // ??? needed ??? config.el = this.wrapper;
39727     
39728     
39729     
39730   
39731     // xtype created footer. - not sure if will work as we normally have to render first..
39732     if (this.footer && !this.footer.el && this.footer.xtype) {
39733         
39734         var ctr = this.grid.getView().getFooterPanel(true);
39735         this.footer.dataSource = this.grid.dataSource;
39736         this.footer = Roo.factory(this.footer, Roo);
39737         this.footer.render(ctr);
39738         
39739     }
39740     
39741     
39742     
39743     
39744      
39745 };
39746
39747 Roo.extend(Roo.bootstrap.panel.Grid, Roo.bootstrap.panel.Content, {
39748     getId : function(){
39749         return this.grid.id;
39750     },
39751     
39752     /**
39753      * Returns the grid for this panel
39754      * @return {Roo.bootstrap.Table} 
39755      */
39756     getGrid : function(){
39757         return this.grid;    
39758     },
39759     
39760     setSize : function(width, height){
39761         if(!this.ignoreResize(width, height)){
39762             var grid = this.grid;
39763             var size = this.adjustForComponents(width, height);
39764             // tfoot is not a footer?
39765           
39766             
39767             var gridel = grid.getGridEl();
39768             gridel.setSize(size.width, size.height);
39769             
39770             var tbd = grid.getGridEl().select('tbody', true).first();
39771             var thd = grid.getGridEl().select('thead',true).first();
39772             var tbf= grid.getGridEl().select('tfoot', true).first();
39773
39774             if (tbf) {
39775                 size.height -= thd.getHeight();
39776             }
39777             if (thd) {
39778                 size.height -= thd.getHeight();
39779             }
39780             
39781             tbd.setSize(size.width, size.height );
39782             // this is for the account management tab -seems to work there.
39783             var thd = grid.getGridEl().select('thead',true).first();
39784             //if (tbd) {
39785             //    tbd.setSize(size.width, size.height - thd.getHeight());
39786             //}
39787              
39788             grid.autoSize();
39789         }
39790     },
39791      
39792     
39793     
39794     beforeSlide : function(){
39795         this.grid.getView().scroller.clip();
39796     },
39797     
39798     afterSlide : function(){
39799         this.grid.getView().scroller.unclip();
39800     },
39801     
39802     destroy : function(){
39803         this.grid.destroy();
39804         delete this.grid;
39805         Roo.bootstrap.panel.Grid.superclass.destroy.call(this); 
39806     }
39807 });
39808
39809 /**
39810  * @class Roo.bootstrap.panel.Nest
39811  * @extends Roo.bootstrap.panel.Content
39812  * @constructor
39813  * Create a new Panel, that can contain a layout.Border.
39814  * 
39815  * 
39816  * @param {Roo.BorderLayout} layout The layout for this panel
39817  * @param {String/Object} config A string to set only the title or a config object
39818  */
39819 Roo.bootstrap.panel.Nest = function(config)
39820 {
39821     // construct with only one argument..
39822     /* FIXME - implement nicer consturctors
39823     if (layout.layout) {
39824         config = layout;
39825         layout = config.layout;
39826         delete config.layout;
39827     }
39828     if (layout.xtype && !layout.getEl) {
39829         // then layout needs constructing..
39830         layout = Roo.factory(layout, Roo);
39831     }
39832     */
39833     
39834     config.el =  config.layout.getEl();
39835     
39836     Roo.bootstrap.panel.Nest.superclass.constructor.call(this, config);
39837     
39838     config.layout.monitorWindowResize = false; // turn off autosizing
39839     this.layout = config.layout;
39840     this.layout.getEl().addClass("roo-layout-nested-layout");
39841     this.layout.parent = this;
39842     
39843     
39844     
39845     
39846 };
39847
39848 Roo.extend(Roo.bootstrap.panel.Nest, Roo.bootstrap.panel.Content, {
39849
39850     setSize : function(width, height){
39851         if(!this.ignoreResize(width, height)){
39852             var size = this.adjustForComponents(width, height);
39853             var el = this.layout.getEl();
39854             if (size.height < 1) {
39855                 el.setWidth(size.width);   
39856             } else {
39857                 el.setSize(size.width, size.height);
39858             }
39859             var touch = el.dom.offsetWidth;
39860             this.layout.layout();
39861             // ie requires a double layout on the first pass
39862             if(Roo.isIE && !this.initialized){
39863                 this.initialized = true;
39864                 this.layout.layout();
39865             }
39866         }
39867     },
39868     
39869     // activate all subpanels if not currently active..
39870     
39871     setActiveState : function(active){
39872         this.active = active;
39873         this.setActiveClass(active);
39874         
39875         if(!active){
39876             this.fireEvent("deactivate", this);
39877             return;
39878         }
39879         
39880         this.fireEvent("activate", this);
39881         // not sure if this should happen before or after..
39882         if (!this.layout) {
39883             return; // should not happen..
39884         }
39885         var reg = false;
39886         for (var r in this.layout.regions) {
39887             reg = this.layout.getRegion(r);
39888             if (reg.getActivePanel()) {
39889                 //reg.showPanel(reg.getActivePanel()); // force it to activate.. 
39890                 reg.setActivePanel(reg.getActivePanel());
39891                 continue;
39892             }
39893             if (!reg.panels.length) {
39894                 continue;
39895             }
39896             reg.showPanel(reg.getPanel(0));
39897         }
39898         
39899         
39900         
39901         
39902     },
39903     
39904     /**
39905      * Returns the nested BorderLayout for this panel
39906      * @return {Roo.BorderLayout} 
39907      */
39908     getLayout : function(){
39909         return this.layout;
39910     },
39911     
39912      /**
39913      * Adds a xtype elements to the layout of the nested panel
39914      * <pre><code>
39915
39916 panel.addxtype({
39917        xtype : 'ContentPanel',
39918        region: 'west',
39919        items: [ .... ]
39920    }
39921 );
39922
39923 panel.addxtype({
39924         xtype : 'NestedLayoutPanel',
39925         region: 'west',
39926         layout: {
39927            center: { },
39928            west: { }   
39929         },
39930         items : [ ... list of content panels or nested layout panels.. ]
39931    }
39932 );
39933 </code></pre>
39934      * @param {Object} cfg Xtype definition of item to add.
39935      */
39936     addxtype : function(cfg) {
39937         return this.layout.addxtype(cfg);
39938     
39939     }
39940 });/*
39941  * Based on:
39942  * Ext JS Library 1.1.1
39943  * Copyright(c) 2006-2007, Ext JS, LLC.
39944  *
39945  * Originally Released Under LGPL - original licence link has changed is not relivant.
39946  *
39947  * Fork - LGPL
39948  * <script type="text/javascript">
39949  */
39950 /**
39951  * @class Roo.TabPanel
39952  * @extends Roo.util.Observable
39953  * A lightweight tab container.
39954  * <br><br>
39955  * Usage:
39956  * <pre><code>
39957 // basic tabs 1, built from existing content
39958 var tabs = new Roo.TabPanel("tabs1");
39959 tabs.addTab("script", "View Script");
39960 tabs.addTab("markup", "View Markup");
39961 tabs.activate("script");
39962
39963 // more advanced tabs, built from javascript
39964 var jtabs = new Roo.TabPanel("jtabs");
39965 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
39966
39967 // set up the UpdateManager
39968 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
39969 var updater = tab2.getUpdateManager();
39970 updater.setDefaultUrl("ajax1.htm");
39971 tab2.on('activate', updater.refresh, updater, true);
39972
39973 // Use setUrl for Ajax loading
39974 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
39975 tab3.setUrl("ajax2.htm", null, true);
39976
39977 // Disabled tab
39978 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
39979 tab4.disable();
39980
39981 jtabs.activate("jtabs-1");
39982  * </code></pre>
39983  * @constructor
39984  * Create a new TabPanel.
39985  * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
39986  * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
39987  */
39988 Roo.bootstrap.panel.Tabs = function(config){
39989     /**
39990     * The container element for this TabPanel.
39991     * @type Roo.Element
39992     */
39993     this.el = Roo.get(config.el);
39994     delete config.el;
39995     if(config){
39996         if(typeof config == "boolean"){
39997             this.tabPosition = config ? "bottom" : "top";
39998         }else{
39999             Roo.apply(this, config);
40000         }
40001     }
40002     
40003     if(this.tabPosition == "bottom"){
40004         // if tabs are at the bottom = create the body first.
40005         this.bodyEl = Roo.get(this.createBody(this.el.dom));
40006         this.el.addClass("roo-tabs-bottom");
40007     }
40008     // next create the tabs holders
40009     
40010     if (this.tabPosition == "west"){
40011         
40012         var reg = this.region; // fake it..
40013         while (reg) {
40014             if (!reg.mgr.parent) {
40015                 break;
40016             }
40017             reg = reg.mgr.parent.region;
40018         }
40019         Roo.log("got nest?");
40020         Roo.log(reg);
40021         if (reg.mgr.getRegion('west')) {
40022             var ctrdom = reg.mgr.getRegion('west').bodyEl.dom;
40023             this.stripWrap = Roo.get(this.createStrip(ctrdom ), true);
40024             this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
40025             this.stripEl.setVisibilityMode(Roo.Element.DISPLAY);
40026             this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
40027         
40028             
40029         }
40030         
40031         
40032     } else {
40033      
40034         this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
40035         this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
40036         this.stripEl.setVisibilityMode(Roo.Element.DISPLAY);
40037         this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
40038     }
40039     
40040     
40041     if(Roo.isIE){
40042         Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
40043     }
40044     
40045     // finally - if tabs are at the top, then create the body last..
40046     if(this.tabPosition != "bottom"){
40047         /** The body element that contains {@link Roo.TabPanelItem} bodies. +
40048          * @type Roo.Element
40049          */
40050         this.bodyEl = Roo.get(this.createBody(this.el.dom));
40051         this.el.addClass("roo-tabs-top");
40052     }
40053     this.items = [];
40054
40055     this.bodyEl.setStyle("position", "relative");
40056
40057     this.active = null;
40058     this.activateDelegate = this.activate.createDelegate(this);
40059
40060     this.addEvents({
40061         /**
40062          * @event tabchange
40063          * Fires when the active tab changes
40064          * @param {Roo.TabPanel} this
40065          * @param {Roo.TabPanelItem} activePanel The new active tab
40066          */
40067         "tabchange": true,
40068         /**
40069          * @event beforetabchange
40070          * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
40071          * @param {Roo.TabPanel} this
40072          * @param {Object} e Set cancel to true on this object to cancel the tab change
40073          * @param {Roo.TabPanelItem} tab The tab being changed to
40074          */
40075         "beforetabchange" : true
40076     });
40077
40078     Roo.EventManager.onWindowResize(this.onResize, this);
40079     this.cpad = this.el.getPadding("lr");
40080     this.hiddenCount = 0;
40081
40082
40083     // toolbar on the tabbar support...
40084     if (this.toolbar) {
40085         alert("no toolbar support yet");
40086         this.toolbar  = false;
40087         /*
40088         var tcfg = this.toolbar;
40089         tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');  
40090         this.toolbar = new Roo.Toolbar(tcfg);
40091         if (Roo.isSafari) {
40092             var tbl = tcfg.container.child('table', true);
40093             tbl.setAttribute('width', '100%');
40094         }
40095         */
40096         
40097     }
40098    
40099
40100
40101     Roo.bootstrap.panel.Tabs.superclass.constructor.call(this);
40102 };
40103
40104 Roo.extend(Roo.bootstrap.panel.Tabs, Roo.util.Observable, {
40105     /*
40106      *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
40107      */
40108     tabPosition : "top",
40109     /*
40110      *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
40111      */
40112     currentTabWidth : 0,
40113     /*
40114      *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
40115      */
40116     minTabWidth : 40,
40117     /*
40118      *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
40119      */
40120     maxTabWidth : 250,
40121     /*
40122      *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
40123      */
40124     preferredTabWidth : 175,
40125     /*
40126      *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
40127      */
40128     resizeTabs : false,
40129     /*
40130      *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
40131      */
40132     monitorResize : true,
40133     /*
40134      *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar. 
40135      */
40136     toolbar : false,  // set by caller..
40137     
40138     region : false, /// set by caller
40139     
40140     disableTooltips : true, // not used yet...
40141
40142     /**
40143      * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
40144      * @param {String} id The id of the div to use <b>or create</b>
40145      * @param {String} text The text for the tab
40146      * @param {String} content (optional) Content to put in the TabPanelItem body
40147      * @param {Boolean} closable (optional) True to create a close icon on the tab
40148      * @return {Roo.TabPanelItem} The created TabPanelItem
40149      */
40150     addTab : function(id, text, content, closable, tpl)
40151     {
40152         var item = new Roo.bootstrap.panel.TabItem({
40153             panel: this,
40154             id : id,
40155             text : text,
40156             closable : closable,
40157             tpl : tpl
40158         });
40159         this.addTabItem(item);
40160         if(content){
40161             item.setContent(content);
40162         }
40163         return item;
40164     },
40165
40166     /**
40167      * Returns the {@link Roo.TabPanelItem} with the specified id/index
40168      * @param {String/Number} id The id or index of the TabPanelItem to fetch.
40169      * @return {Roo.TabPanelItem}
40170      */
40171     getTab : function(id){
40172         return this.items[id];
40173     },
40174
40175     /**
40176      * Hides the {@link Roo.TabPanelItem} with the specified id/index
40177      * @param {String/Number} id The id or index of the TabPanelItem to hide.
40178      */
40179     hideTab : function(id){
40180         var t = this.items[id];
40181         if(!t.isHidden()){
40182            t.setHidden(true);
40183            this.hiddenCount++;
40184            this.autoSizeTabs();
40185         }
40186     },
40187
40188     /**
40189      * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
40190      * @param {String/Number} id The id or index of the TabPanelItem to unhide.
40191      */
40192     unhideTab : function(id){
40193         var t = this.items[id];
40194         if(t.isHidden()){
40195            t.setHidden(false);
40196            this.hiddenCount--;
40197            this.autoSizeTabs();
40198         }
40199     },
40200
40201     /**
40202      * Adds an existing {@link Roo.TabPanelItem}.
40203      * @param {Roo.TabPanelItem} item The TabPanelItem to add
40204      */
40205     addTabItem : function(item)
40206     {
40207         this.items[item.id] = item;
40208         this.items.push(item);
40209         this.autoSizeTabs();
40210       //  if(this.resizeTabs){
40211     //       item.setWidth(this.currentTabWidth || this.preferredTabWidth);
40212   //         this.autoSizeTabs();
40213 //        }else{
40214 //            item.autoSize();
40215        // }
40216     },
40217
40218     /**
40219      * Removes a {@link Roo.TabPanelItem}.
40220      * @param {String/Number} id The id or index of the TabPanelItem to remove.
40221      */
40222     removeTab : function(id){
40223         var items = this.items;
40224         var tab = items[id];
40225         if(!tab) { return; }
40226         var index = items.indexOf(tab);
40227         if(this.active == tab && items.length > 1){
40228             var newTab = this.getNextAvailable(index);
40229             if(newTab) {
40230                 newTab.activate();
40231             }
40232         }
40233         this.stripEl.dom.removeChild(tab.pnode.dom);
40234         if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
40235             this.bodyEl.dom.removeChild(tab.bodyEl.dom);
40236         }
40237         items.splice(index, 1);
40238         delete this.items[tab.id];
40239         tab.fireEvent("close", tab);
40240         tab.purgeListeners();
40241         this.autoSizeTabs();
40242     },
40243
40244     getNextAvailable : function(start){
40245         var items = this.items;
40246         var index = start;
40247         // look for a next tab that will slide over to
40248         // replace the one being removed
40249         while(index < items.length){
40250             var item = items[++index];
40251             if(item && !item.isHidden()){
40252                 return item;
40253             }
40254         }
40255         // if one isn't found select the previous tab (on the left)
40256         index = start;
40257         while(index >= 0){
40258             var item = items[--index];
40259             if(item && !item.isHidden()){
40260                 return item;
40261             }
40262         }
40263         return null;
40264     },
40265
40266     /**
40267      * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
40268      * @param {String/Number} id The id or index of the TabPanelItem to disable.
40269      */
40270     disableTab : function(id){
40271         var tab = this.items[id];
40272         if(tab && this.active != tab){
40273             tab.disable();
40274         }
40275     },
40276
40277     /**
40278      * Enables a {@link Roo.TabPanelItem} that is disabled.
40279      * @param {String/Number} id The id or index of the TabPanelItem to enable.
40280      */
40281     enableTab : function(id){
40282         var tab = this.items[id];
40283         tab.enable();
40284     },
40285
40286     /**
40287      * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
40288      * @param {String/Number} id The id or index of the TabPanelItem to activate.
40289      * @return {Roo.TabPanelItem} The TabPanelItem.
40290      */
40291     activate : function(id)
40292     {
40293         //Roo.log('activite:'  + id);
40294         
40295         var tab = this.items[id];
40296         if(!tab){
40297             return null;
40298         }
40299         if(tab == this.active || tab.disabled){
40300             return tab;
40301         }
40302         var e = {};
40303         this.fireEvent("beforetabchange", this, e, tab);
40304         if(e.cancel !== true && !tab.disabled){
40305             if(this.active){
40306                 this.active.hide();
40307             }
40308             this.active = this.items[id];
40309             this.active.show();
40310             this.fireEvent("tabchange", this, this.active);
40311         }
40312         return tab;
40313     },
40314
40315     /**
40316      * Gets the active {@link Roo.TabPanelItem}.
40317      * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
40318      */
40319     getActiveTab : function(){
40320         return this.active;
40321     },
40322
40323     /**
40324      * Updates the tab body element to fit the height of the container element
40325      * for overflow scrolling
40326      * @param {Number} targetHeight (optional) Override the starting height from the elements height
40327      */
40328     syncHeight : function(targetHeight){
40329         var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
40330         var bm = this.bodyEl.getMargins();
40331         var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
40332         this.bodyEl.setHeight(newHeight);
40333         return newHeight;
40334     },
40335
40336     onResize : function(){
40337         if(this.monitorResize){
40338             this.autoSizeTabs();
40339         }
40340     },
40341
40342     /**
40343      * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
40344      */
40345     beginUpdate : function(){
40346         this.updating = true;
40347     },
40348
40349     /**
40350      * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
40351      */
40352     endUpdate : function(){
40353         this.updating = false;
40354         this.autoSizeTabs();
40355     },
40356
40357     /**
40358      * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
40359      */
40360     autoSizeTabs : function()
40361     {
40362         var count = this.items.length;
40363         var vcount = count - this.hiddenCount;
40364         
40365         if (vcount < 2) {
40366             this.stripEl.hide();
40367         } else {
40368             this.stripEl.show();
40369         }
40370         
40371         if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) {
40372             return;
40373         }
40374         
40375         
40376         var w = Math.max(this.el.getWidth() - this.cpad, 10);
40377         var availWidth = Math.floor(w / vcount);
40378         var b = this.stripBody;
40379         if(b.getWidth() > w){
40380             var tabs = this.items;
40381             this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
40382             if(availWidth < this.minTabWidth){
40383                 /*if(!this.sleft){    // incomplete scrolling code
40384                     this.createScrollButtons();
40385                 }
40386                 this.showScroll();
40387                 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
40388             }
40389         }else{
40390             if(this.currentTabWidth < this.preferredTabWidth){
40391                 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
40392             }
40393         }
40394     },
40395
40396     /**
40397      * Returns the number of tabs in this TabPanel.
40398      * @return {Number}
40399      */
40400      getCount : function(){
40401          return this.items.length;
40402      },
40403
40404     /**
40405      * Resizes all the tabs to the passed width
40406      * @param {Number} The new width
40407      */
40408     setTabWidth : function(width){
40409         this.currentTabWidth = width;
40410         for(var i = 0, len = this.items.length; i < len; i++) {
40411                 if(!this.items[i].isHidden()) {
40412                 this.items[i].setWidth(width);
40413             }
40414         }
40415     },
40416
40417     /**
40418      * Destroys this TabPanel
40419      * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
40420      */
40421     destroy : function(removeEl){
40422         Roo.EventManager.removeResizeListener(this.onResize, this);
40423         for(var i = 0, len = this.items.length; i < len; i++){
40424             this.items[i].purgeListeners();
40425         }
40426         if(removeEl === true){
40427             this.el.update("");
40428             this.el.remove();
40429         }
40430     },
40431     
40432     createStrip : function(container)
40433     {
40434         var strip = document.createElement("nav");
40435         strip.className = Roo.bootstrap.version == 4 ?
40436             "navbar-light bg-light" : 
40437             "navbar navbar-default"; //"x-tabs-wrap";
40438         container.appendChild(strip);
40439         return strip;
40440     },
40441     
40442     createStripList : function(strip)
40443     {
40444         // div wrapper for retard IE
40445         // returns the "tr" element.
40446         strip.innerHTML = '<ul class="nav nav-tabs" role="tablist"></ul>';
40447         //'<div class="x-tabs-strip-wrap">'+
40448           //  '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
40449           //  '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
40450         return strip.firstChild; //.firstChild.firstChild.firstChild;
40451     },
40452     createBody : function(container)
40453     {
40454         var body = document.createElement("div");
40455         Roo.id(body, "tab-body");
40456         //Roo.fly(body).addClass("x-tabs-body");
40457         Roo.fly(body).addClass("tab-content");
40458         container.appendChild(body);
40459         return body;
40460     },
40461     createItemBody :function(bodyEl, id){
40462         var body = Roo.getDom(id);
40463         if(!body){
40464             body = document.createElement("div");
40465             body.id = id;
40466         }
40467         //Roo.fly(body).addClass("x-tabs-item-body");
40468         Roo.fly(body).addClass("tab-pane");
40469          bodyEl.insertBefore(body, bodyEl.firstChild);
40470         return body;
40471     },
40472     /** @private */
40473     createStripElements :  function(stripEl, text, closable, tpl)
40474     {
40475         var td = document.createElement("li"); // was td..
40476         td.className = 'nav-item';
40477         
40478         //stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
40479         
40480         
40481         stripEl.appendChild(td);
40482         /*if(closable){
40483             td.className = "x-tabs-closable";
40484             if(!this.closeTpl){
40485                 this.closeTpl = new Roo.Template(
40486                    '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
40487                    '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
40488                    '<div unselectable="on" class="close-icon">&#160;</div></em></span></a>'
40489                 );
40490             }
40491             var el = this.closeTpl.overwrite(td, {"text": text});
40492             var close = el.getElementsByTagName("div")[0];
40493             var inner = el.getElementsByTagName("em")[0];
40494             return {"el": el, "close": close, "inner": inner};
40495         } else {
40496         */
40497         // not sure what this is..
40498 //            if(!this.tabTpl){
40499                 //this.tabTpl = new Roo.Template(
40500                 //   '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
40501                 //   '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
40502                 //);
40503 //                this.tabTpl = new Roo.Template(
40504 //                   '<a href="#">' +
40505 //                   '<span unselectable="on"' +
40506 //                            (this.disableTooltips ? '' : ' title="{text}"') +
40507 //                            ' >{text}</span></a>'
40508 //                );
40509 //                
40510 //            }
40511
40512
40513             var template = tpl || this.tabTpl || false;
40514             
40515             if(!template){
40516                 template =  new Roo.Template(
40517                         Roo.bootstrap.version == 4 ? 
40518                             (
40519                                 '<a class="nav-link" href="#" unselectable="on"' +
40520                                      (this.disableTooltips ? '' : ' title="{text}"') +
40521                                      ' >{text}</a>'
40522                             ) : (
40523                                 '<a class="nav-link" href="#">' +
40524                                 '<span unselectable="on"' +
40525                                          (this.disableTooltips ? '' : ' title="{text}"') +
40526                                     ' >{text}</span></a>'
40527                             )
40528                 );
40529             }
40530             
40531             switch (typeof(template)) {
40532                 case 'object' :
40533                     break;
40534                 case 'string' :
40535                     template = new Roo.Template(template);
40536                     break;
40537                 default :
40538                     break;
40539             }
40540             
40541             var el = template.overwrite(td, {"text": text});
40542             
40543             var inner = el.getElementsByTagName("span")[0];
40544             
40545             return {"el": el, "inner": inner};
40546             
40547     }
40548         
40549     
40550 });
40551
40552 /**
40553  * @class Roo.TabPanelItem
40554  * @extends Roo.util.Observable
40555  * Represents an individual item (tab plus body) in a TabPanel.
40556  * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
40557  * @param {String} id The id of this TabPanelItem
40558  * @param {String} text The text for the tab of this TabPanelItem
40559  * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
40560  */
40561 Roo.bootstrap.panel.TabItem = function(config){
40562     /**
40563      * The {@link Roo.TabPanel} this TabPanelItem belongs to
40564      * @type Roo.TabPanel
40565      */
40566     this.tabPanel = config.panel;
40567     /**
40568      * The id for this TabPanelItem
40569      * @type String
40570      */
40571     this.id = config.id;
40572     /** @private */
40573     this.disabled = false;
40574     /** @private */
40575     this.text = config.text;
40576     /** @private */
40577     this.loaded = false;
40578     this.closable = config.closable;
40579
40580     /**
40581      * The body element for this TabPanelItem.
40582      * @type Roo.Element
40583      */
40584     this.bodyEl = Roo.get(this.tabPanel.createItemBody(this.tabPanel.bodyEl.dom, config.id));
40585     this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
40586     this.bodyEl.setStyle("display", "block");
40587     this.bodyEl.setStyle("zoom", "1");
40588     //this.hideAction();
40589
40590     var els = this.tabPanel.createStripElements(this.tabPanel.stripEl.dom, config.text, config.closable, config.tpl);
40591     /** @private */
40592     this.el = Roo.get(els.el);
40593     this.inner = Roo.get(els.inner, true);
40594      this.textEl = Roo.bootstrap.version == 4 ?
40595         this.el : Roo.get(this.el.dom.firstChild, true);
40596
40597     this.pnode = this.linode = Roo.get(els.el.parentNode, true);
40598     this.status_node = Roo.bootstrap.version == 4 ? this.el : this.linode;
40599
40600     
40601 //    this.el.on("mousedown", this.onTabMouseDown, this);
40602     this.el.on("click", this.onTabClick, this);
40603     /** @private */
40604     if(config.closable){
40605         var c = Roo.get(els.close, true);
40606         c.dom.title = this.closeText;
40607         c.addClassOnOver("close-over");
40608         c.on("click", this.closeClick, this);
40609      }
40610
40611     this.addEvents({
40612          /**
40613          * @event activate
40614          * Fires when this tab becomes the active tab.
40615          * @param {Roo.TabPanel} tabPanel The parent TabPanel
40616          * @param {Roo.TabPanelItem} this
40617          */
40618         "activate": true,
40619         /**
40620          * @event beforeclose
40621          * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
40622          * @param {Roo.TabPanelItem} this
40623          * @param {Object} e Set cancel to true on this object to cancel the close.
40624          */
40625         "beforeclose": true,
40626         /**
40627          * @event close
40628          * Fires when this tab is closed.
40629          * @param {Roo.TabPanelItem} this
40630          */
40631          "close": true,
40632         /**
40633          * @event deactivate
40634          * Fires when this tab is no longer the active tab.
40635          * @param {Roo.TabPanel} tabPanel The parent TabPanel
40636          * @param {Roo.TabPanelItem} this
40637          */
40638          "deactivate" : true
40639     });
40640     this.hidden = false;
40641
40642     Roo.bootstrap.panel.TabItem.superclass.constructor.call(this);
40643 };
40644
40645 Roo.extend(Roo.bootstrap.panel.TabItem, Roo.util.Observable,
40646            {
40647     purgeListeners : function(){
40648        Roo.util.Observable.prototype.purgeListeners.call(this);
40649        this.el.removeAllListeners();
40650     },
40651     /**
40652      * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
40653      */
40654     show : function(){
40655         this.status_node.addClass("active");
40656         this.showAction();
40657         if(Roo.isOpera){
40658             this.tabPanel.stripWrap.repaint();
40659         }
40660         this.fireEvent("activate", this.tabPanel, this);
40661     },
40662
40663     /**
40664      * Returns true if this tab is the active tab.
40665      * @return {Boolean}
40666      */
40667     isActive : function(){
40668         return this.tabPanel.getActiveTab() == this;
40669     },
40670
40671     /**
40672      * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
40673      */
40674     hide : function(){
40675         this.status_node.removeClass("active");
40676         this.hideAction();
40677         this.fireEvent("deactivate", this.tabPanel, this);
40678     },
40679
40680     hideAction : function(){
40681         this.bodyEl.hide();
40682         this.bodyEl.setStyle("position", "absolute");
40683         this.bodyEl.setLeft("-20000px");
40684         this.bodyEl.setTop("-20000px");
40685     },
40686
40687     showAction : function(){
40688         this.bodyEl.setStyle("position", "relative");
40689         this.bodyEl.setTop("");
40690         this.bodyEl.setLeft("");
40691         this.bodyEl.show();
40692     },
40693
40694     /**
40695      * Set the tooltip for the tab.
40696      * @param {String} tooltip The tab's tooltip
40697      */
40698     setTooltip : function(text){
40699         if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
40700             this.textEl.dom.qtip = text;
40701             this.textEl.dom.removeAttribute('title');
40702         }else{
40703             this.textEl.dom.title = text;
40704         }
40705     },
40706
40707     onTabClick : function(e){
40708         e.preventDefault();
40709         this.tabPanel.activate(this.id);
40710     },
40711
40712     onTabMouseDown : function(e){
40713         e.preventDefault();
40714         this.tabPanel.activate(this.id);
40715     },
40716 /*
40717     getWidth : function(){
40718         return this.inner.getWidth();
40719     },
40720
40721     setWidth : function(width){
40722         var iwidth = width - this.linode.getPadding("lr");
40723         this.inner.setWidth(iwidth);
40724         this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
40725         this.linode.setWidth(width);
40726     },
40727 */
40728     /**
40729      * Show or hide the tab
40730      * @param {Boolean} hidden True to hide or false to show.
40731      */
40732     setHidden : function(hidden){
40733         this.hidden = hidden;
40734         this.linode.setStyle("display", hidden ? "none" : "");
40735     },
40736
40737     /**
40738      * Returns true if this tab is "hidden"
40739      * @return {Boolean}
40740      */
40741     isHidden : function(){
40742         return this.hidden;
40743     },
40744
40745     /**
40746      * Returns the text for this tab
40747      * @return {String}
40748      */
40749     getText : function(){
40750         return this.text;
40751     },
40752     /*
40753     autoSize : function(){
40754         //this.el.beginMeasure();
40755         this.textEl.setWidth(1);
40756         /*
40757          *  #2804 [new] Tabs in Roojs
40758          *  increase the width by 2-4 pixels to prevent the ellipssis showing in chrome
40759          */
40760         //this.setWidth(this.textEl.dom.scrollWidth+this.linode.getPadding("lr")+this.inner.getPadding("lr") + 2);
40761         //this.el.endMeasure();
40762     //},
40763
40764     /**
40765      * Sets the text for the tab (Note: this also sets the tooltip text)
40766      * @param {String} text The tab's text and tooltip
40767      */
40768     setText : function(text){
40769         this.text = text;
40770         this.textEl.update(text);
40771         this.setTooltip(text);
40772         //if(!this.tabPanel.resizeTabs){
40773         //    this.autoSize();
40774         //}
40775     },
40776     /**
40777      * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
40778      */
40779     activate : function(){
40780         this.tabPanel.activate(this.id);
40781     },
40782
40783     /**
40784      * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
40785      */
40786     disable : function(){
40787         if(this.tabPanel.active != this){
40788             this.disabled = true;
40789             this.status_node.addClass("disabled");
40790         }
40791     },
40792
40793     /**
40794      * Enables this TabPanelItem if it was previously disabled.
40795      */
40796     enable : function(){
40797         this.disabled = false;
40798         this.status_node.removeClass("disabled");
40799     },
40800
40801     /**
40802      * Sets the content for this TabPanelItem.
40803      * @param {String} content The content
40804      * @param {Boolean} loadScripts true to look for and load scripts
40805      */
40806     setContent : function(content, loadScripts){
40807         this.bodyEl.update(content, loadScripts);
40808     },
40809
40810     /**
40811      * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
40812      * @return {Roo.UpdateManager} The UpdateManager
40813      */
40814     getUpdateManager : function(){
40815         return this.bodyEl.getUpdateManager();
40816     },
40817
40818     /**
40819      * Set a URL to be used to load the content for this TabPanelItem.
40820      * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
40821      * @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)
40822      * @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)
40823      * @return {Roo.UpdateManager} The UpdateManager
40824      */
40825     setUrl : function(url, params, loadOnce){
40826         if(this.refreshDelegate){
40827             this.un('activate', this.refreshDelegate);
40828         }
40829         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
40830         this.on("activate", this.refreshDelegate);
40831         return this.bodyEl.getUpdateManager();
40832     },
40833
40834     /** @private */
40835     _handleRefresh : function(url, params, loadOnce){
40836         if(!loadOnce || !this.loaded){
40837             var updater = this.bodyEl.getUpdateManager();
40838             updater.update(url, params, this._setLoaded.createDelegate(this));
40839         }
40840     },
40841
40842     /**
40843      *   Forces a content refresh from the URL specified in the {@link #setUrl} method.
40844      *   Will fail silently if the setUrl method has not been called.
40845      *   This does not activate the panel, just updates its content.
40846      */
40847     refresh : function(){
40848         if(this.refreshDelegate){
40849            this.loaded = false;
40850            this.refreshDelegate();
40851         }
40852     },
40853
40854     /** @private */
40855     _setLoaded : function(){
40856         this.loaded = true;
40857     },
40858
40859     /** @private */
40860     closeClick : function(e){
40861         var o = {};
40862         e.stopEvent();
40863         this.fireEvent("beforeclose", this, o);
40864         if(o.cancel !== true){
40865             this.tabPanel.removeTab(this.id);
40866         }
40867     },
40868     /**
40869      * The text displayed in the tooltip for the close icon.
40870      * @type String
40871      */
40872     closeText : "Close this tab"
40873 });
40874 /**
40875 *    This script refer to:
40876 *    Title: International Telephone Input
40877 *    Author: Jack O'Connor
40878 *    Code version:  v12.1.12
40879 *    Availability: https://github.com/jackocnr/intl-tel-input.git
40880 **/
40881
40882 Roo.bootstrap.PhoneInputData = function() {
40883     var d = [
40884       [
40885         "Afghanistan (‫افغانستان‬‎)",
40886         "af",
40887         "93"
40888       ],
40889       [
40890         "Albania (Shqipëri)",
40891         "al",
40892         "355"
40893       ],
40894       [
40895         "Algeria (‫الجزائر‬‎)",
40896         "dz",
40897         "213"
40898       ],
40899       [
40900         "American Samoa",
40901         "as",
40902         "1684"
40903       ],
40904       [
40905         "Andorra",
40906         "ad",
40907         "376"
40908       ],
40909       [
40910         "Angola",
40911         "ao",
40912         "244"
40913       ],
40914       [
40915         "Anguilla",
40916         "ai",
40917         "1264"
40918       ],
40919       [
40920         "Antigua and Barbuda",
40921         "ag",
40922         "1268"
40923       ],
40924       [
40925         "Argentina",
40926         "ar",
40927         "54"
40928       ],
40929       [
40930         "Armenia (Հայաստան)",
40931         "am",
40932         "374"
40933       ],
40934       [
40935         "Aruba",
40936         "aw",
40937         "297"
40938       ],
40939       [
40940         "Australia",
40941         "au",
40942         "61",
40943         0
40944       ],
40945       [
40946         "Austria (Österreich)",
40947         "at",
40948         "43"
40949       ],
40950       [
40951         "Azerbaijan (Azərbaycan)",
40952         "az",
40953         "994"
40954       ],
40955       [
40956         "Bahamas",
40957         "bs",
40958         "1242"
40959       ],
40960       [
40961         "Bahrain (‫البحرين‬‎)",
40962         "bh",
40963         "973"
40964       ],
40965       [
40966         "Bangladesh (বাংলাদেশ)",
40967         "bd",
40968         "880"
40969       ],
40970       [
40971         "Barbados",
40972         "bb",
40973         "1246"
40974       ],
40975       [
40976         "Belarus (Беларусь)",
40977         "by",
40978         "375"
40979       ],
40980       [
40981         "Belgium (België)",
40982         "be",
40983         "32"
40984       ],
40985       [
40986         "Belize",
40987         "bz",
40988         "501"
40989       ],
40990       [
40991         "Benin (Bénin)",
40992         "bj",
40993         "229"
40994       ],
40995       [
40996         "Bermuda",
40997         "bm",
40998         "1441"
40999       ],
41000       [
41001         "Bhutan (འབྲུག)",
41002         "bt",
41003         "975"
41004       ],
41005       [
41006         "Bolivia",
41007         "bo",
41008         "591"
41009       ],
41010       [
41011         "Bosnia and Herzegovina (Босна и Херцеговина)",
41012         "ba",
41013         "387"
41014       ],
41015       [
41016         "Botswana",
41017         "bw",
41018         "267"
41019       ],
41020       [
41021         "Brazil (Brasil)",
41022         "br",
41023         "55"
41024       ],
41025       [
41026         "British Indian Ocean Territory",
41027         "io",
41028         "246"
41029       ],
41030       [
41031         "British Virgin Islands",
41032         "vg",
41033         "1284"
41034       ],
41035       [
41036         "Brunei",
41037         "bn",
41038         "673"
41039       ],
41040       [
41041         "Bulgaria (България)",
41042         "bg",
41043         "359"
41044       ],
41045       [
41046         "Burkina Faso",
41047         "bf",
41048         "226"
41049       ],
41050       [
41051         "Burundi (Uburundi)",
41052         "bi",
41053         "257"
41054       ],
41055       [
41056         "Cambodia (កម្ពុជា)",
41057         "kh",
41058         "855"
41059       ],
41060       [
41061         "Cameroon (Cameroun)",
41062         "cm",
41063         "237"
41064       ],
41065       [
41066         "Canada",
41067         "ca",
41068         "1",
41069         1,
41070         ["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"]
41071       ],
41072       [
41073         "Cape Verde (Kabu Verdi)",
41074         "cv",
41075         "238"
41076       ],
41077       [
41078         "Caribbean Netherlands",
41079         "bq",
41080         "599",
41081         1
41082       ],
41083       [
41084         "Cayman Islands",
41085         "ky",
41086         "1345"
41087       ],
41088       [
41089         "Central African Republic (République centrafricaine)",
41090         "cf",
41091         "236"
41092       ],
41093       [
41094         "Chad (Tchad)",
41095         "td",
41096         "235"
41097       ],
41098       [
41099         "Chile",
41100         "cl",
41101         "56"
41102       ],
41103       [
41104         "China (中国)",
41105         "cn",
41106         "86"
41107       ],
41108       [
41109         "Christmas Island",
41110         "cx",
41111         "61",
41112         2
41113       ],
41114       [
41115         "Cocos (Keeling) Islands",
41116         "cc",
41117         "61",
41118         1
41119       ],
41120       [
41121         "Colombia",
41122         "co",
41123         "57"
41124       ],
41125       [
41126         "Comoros (‫جزر القمر‬‎)",
41127         "km",
41128         "269"
41129       ],
41130       [
41131         "Congo (DRC) (Jamhuri ya Kidemokrasia ya Kongo)",
41132         "cd",
41133         "243"
41134       ],
41135       [
41136         "Congo (Republic) (Congo-Brazzaville)",
41137         "cg",
41138         "242"
41139       ],
41140       [
41141         "Cook Islands",
41142         "ck",
41143         "682"
41144       ],
41145       [
41146         "Costa Rica",
41147         "cr",
41148         "506"
41149       ],
41150       [
41151         "Côte d’Ivoire",
41152         "ci",
41153         "225"
41154       ],
41155       [
41156         "Croatia (Hrvatska)",
41157         "hr",
41158         "385"
41159       ],
41160       [
41161         "Cuba",
41162         "cu",
41163         "53"
41164       ],
41165       [
41166         "Curaçao",
41167         "cw",
41168         "599",
41169         0
41170       ],
41171       [
41172         "Cyprus (Κύπρος)",
41173         "cy",
41174         "357"
41175       ],
41176       [
41177         "Czech Republic (Česká republika)",
41178         "cz",
41179         "420"
41180       ],
41181       [
41182         "Denmark (Danmark)",
41183         "dk",
41184         "45"
41185       ],
41186       [
41187         "Djibouti",
41188         "dj",
41189         "253"
41190       ],
41191       [
41192         "Dominica",
41193         "dm",
41194         "1767"
41195       ],
41196       [
41197         "Dominican Republic (República Dominicana)",
41198         "do",
41199         "1",
41200         2,
41201         ["809", "829", "849"]
41202       ],
41203       [
41204         "Ecuador",
41205         "ec",
41206         "593"
41207       ],
41208       [
41209         "Egypt (‫مصر‬‎)",
41210         "eg",
41211         "20"
41212       ],
41213       [
41214         "El Salvador",
41215         "sv",
41216         "503"
41217       ],
41218       [
41219         "Equatorial Guinea (Guinea Ecuatorial)",
41220         "gq",
41221         "240"
41222       ],
41223       [
41224         "Eritrea",
41225         "er",
41226         "291"
41227       ],
41228       [
41229         "Estonia (Eesti)",
41230         "ee",
41231         "372"
41232       ],
41233       [
41234         "Ethiopia",
41235         "et",
41236         "251"
41237       ],
41238       [
41239         "Falkland Islands (Islas Malvinas)",
41240         "fk",
41241         "500"
41242       ],
41243       [
41244         "Faroe Islands (Føroyar)",
41245         "fo",
41246         "298"
41247       ],
41248       [
41249         "Fiji",
41250         "fj",
41251         "679"
41252       ],
41253       [
41254         "Finland (Suomi)",
41255         "fi",
41256         "358",
41257         0
41258       ],
41259       [
41260         "France",
41261         "fr",
41262         "33"
41263       ],
41264       [
41265         "French Guiana (Guyane française)",
41266         "gf",
41267         "594"
41268       ],
41269       [
41270         "French Polynesia (Polynésie française)",
41271         "pf",
41272         "689"
41273       ],
41274       [
41275         "Gabon",
41276         "ga",
41277         "241"
41278       ],
41279       [
41280         "Gambia",
41281         "gm",
41282         "220"
41283       ],
41284       [
41285         "Georgia (საქართველო)",
41286         "ge",
41287         "995"
41288       ],
41289       [
41290         "Germany (Deutschland)",
41291         "de",
41292         "49"
41293       ],
41294       [
41295         "Ghana (Gaana)",
41296         "gh",
41297         "233"
41298       ],
41299       [
41300         "Gibraltar",
41301         "gi",
41302         "350"
41303       ],
41304       [
41305         "Greece (Ελλάδα)",
41306         "gr",
41307         "30"
41308       ],
41309       [
41310         "Greenland (Kalaallit Nunaat)",
41311         "gl",
41312         "299"
41313       ],
41314       [
41315         "Grenada",
41316         "gd",
41317         "1473"
41318       ],
41319       [
41320         "Guadeloupe",
41321         "gp",
41322         "590",
41323         0
41324       ],
41325       [
41326         "Guam",
41327         "gu",
41328         "1671"
41329       ],
41330       [
41331         "Guatemala",
41332         "gt",
41333         "502"
41334       ],
41335       [
41336         "Guernsey",
41337         "gg",
41338         "44",
41339         1
41340       ],
41341       [
41342         "Guinea (Guinée)",
41343         "gn",
41344         "224"
41345       ],
41346       [
41347         "Guinea-Bissau (Guiné Bissau)",
41348         "gw",
41349         "245"
41350       ],
41351       [
41352         "Guyana",
41353         "gy",
41354         "592"
41355       ],
41356       [
41357         "Haiti",
41358         "ht",
41359         "509"
41360       ],
41361       [
41362         "Honduras",
41363         "hn",
41364         "504"
41365       ],
41366       [
41367         "Hong Kong (香港)",
41368         "hk",
41369         "852"
41370       ],
41371       [
41372         "Hungary (Magyarország)",
41373         "hu",
41374         "36"
41375       ],
41376       [
41377         "Iceland (Ísland)",
41378         "is",
41379         "354"
41380       ],
41381       [
41382         "India (भारत)",
41383         "in",
41384         "91"
41385       ],
41386       [
41387         "Indonesia",
41388         "id",
41389         "62"
41390       ],
41391       [
41392         "Iran (‫ایران‬‎)",
41393         "ir",
41394         "98"
41395       ],
41396       [
41397         "Iraq (‫العراق‬‎)",
41398         "iq",
41399         "964"
41400       ],
41401       [
41402         "Ireland",
41403         "ie",
41404         "353"
41405       ],
41406       [
41407         "Isle of Man",
41408         "im",
41409         "44",
41410         2
41411       ],
41412       [
41413         "Israel (‫ישראל‬‎)",
41414         "il",
41415         "972"
41416       ],
41417       [
41418         "Italy (Italia)",
41419         "it",
41420         "39",
41421         0
41422       ],
41423       [
41424         "Jamaica",
41425         "jm",
41426         "1876"
41427       ],
41428       [
41429         "Japan (日本)",
41430         "jp",
41431         "81"
41432       ],
41433       [
41434         "Jersey",
41435         "je",
41436         "44",
41437         3
41438       ],
41439       [
41440         "Jordan (‫الأردن‬‎)",
41441         "jo",
41442         "962"
41443       ],
41444       [
41445         "Kazakhstan (Казахстан)",
41446         "kz",
41447         "7",
41448         1
41449       ],
41450       [
41451         "Kenya",
41452         "ke",
41453         "254"
41454       ],
41455       [
41456         "Kiribati",
41457         "ki",
41458         "686"
41459       ],
41460       [
41461         "Kosovo",
41462         "xk",
41463         "383"
41464       ],
41465       [
41466         "Kuwait (‫الكويت‬‎)",
41467         "kw",
41468         "965"
41469       ],
41470       [
41471         "Kyrgyzstan (Кыргызстан)",
41472         "kg",
41473         "996"
41474       ],
41475       [
41476         "Laos (ລາວ)",
41477         "la",
41478         "856"
41479       ],
41480       [
41481         "Latvia (Latvija)",
41482         "lv",
41483         "371"
41484       ],
41485       [
41486         "Lebanon (‫لبنان‬‎)",
41487         "lb",
41488         "961"
41489       ],
41490       [
41491         "Lesotho",
41492         "ls",
41493         "266"
41494       ],
41495       [
41496         "Liberia",
41497         "lr",
41498         "231"
41499       ],
41500       [
41501         "Libya (‫ليبيا‬‎)",
41502         "ly",
41503         "218"
41504       ],
41505       [
41506         "Liechtenstein",
41507         "li",
41508         "423"
41509       ],
41510       [
41511         "Lithuania (Lietuva)",
41512         "lt",
41513         "370"
41514       ],
41515       [
41516         "Luxembourg",
41517         "lu",
41518         "352"
41519       ],
41520       [
41521         "Macau (澳門)",
41522         "mo",
41523         "853"
41524       ],
41525       [
41526         "Macedonia (FYROM) (Македонија)",
41527         "mk",
41528         "389"
41529       ],
41530       [
41531         "Madagascar (Madagasikara)",
41532         "mg",
41533         "261"
41534       ],
41535       [
41536         "Malawi",
41537         "mw",
41538         "265"
41539       ],
41540       [
41541         "Malaysia",
41542         "my",
41543         "60"
41544       ],
41545       [
41546         "Maldives",
41547         "mv",
41548         "960"
41549       ],
41550       [
41551         "Mali",
41552         "ml",
41553         "223"
41554       ],
41555       [
41556         "Malta",
41557         "mt",
41558         "356"
41559       ],
41560       [
41561         "Marshall Islands",
41562         "mh",
41563         "692"
41564       ],
41565       [
41566         "Martinique",
41567         "mq",
41568         "596"
41569       ],
41570       [
41571         "Mauritania (‫موريتانيا‬‎)",
41572         "mr",
41573         "222"
41574       ],
41575       [
41576         "Mauritius (Moris)",
41577         "mu",
41578         "230"
41579       ],
41580       [
41581         "Mayotte",
41582         "yt",
41583         "262",
41584         1
41585       ],
41586       [
41587         "Mexico (México)",
41588         "mx",
41589         "52"
41590       ],
41591       [
41592         "Micronesia",
41593         "fm",
41594         "691"
41595       ],
41596       [
41597         "Moldova (Republica Moldova)",
41598         "md",
41599         "373"
41600       ],
41601       [
41602         "Monaco",
41603         "mc",
41604         "377"
41605       ],
41606       [
41607         "Mongolia (Монгол)",
41608         "mn",
41609         "976"
41610       ],
41611       [
41612         "Montenegro (Crna Gora)",
41613         "me",
41614         "382"
41615       ],
41616       [
41617         "Montserrat",
41618         "ms",
41619         "1664"
41620       ],
41621       [
41622         "Morocco (‫المغرب‬‎)",
41623         "ma",
41624         "212",
41625         0
41626       ],
41627       [
41628         "Mozambique (Moçambique)",
41629         "mz",
41630         "258"
41631       ],
41632       [
41633         "Myanmar (Burma) (မြန်မာ)",
41634         "mm",
41635         "95"
41636       ],
41637       [
41638         "Namibia (Namibië)",
41639         "na",
41640         "264"
41641       ],
41642       [
41643         "Nauru",
41644         "nr",
41645         "674"
41646       ],
41647       [
41648         "Nepal (नेपाल)",
41649         "np",
41650         "977"
41651       ],
41652       [
41653         "Netherlands (Nederland)",
41654         "nl",
41655         "31"
41656       ],
41657       [
41658         "New Caledonia (Nouvelle-Calédonie)",
41659         "nc",
41660         "687"
41661       ],
41662       [
41663         "New Zealand",
41664         "nz",
41665         "64"
41666       ],
41667       [
41668         "Nicaragua",
41669         "ni",
41670         "505"
41671       ],
41672       [
41673         "Niger (Nijar)",
41674         "ne",
41675         "227"
41676       ],
41677       [
41678         "Nigeria",
41679         "ng",
41680         "234"
41681       ],
41682       [
41683         "Niue",
41684         "nu",
41685         "683"
41686       ],
41687       [
41688         "Norfolk Island",
41689         "nf",
41690         "672"
41691       ],
41692       [
41693         "North Korea (조선 민주주의 인민 공화국)",
41694         "kp",
41695         "850"
41696       ],
41697       [
41698         "Northern Mariana Islands",
41699         "mp",
41700         "1670"
41701       ],
41702       [
41703         "Norway (Norge)",
41704         "no",
41705         "47",
41706         0
41707       ],
41708       [
41709         "Oman (‫عُمان‬‎)",
41710         "om",
41711         "968"
41712       ],
41713       [
41714         "Pakistan (‫پاکستان‬‎)",
41715         "pk",
41716         "92"
41717       ],
41718       [
41719         "Palau",
41720         "pw",
41721         "680"
41722       ],
41723       [
41724         "Palestine (‫فلسطين‬‎)",
41725         "ps",
41726         "970"
41727       ],
41728       [
41729         "Panama (Panamá)",
41730         "pa",
41731         "507"
41732       ],
41733       [
41734         "Papua New Guinea",
41735         "pg",
41736         "675"
41737       ],
41738       [
41739         "Paraguay",
41740         "py",
41741         "595"
41742       ],
41743       [
41744         "Peru (Perú)",
41745         "pe",
41746         "51"
41747       ],
41748       [
41749         "Philippines",
41750         "ph",
41751         "63"
41752       ],
41753       [
41754         "Poland (Polska)",
41755         "pl",
41756         "48"
41757       ],
41758       [
41759         "Portugal",
41760         "pt",
41761         "351"
41762       ],
41763       [
41764         "Puerto Rico",
41765         "pr",
41766         "1",
41767         3,
41768         ["787", "939"]
41769       ],
41770       [
41771         "Qatar (‫قطر‬‎)",
41772         "qa",
41773         "974"
41774       ],
41775       [
41776         "Réunion (La Réunion)",
41777         "re",
41778         "262",
41779         0
41780       ],
41781       [
41782         "Romania (România)",
41783         "ro",
41784         "40"
41785       ],
41786       [
41787         "Russia (Россия)",
41788         "ru",
41789         "7",
41790         0
41791       ],
41792       [
41793         "Rwanda",
41794         "rw",
41795         "250"
41796       ],
41797       [
41798         "Saint Barthélemy",
41799         "bl",
41800         "590",
41801         1
41802       ],
41803       [
41804         "Saint Helena",
41805         "sh",
41806         "290"
41807       ],
41808       [
41809         "Saint Kitts and Nevis",
41810         "kn",
41811         "1869"
41812       ],
41813       [
41814         "Saint Lucia",
41815         "lc",
41816         "1758"
41817       ],
41818       [
41819         "Saint Martin (Saint-Martin (partie française))",
41820         "mf",
41821         "590",
41822         2
41823       ],
41824       [
41825         "Saint Pierre and Miquelon (Saint-Pierre-et-Miquelon)",
41826         "pm",
41827         "508"
41828       ],
41829       [
41830         "Saint Vincent and the Grenadines",
41831         "vc",
41832         "1784"
41833       ],
41834       [
41835         "Samoa",
41836         "ws",
41837         "685"
41838       ],
41839       [
41840         "San Marino",
41841         "sm",
41842         "378"
41843       ],
41844       [
41845         "São Tomé and Príncipe (São Tomé e Príncipe)",
41846         "st",
41847         "239"
41848       ],
41849       [
41850         "Saudi Arabia (‫المملكة العربية السعودية‬‎)",
41851         "sa",
41852         "966"
41853       ],
41854       [
41855         "Senegal (Sénégal)",
41856         "sn",
41857         "221"
41858       ],
41859       [
41860         "Serbia (Србија)",
41861         "rs",
41862         "381"
41863       ],
41864       [
41865         "Seychelles",
41866         "sc",
41867         "248"
41868       ],
41869       [
41870         "Sierra Leone",
41871         "sl",
41872         "232"
41873       ],
41874       [
41875         "Singapore",
41876         "sg",
41877         "65"
41878       ],
41879       [
41880         "Sint Maarten",
41881         "sx",
41882         "1721"
41883       ],
41884       [
41885         "Slovakia (Slovensko)",
41886         "sk",
41887         "421"
41888       ],
41889       [
41890         "Slovenia (Slovenija)",
41891         "si",
41892         "386"
41893       ],
41894       [
41895         "Solomon Islands",
41896         "sb",
41897         "677"
41898       ],
41899       [
41900         "Somalia (Soomaaliya)",
41901         "so",
41902         "252"
41903       ],
41904       [
41905         "South Africa",
41906         "za",
41907         "27"
41908       ],
41909       [
41910         "South Korea (대한민국)",
41911         "kr",
41912         "82"
41913       ],
41914       [
41915         "South Sudan (‫جنوب السودان‬‎)",
41916         "ss",
41917         "211"
41918       ],
41919       [
41920         "Spain (España)",
41921         "es",
41922         "34"
41923       ],
41924       [
41925         "Sri Lanka (ශ්‍රී ලංකාව)",
41926         "lk",
41927         "94"
41928       ],
41929       [
41930         "Sudan (‫السودان‬‎)",
41931         "sd",
41932         "249"
41933       ],
41934       [
41935         "Suriname",
41936         "sr",
41937         "597"
41938       ],
41939       [
41940         "Svalbard and Jan Mayen",
41941         "sj",
41942         "47",
41943         1
41944       ],
41945       [
41946         "Swaziland",
41947         "sz",
41948         "268"
41949       ],
41950       [
41951         "Sweden (Sverige)",
41952         "se",
41953         "46"
41954       ],
41955       [
41956         "Switzerland (Schweiz)",
41957         "ch",
41958         "41"
41959       ],
41960       [
41961         "Syria (‫سوريا‬‎)",
41962         "sy",
41963         "963"
41964       ],
41965       [
41966         "Taiwan (台灣)",
41967         "tw",
41968         "886"
41969       ],
41970       [
41971         "Tajikistan",
41972         "tj",
41973         "992"
41974       ],
41975       [
41976         "Tanzania",
41977         "tz",
41978         "255"
41979       ],
41980       [
41981         "Thailand (ไทย)",
41982         "th",
41983         "66"
41984       ],
41985       [
41986         "Timor-Leste",
41987         "tl",
41988         "670"
41989       ],
41990       [
41991         "Togo",
41992         "tg",
41993         "228"
41994       ],
41995       [
41996         "Tokelau",
41997         "tk",
41998         "690"
41999       ],
42000       [
42001         "Tonga",
42002         "to",
42003         "676"
42004       ],
42005       [
42006         "Trinidad and Tobago",
42007         "tt",
42008         "1868"
42009       ],
42010       [
42011         "Tunisia (‫تونس‬‎)",
42012         "tn",
42013         "216"
42014       ],
42015       [
42016         "Turkey (Türkiye)",
42017         "tr",
42018         "90"
42019       ],
42020       [
42021         "Turkmenistan",
42022         "tm",
42023         "993"
42024       ],
42025       [
42026         "Turks and Caicos Islands",
42027         "tc",
42028         "1649"
42029       ],
42030       [
42031         "Tuvalu",
42032         "tv",
42033         "688"
42034       ],
42035       [
42036         "U.S. Virgin Islands",
42037         "vi",
42038         "1340"
42039       ],
42040       [
42041         "Uganda",
42042         "ug",
42043         "256"
42044       ],
42045       [
42046         "Ukraine (Україна)",
42047         "ua",
42048         "380"
42049       ],
42050       [
42051         "United Arab Emirates (‫الإمارات العربية المتحدة‬‎)",
42052         "ae",
42053         "971"
42054       ],
42055       [
42056         "United Kingdom",
42057         "gb",
42058         "44",
42059         0
42060       ],
42061       [
42062         "United States",
42063         "us",
42064         "1",
42065         0
42066       ],
42067       [
42068         "Uruguay",
42069         "uy",
42070         "598"
42071       ],
42072       [
42073         "Uzbekistan (Oʻzbekiston)",
42074         "uz",
42075         "998"
42076       ],
42077       [
42078         "Vanuatu",
42079         "vu",
42080         "678"
42081       ],
42082       [
42083         "Vatican City (Città del Vaticano)",
42084         "va",
42085         "39",
42086         1
42087       ],
42088       [
42089         "Venezuela",
42090         "ve",
42091         "58"
42092       ],
42093       [
42094         "Vietnam (Việt Nam)",
42095         "vn",
42096         "84"
42097       ],
42098       [
42099         "Wallis and Futuna (Wallis-et-Futuna)",
42100         "wf",
42101         "681"
42102       ],
42103       [
42104         "Western Sahara (‫الصحراء الغربية‬‎)",
42105         "eh",
42106         "212",
42107         1
42108       ],
42109       [
42110         "Yemen (‫اليمن‬‎)",
42111         "ye",
42112         "967"
42113       ],
42114       [
42115         "Zambia",
42116         "zm",
42117         "260"
42118       ],
42119       [
42120         "Zimbabwe",
42121         "zw",
42122         "263"
42123       ],
42124       [
42125         "Åland Islands",
42126         "ax",
42127         "358",
42128         1
42129       ]
42130   ];
42131   
42132   return d;
42133 }/**
42134 *    This script refer to:
42135 *    Title: International Telephone Input
42136 *    Author: Jack O'Connor
42137 *    Code version:  v12.1.12
42138 *    Availability: https://github.com/jackocnr/intl-tel-input.git
42139 **/
42140
42141 /**
42142  * @class Roo.bootstrap.PhoneInput
42143  * @extends Roo.bootstrap.TriggerField
42144  * An input with International dial-code selection
42145  
42146  * @cfg {String} defaultDialCode default '+852'
42147  * @cfg {Array} preferedCountries default []
42148   
42149  * @constructor
42150  * Create a new PhoneInput.
42151  * @param {Object} config Configuration options
42152  */
42153
42154 Roo.bootstrap.PhoneInput = function(config) {
42155     Roo.bootstrap.PhoneInput.superclass.constructor.call(this, config);
42156 };
42157
42158 Roo.extend(Roo.bootstrap.PhoneInput, Roo.bootstrap.TriggerField, {
42159         
42160         listWidth: undefined,
42161         
42162         selectedClass: 'active',
42163         
42164         invalidClass : "has-warning",
42165         
42166         validClass: 'has-success',
42167         
42168         allowed: '0123456789',
42169         
42170         max_length: 15,
42171         
42172         /**
42173          * @cfg {String} defaultDialCode The default dial code when initializing the input
42174          */
42175         defaultDialCode: '+852',
42176         
42177         /**
42178          * @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
42179          */
42180         preferedCountries: false,
42181         
42182         getAutoCreate : function()
42183         {
42184             var data = Roo.bootstrap.PhoneInputData();
42185             var align = this.labelAlign || this.parentLabelAlign();
42186             var id = Roo.id();
42187             
42188             this.allCountries = [];
42189             this.dialCodeMapping = [];
42190             
42191             for (var i = 0; i < data.length; i++) {
42192               var c = data[i];
42193               this.allCountries[i] = {
42194                 name: c[0],
42195                 iso2: c[1],
42196                 dialCode: c[2],
42197                 priority: c[3] || 0,
42198                 areaCodes: c[4] || null
42199               };
42200               this.dialCodeMapping[c[2]] = {
42201                   name: c[0],
42202                   iso2: c[1],
42203                   priority: c[3] || 0,
42204                   areaCodes: c[4] || null
42205               };
42206             }
42207             
42208             var cfg = {
42209                 cls: 'form-group',
42210                 cn: []
42211             };
42212             
42213             var input =  {
42214                 tag: 'input',
42215                 id : id,
42216                 // type: 'number', -- do not use number - we get the flaky up/down arrows.
42217                 maxlength: this.max_length,
42218                 cls : 'form-control tel-input',
42219                 autocomplete: 'new-password'
42220             };
42221             
42222             var hiddenInput = {
42223                 tag: 'input',
42224                 type: 'hidden',
42225                 cls: 'hidden-tel-input'
42226             };
42227             
42228             if (this.name) {
42229                 hiddenInput.name = this.name;
42230             }
42231             
42232             if (this.disabled) {
42233                 input.disabled = true;
42234             }
42235             
42236             var flag_container = {
42237                 tag: 'div',
42238                 cls: 'flag-box',
42239                 cn: [
42240                     {
42241                         tag: 'div',
42242                         cls: 'flag'
42243                     },
42244                     {
42245                         tag: 'div',
42246                         cls: 'caret'
42247                     }
42248                 ]
42249             };
42250             
42251             var box = {
42252                 tag: 'div',
42253                 cls: this.hasFeedback ? 'has-feedback' : '',
42254                 cn: [
42255                     hiddenInput,
42256                     input,
42257                     {
42258                         tag: 'input',
42259                         cls: 'dial-code-holder',
42260                         disabled: true
42261                     }
42262                 ]
42263             };
42264             
42265             var container = {
42266                 cls: 'roo-select2-container input-group',
42267                 cn: [
42268                     flag_container,
42269                     box
42270                 ]
42271             };
42272             
42273             if (this.fieldLabel.length) {
42274                 var indicator = {
42275                     tag: 'i',
42276                     tooltip: 'This field is required'
42277                 };
42278                 
42279                 var label = {
42280                     tag: 'label',
42281                     'for':  id,
42282                     cls: 'control-label',
42283                     cn: []
42284                 };
42285                 
42286                 var label_text = {
42287                     tag: 'span',
42288                     html: this.fieldLabel
42289                 };
42290                 
42291                 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
42292                 label.cn = [
42293                     indicator,
42294                     label_text
42295                 ];
42296                 
42297                 if(this.indicatorpos == 'right') {
42298                     indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
42299                     label.cn = [
42300                         label_text,
42301                         indicator
42302                     ];
42303                 }
42304                 
42305                 if(align == 'left') {
42306                     container = {
42307                         tag: 'div',
42308                         cn: [
42309                             container
42310                         ]
42311                     };
42312                     
42313                     if(this.labelWidth > 12){
42314                         label.style = "width: " + this.labelWidth + 'px';
42315                     }
42316                     if(this.labelWidth < 13 && this.labelmd == 0){
42317                         this.labelmd = this.labelWidth;
42318                     }
42319                     if(this.labellg > 0){
42320                         label.cls += ' col-lg-' + this.labellg;
42321                         input.cls += ' col-lg-' + (12 - this.labellg);
42322                     }
42323                     if(this.labelmd > 0){
42324                         label.cls += ' col-md-' + this.labelmd;
42325                         container.cls += ' col-md-' + (12 - this.labelmd);
42326                     }
42327                     if(this.labelsm > 0){
42328                         label.cls += ' col-sm-' + this.labelsm;
42329                         container.cls += ' col-sm-' + (12 - this.labelsm);
42330                     }
42331                     if(this.labelxs > 0){
42332                         label.cls += ' col-xs-' + this.labelxs;
42333                         container.cls += ' col-xs-' + (12 - this.labelxs);
42334                     }
42335                 }
42336             }
42337             
42338             cfg.cn = [
42339                 label,
42340                 container
42341             ];
42342             
42343             var settings = this;
42344             
42345             ['xs','sm','md','lg'].map(function(size){
42346                 if (settings[size]) {
42347                     cfg.cls += ' col-' + size + '-' + settings[size];
42348                 }
42349             });
42350             
42351             this.store = new Roo.data.Store({
42352                 proxy : new Roo.data.MemoryProxy({}),
42353                 reader : new Roo.data.JsonReader({
42354                     fields : [
42355                         {
42356                             'name' : 'name',
42357                             'type' : 'string'
42358                         },
42359                         {
42360                             'name' : 'iso2',
42361                             'type' : 'string'
42362                         },
42363                         {
42364                             'name' : 'dialCode',
42365                             'type' : 'string'
42366                         },
42367                         {
42368                             'name' : 'priority',
42369                             'type' : 'string'
42370                         },
42371                         {
42372                             'name' : 'areaCodes',
42373                             'type' : 'string'
42374                         }
42375                     ]
42376                 })
42377             });
42378             
42379             if(!this.preferedCountries) {
42380                 this.preferedCountries = [
42381                     'hk',
42382                     'gb',
42383                     'us'
42384                 ];
42385             }
42386             
42387             var p = this.preferedCountries.reverse();
42388             
42389             if(p) {
42390                 for (var i = 0; i < p.length; i++) {
42391                     for (var j = 0; j < this.allCountries.length; j++) {
42392                         if(this.allCountries[j].iso2 == p[i]) {
42393                             var t = this.allCountries[j];
42394                             this.allCountries.splice(j,1);
42395                             this.allCountries.unshift(t);
42396                         }
42397                     } 
42398                 }
42399             }
42400             
42401             this.store.proxy.data = {
42402                 success: true,
42403                 data: this.allCountries
42404             };
42405             
42406             return cfg;
42407         },
42408         
42409         initEvents : function()
42410         {
42411             this.createList();
42412             Roo.bootstrap.PhoneInput.superclass.initEvents.call(this);
42413             
42414             this.indicator = this.indicatorEl();
42415             this.flag = this.flagEl();
42416             this.dialCodeHolder = this.dialCodeHolderEl();
42417             
42418             this.trigger = this.el.select('div.flag-box',true).first();
42419             this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
42420             
42421             var _this = this;
42422             
42423             (function(){
42424                 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
42425                 _this.list.setWidth(lw);
42426             }).defer(100);
42427             
42428             this.list.on('mouseover', this.onViewOver, this);
42429             this.list.on('mousemove', this.onViewMove, this);
42430             this.inputEl().on("keyup", this.onKeyUp, this);
42431             this.inputEl().on("keypress", this.onKeyPress, this);
42432             
42433             this.tpl = '<li><a href="#"><div class="flag {iso2}"></div>{name} <span class="dial-code">+{dialCode}</span></a></li>';
42434
42435             this.view = new Roo.View(this.list, this.tpl, {
42436                 singleSelect:true, store: this.store, selectedClass: this.selectedClass
42437             });
42438             
42439             this.view.on('click', this.onViewClick, this);
42440             this.setValue(this.defaultDialCode);
42441         },
42442         
42443         onTriggerClick : function(e)
42444         {
42445             Roo.log('trigger click');
42446             if(this.disabled){
42447                 return;
42448             }
42449             
42450             if(this.isExpanded()){
42451                 this.collapse();
42452                 this.hasFocus = false;
42453             }else {
42454                 this.store.load({});
42455                 this.hasFocus = true;
42456                 this.expand();
42457             }
42458         },
42459         
42460         isExpanded : function()
42461         {
42462             return this.list.isVisible();
42463         },
42464         
42465         collapse : function()
42466         {
42467             if(!this.isExpanded()){
42468                 return;
42469             }
42470             this.list.hide();
42471             Roo.get(document).un('mousedown', this.collapseIf, this);
42472             Roo.get(document).un('mousewheel', this.collapseIf, this);
42473             this.fireEvent('collapse', this);
42474             this.validate();
42475         },
42476         
42477         expand : function()
42478         {
42479             Roo.log('expand');
42480
42481             if(this.isExpanded() || !this.hasFocus){
42482                 return;
42483             }
42484             
42485             var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
42486             this.list.setWidth(lw);
42487             
42488             this.list.show();
42489             this.restrictHeight();
42490             
42491             Roo.get(document).on('mousedown', this.collapseIf, this);
42492             Roo.get(document).on('mousewheel', this.collapseIf, this);
42493             
42494             this.fireEvent('expand', this);
42495         },
42496         
42497         restrictHeight : function()
42498         {
42499             this.list.alignTo(this.inputEl(), this.listAlign);
42500             this.list.alignTo(this.inputEl(), this.listAlign);
42501         },
42502         
42503         onViewOver : function(e, t)
42504         {
42505             if(this.inKeyMode){
42506                 return;
42507             }
42508             var item = this.view.findItemFromChild(t);
42509             
42510             if(item){
42511                 var index = this.view.indexOf(item);
42512                 this.select(index, false);
42513             }
42514         },
42515
42516         // private
42517         onViewClick : function(view, doFocus, el, e)
42518         {
42519             var index = this.view.getSelectedIndexes()[0];
42520             
42521             var r = this.store.getAt(index);
42522             
42523             if(r){
42524                 this.onSelect(r, index);
42525             }
42526             if(doFocus !== false && !this.blockFocus){
42527                 this.inputEl().focus();
42528             }
42529         },
42530         
42531         onViewMove : function(e, t)
42532         {
42533             this.inKeyMode = false;
42534         },
42535         
42536         select : function(index, scrollIntoView)
42537         {
42538             this.selectedIndex = index;
42539             this.view.select(index);
42540             if(scrollIntoView !== false){
42541                 var el = this.view.getNode(index);
42542                 if(el){
42543                     this.list.scrollChildIntoView(el, false);
42544                 }
42545             }
42546         },
42547         
42548         createList : function()
42549         {
42550             this.list = Roo.get(document.body).createChild({
42551                 tag: 'ul',
42552                 cls: 'typeahead typeahead-long dropdown-menu tel-list',
42553                 style: 'display:none'
42554             });
42555             
42556             this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
42557         },
42558         
42559         collapseIf : function(e)
42560         {
42561             var in_combo  = e.within(this.el);
42562             var in_list =  e.within(this.list);
42563             var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
42564             
42565             if (in_combo || in_list || is_list) {
42566                 return;
42567             }
42568             this.collapse();
42569         },
42570         
42571         onSelect : function(record, index)
42572         {
42573             if(this.fireEvent('beforeselect', this, record, index) !== false){
42574                 
42575                 this.setFlagClass(record.data.iso2);
42576                 this.setDialCode(record.data.dialCode);
42577                 this.hasFocus = false;
42578                 this.collapse();
42579                 this.fireEvent('select', this, record, index);
42580             }
42581         },
42582         
42583         flagEl : function()
42584         {
42585             var flag = this.el.select('div.flag',true).first();
42586             if(!flag){
42587                 return false;
42588             }
42589             return flag;
42590         },
42591         
42592         dialCodeHolderEl : function()
42593         {
42594             var d = this.el.select('input.dial-code-holder',true).first();
42595             if(!d){
42596                 return false;
42597             }
42598             return d;
42599         },
42600         
42601         setDialCode : function(v)
42602         {
42603             this.dialCodeHolder.dom.value = '+'+v;
42604         },
42605         
42606         setFlagClass : function(n)
42607         {
42608             this.flag.dom.className = 'flag '+n;
42609         },
42610         
42611         getValue : function()
42612         {
42613             var v = this.inputEl().getValue();
42614             if(this.dialCodeHolder) {
42615                 v = this.dialCodeHolder.dom.value+this.inputEl().getValue();
42616             }
42617             return v;
42618         },
42619         
42620         setValue : function(v)
42621         {
42622             var d = this.getDialCode(v);
42623             
42624             //invalid dial code
42625             if(v.length == 0 || !d || d.length == 0) {
42626                 if(this.rendered){
42627                     this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
42628                     this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
42629                 }
42630                 return;
42631             }
42632             
42633             //valid dial code
42634             this.setFlagClass(this.dialCodeMapping[d].iso2);
42635             this.setDialCode(d);
42636             this.inputEl().dom.value = v.replace('+'+d,'');
42637             this.hiddenEl().dom.value = this.getValue();
42638             
42639             this.validate();
42640         },
42641         
42642         getDialCode : function(v)
42643         {
42644             v = v ||  '';
42645             
42646             if (v.length == 0) {
42647                 return this.dialCodeHolder.dom.value;
42648             }
42649             
42650             var dialCode = "";
42651             if (v.charAt(0) != "+") {
42652                 return false;
42653             }
42654             var numericChars = "";
42655             for (var i = 1; i < v.length; i++) {
42656               var c = v.charAt(i);
42657               if (!isNaN(c)) {
42658                 numericChars += c;
42659                 if (this.dialCodeMapping[numericChars]) {
42660                   dialCode = v.substr(1, i);
42661                 }
42662                 if (numericChars.length == 4) {
42663                   break;
42664                 }
42665               }
42666             }
42667             return dialCode;
42668         },
42669         
42670         reset : function()
42671         {
42672             this.setValue(this.defaultDialCode);
42673             this.validate();
42674         },
42675         
42676         hiddenEl : function()
42677         {
42678             return this.el.select('input.hidden-tel-input',true).first();
42679         },
42680         
42681         // after setting val
42682         onKeyUp : function(e){
42683             this.setValue(this.getValue());
42684         },
42685         
42686         onKeyPress : function(e){
42687             if(this.allowed.indexOf(String.fromCharCode(e.getCharCode())) === -1){
42688                 e.stopEvent();
42689             }
42690         }
42691         
42692 });
42693 /**
42694  * @class Roo.bootstrap.MoneyField
42695  * @extends Roo.bootstrap.ComboBox
42696  * Bootstrap MoneyField class
42697  * 
42698  * @constructor
42699  * Create a new MoneyField.
42700  * @param {Object} config Configuration options
42701  */
42702
42703 Roo.bootstrap.MoneyField = function(config) {
42704     
42705     Roo.bootstrap.MoneyField.superclass.constructor.call(this, config);
42706     
42707 };
42708
42709 Roo.extend(Roo.bootstrap.MoneyField, Roo.bootstrap.ComboBox, {
42710     
42711     /**
42712      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
42713      */
42714     allowDecimals : true,
42715     /**
42716      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
42717      */
42718     decimalSeparator : ".",
42719     /**
42720      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
42721      */
42722     decimalPrecision : 0,
42723     /**
42724      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
42725      */
42726     allowNegative : true,
42727     /**
42728      * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
42729      */
42730     allowZero: true,
42731     /**
42732      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
42733      */
42734     minValue : Number.NEGATIVE_INFINITY,
42735     /**
42736      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
42737      */
42738     maxValue : Number.MAX_VALUE,
42739     /**
42740      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
42741      */
42742     minText : "The minimum value for this field is {0}",
42743     /**
42744      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
42745      */
42746     maxText : "The maximum value for this field is {0}",
42747     /**
42748      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
42749      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
42750      */
42751     nanText : "{0} is not a valid number",
42752     /**
42753      * @cfg {Boolean} castInt (true|false) cast int if true (defalut true)
42754      */
42755     castInt : true,
42756     /**
42757      * @cfg {String} defaults currency of the MoneyField
42758      * value should be in lkey
42759      */
42760     defaultCurrency : false,
42761     /**
42762      * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
42763      */
42764     thousandsDelimiter : false,
42765     /**
42766      * @cfg {Number} max_length Maximum input field length allowed (defaults to Number.MAX_VALUE)
42767      */
42768     max_length: false,
42769     
42770     inputlg : 9,
42771     inputmd : 9,
42772     inputsm : 9,
42773     inputxs : 6,
42774     
42775     store : false,
42776     
42777     getAutoCreate : function()
42778     {
42779         var align = this.labelAlign || this.parentLabelAlign();
42780         
42781         var id = Roo.id();
42782
42783         var cfg = {
42784             cls: 'form-group',
42785             cn: []
42786         };
42787
42788         var input =  {
42789             tag: 'input',
42790             id : id,
42791             cls : 'form-control roo-money-amount-input',
42792             autocomplete: 'new-password'
42793         };
42794         
42795         var hiddenInput = {
42796             tag: 'input',
42797             type: 'hidden',
42798             id: Roo.id(),
42799             cls: 'hidden-number-input'
42800         };
42801         
42802         if(this.max_length) {
42803             input.maxlength = this.max_length; 
42804         }
42805         
42806         if (this.name) {
42807             hiddenInput.name = this.name;
42808         }
42809
42810         if (this.disabled) {
42811             input.disabled = true;
42812         }
42813
42814         var clg = 12 - this.inputlg;
42815         var cmd = 12 - this.inputmd;
42816         var csm = 12 - this.inputsm;
42817         var cxs = 12 - this.inputxs;
42818         
42819         var container = {
42820             tag : 'div',
42821             cls : 'row roo-money-field',
42822             cn : [
42823                 {
42824                     tag : 'div',
42825                     cls : 'roo-money-currency column col-lg-' + clg + ' col-md-' + cmd + ' col-sm-' + csm + ' col-xs-' + cxs,
42826                     cn : [
42827                         {
42828                             tag : 'div',
42829                             cls: 'roo-select2-container input-group',
42830                             cn: [
42831                                 {
42832                                     tag : 'input',
42833                                     cls : 'form-control roo-money-currency-input',
42834                                     autocomplete: 'new-password',
42835                                     readOnly : 1,
42836                                     name : this.currencyName
42837                                 },
42838                                 {
42839                                     tag :'span',
42840                                     cls : 'input-group-addon',
42841                                     cn : [
42842                                         {
42843                                             tag: 'span',
42844                                             cls: 'caret'
42845                                         }
42846                                     ]
42847                                 }
42848                             ]
42849                         }
42850                     ]
42851                 },
42852                 {
42853                     tag : 'div',
42854                     cls : 'roo-money-amount column col-lg-' + this.inputlg + ' col-md-' + this.inputmd + ' col-sm-' + this.inputsm + ' col-xs-' + this.inputxs,
42855                     cn : [
42856                         {
42857                             tag: 'div',
42858                             cls: this.hasFeedback ? 'has-feedback' : '',
42859                             cn: [
42860                                 input
42861                             ]
42862                         }
42863                     ]
42864                 }
42865             ]
42866             
42867         };
42868         
42869         if (this.fieldLabel.length) {
42870             var indicator = {
42871                 tag: 'i',
42872                 tooltip: 'This field is required'
42873             };
42874
42875             var label = {
42876                 tag: 'label',
42877                 'for':  id,
42878                 cls: 'control-label',
42879                 cn: []
42880             };
42881
42882             var label_text = {
42883                 tag: 'span',
42884                 html: this.fieldLabel
42885             };
42886
42887             indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
42888             label.cn = [
42889                 indicator,
42890                 label_text
42891             ];
42892
42893             if(this.indicatorpos == 'right') {
42894                 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
42895                 label.cn = [
42896                     label_text,
42897                     indicator
42898                 ];
42899             }
42900
42901             if(align == 'left') {
42902                 container = {
42903                     tag: 'div',
42904                     cn: [
42905                         container
42906                     ]
42907                 };
42908
42909                 if(this.labelWidth > 12){
42910                     label.style = "width: " + this.labelWidth + 'px';
42911                 }
42912                 if(this.labelWidth < 13 && this.labelmd == 0){
42913                     this.labelmd = this.labelWidth;
42914                 }
42915                 if(this.labellg > 0){
42916                     label.cls += ' col-lg-' + this.labellg;
42917                     input.cls += ' col-lg-' + (12 - this.labellg);
42918                 }
42919                 if(this.labelmd > 0){
42920                     label.cls += ' col-md-' + this.labelmd;
42921                     container.cls += ' col-md-' + (12 - this.labelmd);
42922                 }
42923                 if(this.labelsm > 0){
42924                     label.cls += ' col-sm-' + this.labelsm;
42925                     container.cls += ' col-sm-' + (12 - this.labelsm);
42926                 }
42927                 if(this.labelxs > 0){
42928                     label.cls += ' col-xs-' + this.labelxs;
42929                     container.cls += ' col-xs-' + (12 - this.labelxs);
42930                 }
42931             }
42932         }
42933
42934         cfg.cn = [
42935             label,
42936             container,
42937             hiddenInput
42938         ];
42939         
42940         var settings = this;
42941
42942         ['xs','sm','md','lg'].map(function(size){
42943             if (settings[size]) {
42944                 cfg.cls += ' col-' + size + '-' + settings[size];
42945             }
42946         });
42947         
42948         return cfg;
42949     },
42950     
42951     initEvents : function()
42952     {
42953         this.indicator = this.indicatorEl();
42954         
42955         this.initCurrencyEvent();
42956         
42957         this.initNumberEvent();
42958     },
42959     
42960     initCurrencyEvent : function()
42961     {
42962         if (!this.store) {
42963             throw "can not find store for combo";
42964         }
42965         
42966         this.store = Roo.factory(this.store, Roo.data);
42967         this.store.parent = this;
42968         
42969         this.createList();
42970         
42971         this.triggerEl = this.el.select('.input-group-addon', true).first();
42972         
42973         this.triggerEl.on("click", this.onTriggerClick, this, { preventDefault : true });
42974         
42975         var _this = this;
42976         
42977         (function(){
42978             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
42979             _this.list.setWidth(lw);
42980         }).defer(100);
42981         
42982         this.list.on('mouseover', this.onViewOver, this);
42983         this.list.on('mousemove', this.onViewMove, this);
42984         this.list.on('scroll', this.onViewScroll, this);
42985         
42986         if(!this.tpl){
42987             this.tpl = '<li><a href="#">{' + this.currencyField + '}</a></li>';
42988         }
42989         
42990         this.view = new Roo.View(this.list, this.tpl, {
42991             singleSelect:true, store: this.store, selectedClass: this.selectedClass
42992         });
42993         
42994         this.view.on('click', this.onViewClick, this);
42995         
42996         this.store.on('beforeload', this.onBeforeLoad, this);
42997         this.store.on('load', this.onLoad, this);
42998         this.store.on('loadexception', this.onLoadException, this);
42999         
43000         this.keyNav = new Roo.KeyNav(this.currencyEl(), {
43001             "up" : function(e){
43002                 this.inKeyMode = true;
43003                 this.selectPrev();
43004             },
43005
43006             "down" : function(e){
43007                 if(!this.isExpanded()){
43008                     this.onTriggerClick();
43009                 }else{
43010                     this.inKeyMode = true;
43011                     this.selectNext();
43012                 }
43013             },
43014
43015             "enter" : function(e){
43016                 this.collapse();
43017                 
43018                 if(this.fireEvent("specialkey", this, e)){
43019                     this.onViewClick(false);
43020                 }
43021                 
43022                 return true;
43023             },
43024
43025             "esc" : function(e){
43026                 this.collapse();
43027             },
43028
43029             "tab" : function(e){
43030                 this.collapse();
43031                 
43032                 if(this.fireEvent("specialkey", this, e)){
43033                     this.onViewClick(false);
43034                 }
43035                 
43036                 return true;
43037             },
43038
43039             scope : this,
43040
43041             doRelay : function(foo, bar, hname){
43042                 if(hname == 'down' || this.scope.isExpanded()){
43043                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
43044                 }
43045                 return true;
43046             },
43047
43048             forceKeyDown: true
43049         });
43050         
43051         this.currencyEl().on("click", this.onTriggerClick, this, { preventDefault : true });
43052         
43053     },
43054     
43055     initNumberEvent : function(e)
43056     {
43057         this.inputEl().on("keydown" , this.fireKey,  this);
43058         this.inputEl().on("focus", this.onFocus,  this);
43059         this.inputEl().on("blur", this.onBlur,  this);
43060         
43061         this.inputEl().relayEvent('keyup', this);
43062         
43063         if(this.indicator){
43064             this.indicator.addClass('invisible');
43065         }
43066  
43067         this.originalValue = this.getValue();
43068         
43069         if(this.validationEvent == 'keyup'){
43070             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
43071             this.inputEl().on('keyup', this.filterValidation, this);
43072         }
43073         else if(this.validationEvent !== false){
43074             this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
43075         }
43076         
43077         if(this.selectOnFocus){
43078             this.on("focus", this.preFocus, this);
43079             
43080         }
43081         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
43082             this.inputEl().on("keypress", this.filterKeys, this);
43083         } else {
43084             this.inputEl().relayEvent('keypress', this);
43085         }
43086         
43087         var allowed = "0123456789";
43088         
43089         if(this.allowDecimals){
43090             allowed += this.decimalSeparator;
43091         }
43092         
43093         if(this.allowNegative){
43094             allowed += "-";
43095         }
43096         
43097         if(this.thousandsDelimiter) {
43098             allowed += ",";
43099         }
43100         
43101         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
43102         
43103         var keyPress = function(e){
43104             
43105             var k = e.getKey();
43106             
43107             var c = e.getCharCode();
43108             
43109             if(
43110                     (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
43111                     allowed.indexOf(String.fromCharCode(c)) === -1
43112             ){
43113                 e.stopEvent();
43114                 return;
43115             }
43116             
43117             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
43118                 return;
43119             }
43120             
43121             if(allowed.indexOf(String.fromCharCode(c)) === -1){
43122                 e.stopEvent();
43123             }
43124         };
43125         
43126         this.inputEl().on("keypress", keyPress, this);
43127         
43128     },
43129     
43130     onTriggerClick : function(e)
43131     {   
43132         if(this.disabled){
43133             return;
43134         }
43135         
43136         this.page = 0;
43137         this.loadNext = false;
43138         
43139         if(this.isExpanded()){
43140             this.collapse();
43141             return;
43142         }
43143         
43144         this.hasFocus = true;
43145         
43146         if(this.triggerAction == 'all') {
43147             this.doQuery(this.allQuery, true);
43148             return;
43149         }
43150         
43151         this.doQuery(this.getRawValue());
43152     },
43153     
43154     getCurrency : function()
43155     {   
43156         var v = this.currencyEl().getValue();
43157         
43158         return v;
43159     },
43160     
43161     restrictHeight : function()
43162     {
43163         this.list.alignTo(this.currencyEl(), this.listAlign);
43164         this.list.alignTo(this.currencyEl(), this.listAlign);
43165     },
43166     
43167     onViewClick : function(view, doFocus, el, e)
43168     {
43169         var index = this.view.getSelectedIndexes()[0];
43170         
43171         var r = this.store.getAt(index);
43172         
43173         if(r){
43174             this.onSelect(r, index);
43175         }
43176     },
43177     
43178     onSelect : function(record, index){
43179         
43180         if(this.fireEvent('beforeselect', this, record, index) !== false){
43181         
43182             this.setFromCurrencyData(index > -1 ? record.data : false);
43183             
43184             this.collapse();
43185             
43186             this.fireEvent('select', this, record, index);
43187         }
43188     },
43189     
43190     setFromCurrencyData : function(o)
43191     {
43192         var currency = '';
43193         
43194         this.lastCurrency = o;
43195         
43196         if (this.currencyField) {
43197             currency = !o || typeof(o[this.currencyField]) == 'undefined' ? '' : o[this.currencyField];
43198         } else {
43199             Roo.log('no  currencyField value set for '+ (this.name ? this.name : this.id));
43200         }
43201         
43202         this.lastSelectionText = currency;
43203         
43204         //setting default currency
43205         if(o[this.currencyField] * 1 == 0 && this.defaultCurrency) {
43206             this.setCurrency(this.defaultCurrency);
43207             return;
43208         }
43209         
43210         this.setCurrency(currency);
43211     },
43212     
43213     setFromData : function(o)
43214     {
43215         var c = {};
43216         
43217         c[this.currencyField] = !o || typeof(o[this.currencyName]) == 'undefined' ? '' : o[this.currencyName];
43218         
43219         this.setFromCurrencyData(c);
43220         
43221         var value = '';
43222         
43223         if (this.name) {
43224             value = !o || typeof(o[this.name]) == 'undefined' ? '' : o[this.name];
43225         } else {
43226             Roo.log('no value set for '+ (this.name ? this.name : this.id));
43227         }
43228         
43229         this.setValue(value);
43230         
43231     },
43232     
43233     setCurrency : function(v)
43234     {   
43235         this.currencyValue = v;
43236         
43237         if(this.rendered){
43238             this.currencyEl().dom.value = (v === null || v === undefined ? '' : v);
43239             this.validate();
43240         }
43241     },
43242     
43243     setValue : function(v)
43244     {
43245         v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
43246         
43247         this.value = v;
43248         
43249         if(this.rendered){
43250             
43251             this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
43252             
43253             this.inputEl().dom.value = (v == '') ? '' :
43254                 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
43255             
43256             if(!this.allowZero && v === '0') {
43257                 this.hiddenEl().dom.value = '';
43258                 this.inputEl().dom.value = '';
43259             }
43260             
43261             this.validate();
43262         }
43263     },
43264     
43265     getRawValue : function()
43266     {
43267         var v = this.inputEl().getValue();
43268         
43269         return v;
43270     },
43271     
43272     getValue : function()
43273     {
43274         return this.fixPrecision(this.parseValue(this.getRawValue()));
43275     },
43276     
43277     parseValue : function(value)
43278     {
43279         if(this.thousandsDelimiter) {
43280             value += "";
43281             r = new RegExp(",", "g");
43282             value = value.replace(r, "");
43283         }
43284         
43285         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
43286         return isNaN(value) ? '' : value;
43287         
43288     },
43289     
43290     fixPrecision : function(value)
43291     {
43292         if(this.thousandsDelimiter) {
43293             value += "";
43294             r = new RegExp(",", "g");
43295             value = value.replace(r, "");
43296         }
43297         
43298         var nan = isNaN(value);
43299         
43300         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
43301             return nan ? '' : value;
43302         }
43303         return parseFloat(value).toFixed(this.decimalPrecision);
43304     },
43305     
43306     decimalPrecisionFcn : function(v)
43307     {
43308         return Math.floor(v);
43309     },
43310     
43311     validateValue : function(value)
43312     {
43313         if(!Roo.bootstrap.MoneyField.superclass.validateValue.call(this, value)){
43314             return false;
43315         }
43316         
43317         var num = this.parseValue(value);
43318         
43319         if(isNaN(num)){
43320             this.markInvalid(String.format(this.nanText, value));
43321             return false;
43322         }
43323         
43324         if(num < this.minValue){
43325             this.markInvalid(String.format(this.minText, this.minValue));
43326             return false;
43327         }
43328         
43329         if(num > this.maxValue){
43330             this.markInvalid(String.format(this.maxText, this.maxValue));
43331             return false;
43332         }
43333         
43334         return true;
43335     },
43336     
43337     validate : function()
43338     {
43339         if(this.disabled || this.allowBlank){
43340             this.markValid();
43341             return true;
43342         }
43343         
43344         var currency = this.getCurrency();
43345         
43346         if(this.validateValue(this.getRawValue()) && currency.length){
43347             this.markValid();
43348             return true;
43349         }
43350         
43351         this.markInvalid();
43352         return false;
43353     },
43354     
43355     getName: function()
43356     {
43357         return this.name;
43358     },
43359     
43360     beforeBlur : function()
43361     {
43362         if(!this.castInt){
43363             return;
43364         }
43365         
43366         var v = this.parseValue(this.getRawValue());
43367         
43368         if(v || v == 0){
43369             this.setValue(v);
43370         }
43371     },
43372     
43373     onBlur : function()
43374     {
43375         this.beforeBlur();
43376         
43377         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
43378             //this.el.removeClass(this.focusClass);
43379         }
43380         
43381         this.hasFocus = false;
43382         
43383         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
43384             this.validate();
43385         }
43386         
43387         var v = this.getValue();
43388         
43389         if(String(v) !== String(this.startValue)){
43390             this.fireEvent('change', this, v, this.startValue);
43391         }
43392         
43393         this.fireEvent("blur", this);
43394     },
43395     
43396     inputEl : function()
43397     {
43398         return this.el.select('.roo-money-amount-input', true).first();
43399     },
43400     
43401     currencyEl : function()
43402     {
43403         return this.el.select('.roo-money-currency-input', true).first();
43404     },
43405     
43406     hiddenEl : function()
43407     {
43408         return this.el.select('input.hidden-number-input',true).first();
43409     }
43410     
43411 });/**
43412  * @class Roo.bootstrap.BezierSignature
43413  * @extends Roo.bootstrap.Component
43414  * Bootstrap BezierSignature class
43415  * This script refer to:
43416  *    Title: Signature Pad
43417  *    Author: szimek
43418  *    Availability: https://github.com/szimek/signature_pad
43419  *
43420  * @constructor
43421  * Create a new BezierSignature
43422  * @param {Object} config The config object
43423  */
43424
43425 Roo.bootstrap.BezierSignature = function(config){
43426     Roo.bootstrap.BezierSignature.superclass.constructor.call(this, config);
43427     this.addEvents({
43428         "resize" : true
43429     });
43430 };
43431
43432 Roo.extend(Roo.bootstrap.BezierSignature, Roo.bootstrap.Component,
43433 {
43434      
43435     curve_data: [],
43436     
43437     is_empty: true,
43438     
43439     mouse_btn_down: true,
43440     
43441     /**
43442      * @cfg {int} canvas height
43443      */
43444     canvas_height: '200px',
43445     
43446     /**
43447      * @cfg {float|function} Radius of a single dot.
43448      */ 
43449     dot_size: false,
43450     
43451     /**
43452      * @cfg {float} Minimum width of a line. Defaults to 0.5.
43453      */
43454     min_width: 0.5,
43455     
43456     /**
43457      * @cfg {float} Maximum width of a line. Defaults to 2.5.
43458      */
43459     max_width: 2.5,
43460     
43461     /**
43462      * @cfg {integer} Draw the next point at most once per every x milliseconds. Set it to 0 to turn off throttling. Defaults to 16.
43463      */
43464     throttle: 16,
43465     
43466     /**
43467      * @cfg {integer} Add the next point only if the previous one is farther than x pixels. Defaults to 5.
43468      */
43469     min_distance: 5,
43470     
43471     /**
43472      * @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.
43473      */
43474     bg_color: 'rgba(0, 0, 0, 0)',
43475     
43476     /**
43477      * @cfg {string} Color used to draw the lines. Can be any color format accepted by context.fillStyle. Defaults to "black".
43478      */
43479     dot_color: 'black',
43480     
43481     /**
43482      * @cfg {float} Weight used to modify new velocity based on the previous velocity. Defaults to 0.7.
43483      */ 
43484     velocity_filter_weight: 0.7,
43485     
43486     /**
43487      * @cfg {function} Callback when stroke begin. 
43488      */
43489     onBegin: false,
43490     
43491     /**
43492      * @cfg {function} Callback when stroke end.
43493      */
43494     onEnd: false,
43495     
43496     getAutoCreate : function()
43497     {
43498         var cls = 'roo-signature column';
43499         
43500         if(this.cls){
43501             cls += ' ' + this.cls;
43502         }
43503         
43504         var col_sizes = [
43505             'lg',
43506             'md',
43507             'sm',
43508             'xs'
43509         ];
43510         
43511         for(var i = 0; i < col_sizes.length; i++) {
43512             if(this[col_sizes[i]]) {
43513                 cls += " col-"+col_sizes[i]+"-"+this[col_sizes[i]];
43514             }
43515         }
43516         
43517         var cfg = {
43518             tag: 'div',
43519             cls: cls,
43520             cn: [
43521                 {
43522                     tag: 'div',
43523                     cls: 'roo-signature-body',
43524                     cn: [
43525                         {
43526                             tag: 'canvas',
43527                             cls: 'roo-signature-body-canvas',
43528                             height: this.canvas_height,
43529                             width: this.canvas_width
43530                         }
43531                     ]
43532                 },
43533                 {
43534                     tag: 'input',
43535                     type: 'file',
43536                     style: 'display: none'
43537                 }
43538             ]
43539         };
43540         
43541         return cfg;
43542     },
43543     
43544     initEvents: function() 
43545     {
43546         Roo.bootstrap.BezierSignature.superclass.initEvents.call(this);
43547         
43548         var canvas = this.canvasEl();
43549         
43550         // mouse && touch event swapping...
43551         canvas.dom.style.touchAction = 'none';
43552         canvas.dom.style.msTouchAction = 'none';
43553         
43554         this.mouse_btn_down = false;
43555         canvas.on('mousedown', this._handleMouseDown, this);
43556         canvas.on('mousemove', this._handleMouseMove, this);
43557         Roo.select('html').first().on('mouseup', this._handleMouseUp, this);
43558         
43559         if (window.PointerEvent) {
43560             canvas.on('pointerdown', this._handleMouseDown, this);
43561             canvas.on('pointermove', this._handleMouseMove, this);
43562             Roo.select('html').first().on('pointerup', this._handleMouseUp, this);
43563         }
43564         
43565         if ('ontouchstart' in window) {
43566             canvas.on('touchstart', this._handleTouchStart, this);
43567             canvas.on('touchmove', this._handleTouchMove, this);
43568             canvas.on('touchend', this._handleTouchEnd, this);
43569         }
43570         
43571         Roo.EventManager.onWindowResize(this.resize, this, true);
43572         
43573         // file input event
43574         this.fileEl().on('change', this.uploadImage, this);
43575         
43576         this.clear();
43577         
43578         this.resize();
43579     },
43580     
43581     resize: function(){
43582         
43583         var canvas = this.canvasEl().dom;
43584         var ctx = this.canvasElCtx();
43585         var img_data = false;
43586         
43587         if(canvas.width > 0) {
43588             var img_data = ctx.getImageData(0, 0, canvas.width, canvas.height);
43589         }
43590         // setting canvas width will clean img data
43591         canvas.width = 0;
43592         
43593         var style = window.getComputedStyle ? 
43594             getComputedStyle(this.el.dom, null) : this.el.dom.currentStyle;
43595             
43596         var padding_left = parseInt(style.paddingLeft) || 0;
43597         var padding_right = parseInt(style.paddingRight) || 0;
43598         
43599         canvas.width = this.el.dom.clientWidth - padding_left - padding_right;
43600         
43601         if(img_data) {
43602             ctx.putImageData(img_data, 0, 0);
43603         }
43604     },
43605     
43606     _handleMouseDown: function(e)
43607     {
43608         if (e.browserEvent.which === 1) {
43609             this.mouse_btn_down = true;
43610             this.strokeBegin(e);
43611         }
43612     },
43613     
43614     _handleMouseMove: function (e)
43615     {
43616         if (this.mouse_btn_down) {
43617             this.strokeMoveUpdate(e);
43618         }
43619     },
43620     
43621     _handleMouseUp: function (e)
43622     {
43623         if (e.browserEvent.which === 1 && this.mouse_btn_down) {
43624             this.mouse_btn_down = false;
43625             this.strokeEnd(e);
43626         }
43627     },
43628     
43629     _handleTouchStart: function (e) {
43630         
43631         e.preventDefault();
43632         if (e.browserEvent.targetTouches.length === 1) {
43633             // var touch = e.browserEvent.changedTouches[0];
43634             // this.strokeBegin(touch);
43635             
43636              this.strokeBegin(e); // assume e catching the correct xy...
43637         }
43638     },
43639     
43640     _handleTouchMove: function (e) {
43641         e.preventDefault();
43642         // var touch = event.targetTouches[0];
43643         // _this._strokeMoveUpdate(touch);
43644         this.strokeMoveUpdate(e);
43645     },
43646     
43647     _handleTouchEnd: function (e) {
43648         var wasCanvasTouched = e.target === this.canvasEl().dom;
43649         if (wasCanvasTouched) {
43650             e.preventDefault();
43651             // var touch = event.changedTouches[0];
43652             // _this._strokeEnd(touch);
43653             this.strokeEnd(e);
43654         }
43655     },
43656     
43657     reset: function () {
43658         this._lastPoints = [];
43659         this._lastVelocity = 0;
43660         this._lastWidth = (this.min_width + this.max_width) / 2;
43661         this.canvasElCtx().fillStyle = this.dot_color;
43662     },
43663     
43664     strokeMoveUpdate: function(e)
43665     {
43666         this.strokeUpdate(e);
43667         
43668         if (this.throttle) {
43669             this.throttleStroke(this.strokeUpdate, this.throttle);
43670         }
43671         else {
43672             this.strokeUpdate(e);
43673         }
43674     },
43675     
43676     strokeBegin: function(e)
43677     {
43678         var newPointGroup = {
43679             color: this.dot_color,
43680             points: []
43681         };
43682         
43683         if (typeof this.onBegin === 'function') {
43684             this.onBegin(e);
43685         }
43686         
43687         this.curve_data.push(newPointGroup);
43688         this.reset();
43689         this.strokeUpdate(e);
43690     },
43691     
43692     strokeUpdate: function(e)
43693     {
43694         var rect = this.canvasEl().dom.getBoundingClientRect();
43695         var point = new this.Point(e.xy[0] - rect.left, e.xy[1] - rect.top, new Date().getTime());
43696         var lastPointGroup = this.curve_data[this.curve_data.length - 1];
43697         var lastPoints = lastPointGroup.points;
43698         var lastPoint = lastPoints.length > 0 && lastPoints[lastPoints.length - 1];
43699         var isLastPointTooClose = lastPoint
43700             ? point.distanceTo(lastPoint) <= this.min_distance
43701             : false;
43702         var color = lastPointGroup.color;
43703         if (!lastPoint || !(lastPoint && isLastPointTooClose)) {
43704             var curve = this.addPoint(point);
43705             if (!lastPoint) {
43706                 this.drawDot({color: color, point: point});
43707             }
43708             else if (curve) {
43709                 this.drawCurve({color: color, curve: curve});
43710             }
43711             lastPoints.push({
43712                 time: point.time,
43713                 x: point.x,
43714                 y: point.y
43715             });
43716         }
43717     },
43718     
43719     strokeEnd: function(e)
43720     {
43721         this.strokeUpdate(e);
43722         if (typeof this.onEnd === 'function') {
43723             this.onEnd(e);
43724         }
43725     },
43726     
43727     addPoint:  function (point) {
43728         var _lastPoints = this._lastPoints;
43729         _lastPoints.push(point);
43730         if (_lastPoints.length > 2) {
43731             if (_lastPoints.length === 3) {
43732                 _lastPoints.unshift(_lastPoints[0]);
43733             }
43734             var widths = this.calculateCurveWidths(_lastPoints[1], _lastPoints[2]);
43735             var curve = this.Bezier.fromPoints(_lastPoints, widths, this);
43736             _lastPoints.shift();
43737             return curve;
43738         }
43739         return null;
43740     },
43741     
43742     calculateCurveWidths: function (startPoint, endPoint) {
43743         var velocity = this.velocity_filter_weight * endPoint.velocityFrom(startPoint) +
43744             (1 - this.velocity_filter_weight) * this._lastVelocity;
43745
43746         var newWidth = Math.max(this.max_width / (velocity + 1), this.min_width);
43747         var widths = {
43748             end: newWidth,
43749             start: this._lastWidth
43750         };
43751         
43752         this._lastVelocity = velocity;
43753         this._lastWidth = newWidth;
43754         return widths;
43755     },
43756     
43757     drawDot: function (_a) {
43758         var color = _a.color, point = _a.point;
43759         var ctx = this.canvasElCtx();
43760         var width = typeof this.dot_size === 'function' ? this.dot_size() : this.dot_size;
43761         ctx.beginPath();
43762         this.drawCurveSegment(point.x, point.y, width);
43763         ctx.closePath();
43764         ctx.fillStyle = color;
43765         ctx.fill();
43766     },
43767     
43768     drawCurve: function (_a) {
43769         var color = _a.color, curve = _a.curve;
43770         var ctx = this.canvasElCtx();
43771         var widthDelta = curve.endWidth - curve.startWidth;
43772         var drawSteps = Math.floor(curve.length()) * 2;
43773         ctx.beginPath();
43774         ctx.fillStyle = color;
43775         for (var i = 0; i < drawSteps; i += 1) {
43776         var t = i / drawSteps;
43777         var tt = t * t;
43778         var ttt = tt * t;
43779         var u = 1 - t;
43780         var uu = u * u;
43781         var uuu = uu * u;
43782         var x = uuu * curve.startPoint.x;
43783         x += 3 * uu * t * curve.control1.x;
43784         x += 3 * u * tt * curve.control2.x;
43785         x += ttt * curve.endPoint.x;
43786         var y = uuu * curve.startPoint.y;
43787         y += 3 * uu * t * curve.control1.y;
43788         y += 3 * u * tt * curve.control2.y;
43789         y += ttt * curve.endPoint.y;
43790         var width = curve.startWidth + ttt * widthDelta;
43791         this.drawCurveSegment(x, y, width);
43792         }
43793         ctx.closePath();
43794         ctx.fill();
43795     },
43796     
43797     drawCurveSegment: function (x, y, width) {
43798         var ctx = this.canvasElCtx();
43799         ctx.moveTo(x, y);
43800         ctx.arc(x, y, width, 0, 2 * Math.PI, false);
43801         this.is_empty = false;
43802     },
43803     
43804     clear: function()
43805     {
43806         var ctx = this.canvasElCtx();
43807         var canvas = this.canvasEl().dom;
43808         ctx.fillStyle = this.bg_color;
43809         ctx.clearRect(0, 0, canvas.width, canvas.height);
43810         ctx.fillRect(0, 0, canvas.width, canvas.height);
43811         this.curve_data = [];
43812         this.reset();
43813         this.is_empty = true;
43814     },
43815     
43816     fileEl: function()
43817     {
43818         return  this.el.select('input',true).first();
43819     },
43820     
43821     canvasEl: function()
43822     {
43823         return this.el.select('canvas',true).first();
43824     },
43825     
43826     canvasElCtx: function()
43827     {
43828         return this.el.select('canvas',true).first().dom.getContext('2d');
43829     },
43830     
43831     getImage: function(type)
43832     {
43833         if(this.is_empty) {
43834             return false;
43835         }
43836         
43837         // encryption ?
43838         return this.canvasEl().dom.toDataURL('image/'+type, 1);
43839     },
43840     
43841     drawFromImage: function(img_src)
43842     {
43843         var img = new Image();
43844         
43845         img.onload = function(){
43846             this.canvasElCtx().drawImage(img, 0, 0);
43847         }.bind(this);
43848         
43849         img.src = img_src;
43850         
43851         this.is_empty = false;
43852     },
43853     
43854     selectImage: function()
43855     {
43856         this.fileEl().dom.click();
43857     },
43858     
43859     uploadImage: function(e)
43860     {
43861         var reader = new FileReader();
43862         
43863         reader.onload = function(e){
43864             var img = new Image();
43865             img.onload = function(){
43866                 this.reset();
43867                 this.canvasElCtx().drawImage(img, 0, 0);
43868             }.bind(this);
43869             img.src = e.target.result;
43870         }.bind(this);
43871         
43872         reader.readAsDataURL(e.target.files[0]);
43873     },
43874     
43875     // Bezier Point Constructor
43876     Point: (function () {
43877         function Point(x, y, time) {
43878             this.x = x;
43879             this.y = y;
43880             this.time = time || Date.now();
43881         }
43882         Point.prototype.distanceTo = function (start) {
43883             return Math.sqrt(Math.pow(this.x - start.x, 2) + Math.pow(this.y - start.y, 2));
43884         };
43885         Point.prototype.equals = function (other) {
43886             return this.x === other.x && this.y === other.y && this.time === other.time;
43887         };
43888         Point.prototype.velocityFrom = function (start) {
43889             return this.time !== start.time
43890             ? this.distanceTo(start) / (this.time - start.time)
43891             : 0;
43892         };
43893         return Point;
43894     }()),
43895     
43896     
43897     // Bezier Constructor
43898     Bezier: (function () {
43899         function Bezier(startPoint, control2, control1, endPoint, startWidth, endWidth) {
43900             this.startPoint = startPoint;
43901             this.control2 = control2;
43902             this.control1 = control1;
43903             this.endPoint = endPoint;
43904             this.startWidth = startWidth;
43905             this.endWidth = endWidth;
43906         }
43907         Bezier.fromPoints = function (points, widths, scope) {
43908             var c2 = this.calculateControlPoints(points[0], points[1], points[2], scope).c2;
43909             var c3 = this.calculateControlPoints(points[1], points[2], points[3], scope).c1;
43910             return new Bezier(points[1], c2, c3, points[2], widths.start, widths.end);
43911         };
43912         Bezier.calculateControlPoints = function (s1, s2, s3, scope) {
43913             var dx1 = s1.x - s2.x;
43914             var dy1 = s1.y - s2.y;
43915             var dx2 = s2.x - s3.x;
43916             var dy2 = s2.y - s3.y;
43917             var m1 = { x: (s1.x + s2.x) / 2.0, y: (s1.y + s2.y) / 2.0 };
43918             var m2 = { x: (s2.x + s3.x) / 2.0, y: (s2.y + s3.y) / 2.0 };
43919             var l1 = Math.sqrt(dx1 * dx1 + dy1 * dy1);
43920             var l2 = Math.sqrt(dx2 * dx2 + dy2 * dy2);
43921             var dxm = m1.x - m2.x;
43922             var dym = m1.y - m2.y;
43923             var k = l2 / (l1 + l2);
43924             var cm = { x: m2.x + dxm * k, y: m2.y + dym * k };
43925             var tx = s2.x - cm.x;
43926             var ty = s2.y - cm.y;
43927             return {
43928                 c1: new scope.Point(m1.x + tx, m1.y + ty),
43929                 c2: new scope.Point(m2.x + tx, m2.y + ty)
43930             };
43931         };
43932         Bezier.prototype.length = function () {
43933             var steps = 10;
43934             var length = 0;
43935             var px;
43936             var py;
43937             for (var i = 0; i <= steps; i += 1) {
43938                 var t = i / steps;
43939                 var cx = this.point(t, this.startPoint.x, this.control1.x, this.control2.x, this.endPoint.x);
43940                 var cy = this.point(t, this.startPoint.y, this.control1.y, this.control2.y, this.endPoint.y);
43941                 if (i > 0) {
43942                     var xdiff = cx - px;
43943                     var ydiff = cy - py;
43944                     length += Math.sqrt(xdiff * xdiff + ydiff * ydiff);
43945                 }
43946                 px = cx;
43947                 py = cy;
43948             }
43949             return length;
43950         };
43951         Bezier.prototype.point = function (t, start, c1, c2, end) {
43952             return (start * (1.0 - t) * (1.0 - t) * (1.0 - t))
43953             + (3.0 * c1 * (1.0 - t) * (1.0 - t) * t)
43954             + (3.0 * c2 * (1.0 - t) * t * t)
43955             + (end * t * t * t);
43956         };
43957         return Bezier;
43958     }()),
43959     
43960     throttleStroke: function(fn, wait) {
43961       if (wait === void 0) { wait = 250; }
43962       var previous = 0;
43963       var timeout = null;
43964       var result;
43965       var storedContext;
43966       var storedArgs;
43967       var later = function () {
43968           previous = Date.now();
43969           timeout = null;
43970           result = fn.apply(storedContext, storedArgs);
43971           if (!timeout) {
43972               storedContext = null;
43973               storedArgs = [];
43974           }
43975       };
43976       return function wrapper() {
43977           var args = [];
43978           for (var _i = 0; _i < arguments.length; _i++) {
43979               args[_i] = arguments[_i];
43980           }
43981           var now = Date.now();
43982           var remaining = wait - (now - previous);
43983           storedContext = this;
43984           storedArgs = args;
43985           if (remaining <= 0 || remaining > wait) {
43986               if (timeout) {
43987                   clearTimeout(timeout);
43988                   timeout = null;
43989               }
43990               previous = now;
43991               result = fn.apply(storedContext, storedArgs);
43992               if (!timeout) {
43993                   storedContext = null;
43994                   storedArgs = [];
43995               }
43996           }
43997           else if (!timeout) {
43998               timeout = window.setTimeout(later, remaining);
43999           }
44000           return result;
44001       };
44002   }
44003   
44004 });
44005
44006  
44007
44008