css-bootstrap4/bootstrap.css
[roojs1] / roojs-bootstrap-debug.js
1 /**
2  * set the version of bootstrap based on the stylesheet...
3  *
4  */
5
6 Roo.bootstrap.version = ( function() {
7     var ret=3;
8     Roo.each(document.styleSheets, function(s) {
9         if ( s.href  && s.href.match(/css-bootstrap4/)) {
10             ret=4;
11         }
12     });
13     if (ret > 3) {
14          Roo.Element.prototype.visibilityMode = Roo.Element.DISPLAY;
15     }
16     return ret;
17 })(); /*
18  * Based on:
19  * Ext JS Library 1.1.1
20  * Copyright(c) 2006-2007, Ext JS, LLC.
21  *
22  * Originally Released Under LGPL - original licence link has changed is not relivant.
23  *
24  * Fork - LGPL
25  * <script type="text/javascript">
26  */
27
28
29 /**
30  * @class Roo.Shadow
31  * Simple class that can provide a shadow effect for any element.  Note that the element MUST be absolutely positioned,
32  * and the shadow does not provide any shimming.  This should be used only in simple cases -- for more advanced
33  * functionality that can also provide the same shadow effect, see the {@link Roo.Layer} class.
34  * @constructor
35  * Create a new Shadow
36  * @param {Object} config The config object
37  */
38 Roo.Shadow = function(config){
39     Roo.apply(this, config);
40     if(typeof this.mode != "string"){
41         this.mode = this.defaultMode;
42     }
43     var o = this.offset, a = {h: 0};
44     var rad = Math.floor(this.offset/2);
45     switch(this.mode.toLowerCase()){ // all this hideous nonsense calculates the various offsets for shadows
46         case "drop":
47             a.w = 0;
48             a.l = a.t = o;
49             a.t -= 1;
50             if(Roo.isIE){
51                 a.l -= this.offset + rad;
52                 a.t -= this.offset + rad;
53                 a.w -= rad;
54                 a.h -= rad;
55                 a.t += 1;
56             }
57         break;
58         case "sides":
59             a.w = (o*2);
60             a.l = -o;
61             a.t = o-1;
62             if(Roo.isIE){
63                 a.l -= (this.offset - rad);
64                 a.t -= this.offset + rad;
65                 a.l += 1;
66                 a.w -= (this.offset - rad)*2;
67                 a.w -= rad + 1;
68                 a.h -= 1;
69             }
70         break;
71         case "frame":
72             a.w = a.h = (o*2);
73             a.l = a.t = -o;
74             a.t += 1;
75             a.h -= 2;
76             if(Roo.isIE){
77                 a.l -= (this.offset - rad);
78                 a.t -= (this.offset - rad);
79                 a.l += 1;
80                 a.w -= (this.offset + rad + 1);
81                 a.h -= (this.offset + rad);
82                 a.h += 1;
83             }
84         break;
85     };
86
87     this.adjusts = a;
88 };
89
90 Roo.Shadow.prototype = {
91     /**
92      * @cfg {String} mode
93      * The shadow display mode.  Supports the following options:<br />
94      * sides: Shadow displays on both sides and bottom only<br />
95      * frame: Shadow displays equally on all four sides<br />
96      * drop: Traditional bottom-right drop shadow (default)
97      */
98     /**
99      * @cfg {String} offset
100      * The number of pixels to offset the shadow from the element (defaults to 4)
101      */
102     offset: 4,
103
104     // private
105     defaultMode: "drop",
106
107     /**
108      * Displays the shadow under the target element
109      * @param {String/HTMLElement/Element} targetEl The id or element under which the shadow should display
110      */
111     show : function(target){
112         target = Roo.get(target);
113         if(!this.el){
114             this.el = Roo.Shadow.Pool.pull();
115             if(this.el.dom.nextSibling != target.dom){
116                 this.el.insertBefore(target);
117             }
118         }
119         this.el.setStyle("z-index", this.zIndex || parseInt(target.getStyle("z-index"), 10)-1);
120         if(Roo.isIE){
121             this.el.dom.style.filter="progid:DXImageTransform.Microsoft.alpha(opacity=50) progid:DXImageTransform.Microsoft.Blur(pixelradius="+(this.offset)+")";
122         }
123         this.realign(
124             target.getLeft(true),
125             target.getTop(true),
126             target.getWidth(),
127             target.getHeight()
128         );
129         this.el.dom.style.display = "block";
130     },
131
132     /**
133      * Returns true if the shadow is visible, else false
134      */
135     isVisible : function(){
136         return this.el ? true : false;  
137     },
138
139     /**
140      * Direct alignment when values are already available. Show must be called at least once before
141      * calling this method to ensure it is initialized.
142      * @param {Number} left The target element left position
143      * @param {Number} top The target element top position
144      * @param {Number} width The target element width
145      * @param {Number} height The target element height
146      */
147     realign : function(l, t, w, h){
148         if(!this.el){
149             return;
150         }
151         var a = this.adjusts, d = this.el.dom, s = d.style;
152         var iea = 0;
153         s.left = (l+a.l)+"px";
154         s.top = (t+a.t)+"px";
155         var sw = (w+a.w), sh = (h+a.h), sws = sw +"px", shs = sh + "px";
156  
157         if(s.width != sws || s.height != shs){
158             s.width = sws;
159             s.height = shs;
160             if(!Roo.isIE){
161                 var cn = d.childNodes;
162                 var sww = Math.max(0, (sw-12))+"px";
163                 cn[0].childNodes[1].style.width = sww;
164                 cn[1].childNodes[1].style.width = sww;
165                 cn[2].childNodes[1].style.width = sww;
166                 cn[1].style.height = Math.max(0, (sh-12))+"px";
167             }
168         }
169     },
170
171     /**
172      * Hides this shadow
173      */
174     hide : function(){
175         if(this.el){
176             this.el.dom.style.display = "none";
177             Roo.Shadow.Pool.push(this.el);
178             delete this.el;
179         }
180     },
181
182     /**
183      * Adjust the z-index of this shadow
184      * @param {Number} zindex The new z-index
185      */
186     setZIndex : function(z){
187         this.zIndex = z;
188         if(this.el){
189             this.el.setStyle("z-index", z);
190         }
191     }
192 };
193
194 // Private utility class that manages the internal Shadow cache
195 Roo.Shadow.Pool = function(){
196     var p = [];
197     var markup = Roo.isIE ?
198                  '<div class="x-ie-shadow"></div>' :
199                  '<div class="x-shadow"><div class="xst"><div class="xstl"></div><div class="xstc"></div><div class="xstr"></div></div><div class="xsc"><div class="xsml"></div><div class="xsmc"></div><div class="xsmr"></div></div><div class="xsb"><div class="xsbl"></div><div class="xsbc"></div><div class="xsbr"></div></div></div>';
200     return {
201         pull : function(){
202             var sh = p.shift();
203             if(!sh){
204                 sh = Roo.get(Roo.DomHelper.insertHtml("beforeBegin", document.body.firstChild, markup));
205                 sh.autoBoxAdjust = false;
206             }
207             return sh;
208         },
209
210         push : function(sh){
211             p.push(sh);
212         }
213     };
214 }();/*
215  * - LGPL
216  *
217  * base class for bootstrap elements.
218  * 
219  */
220
221 Roo.bootstrap = Roo.bootstrap || {};
222 /**
223  * @class Roo.bootstrap.Component
224  * @extends Roo.Component
225  * Bootstrap Component base class
226  * @cfg {String} cls css class
227  * @cfg {String} style any extra css
228  * @cfg {Object} xattr extra attributes to add to 'element' (used by builder to store stuff.)
229  * @cfg {Boolean} can_build_overlaid  True if element can be rebuild from a HTML page
230  * @cfg {string} dataId cutomer id
231  * @cfg {string} name Specifies name attribute
232  * @cfg {string} tooltip  Text for the tooltip
233  * @cfg {string} container_method method to fetch parents container element (used by NavHeaderbar -  getHeaderChildContainer)
234  * @cfg {string|object} visibilityEl (el|parent) What element to use for visibility (@see getVisibilityEl())
235  
236  * @constructor
237  * Do not use directly - it does not do anything..
238  * @param {Object} config The config object
239  */
240
241
242
243 Roo.bootstrap.Component = function(config){
244     Roo.bootstrap.Component.superclass.constructor.call(this, config);
245        
246     this.addEvents({
247         /**
248          * @event childrenrendered
249          * Fires when the children have been rendered..
250          * @param {Roo.bootstrap.Component} this
251          */
252         "childrenrendered" : true
253         
254         
255         
256     });
257     
258     
259 };
260
261 Roo.extend(Roo.bootstrap.Component, Roo.BoxComponent,  {
262     
263     
264     allowDomMove : false, // to stop relocations in parent onRender...
265     
266     cls : false,
267     
268     style : false,
269     
270     autoCreate : false,
271     
272     tooltip : null,
273     /**
274      * Initialize Events for the element
275      */
276     initEvents : function() { },
277     
278     xattr : false,
279     
280     parentId : false,
281     
282     can_build_overlaid : true,
283     
284     container_method : false,
285     
286     dataId : false,
287     
288     name : false,
289     
290     parent: function() {
291         // returns the parent component..
292         return Roo.ComponentMgr.get(this.parentId)
293         
294         
295     },
296     
297     // private
298     onRender : function(ct, position)
299     {
300        // Roo.log("Call onRender: " + this.xtype);
301         
302         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
303         
304         if(this.el){
305             if (this.el.attr('xtype')) {
306                 this.el.attr('xtypex', this.el.attr('xtype'));
307                 this.el.dom.removeAttribute('xtype');
308                 
309                 this.initEvents();
310             }
311             
312             return;
313         }
314         
315          
316         
317         var cfg = Roo.apply({},  this.getAutoCreate());
318         
319         cfg.id = this.id || Roo.id();
320         
321         // fill in the extra attributes 
322         if (this.xattr && typeof(this.xattr) =='object') {
323             for (var i in this.xattr) {
324                 cfg[i] = this.xattr[i];
325             }
326         }
327         
328         if(this.dataId){
329             cfg.dataId = this.dataId;
330         }
331         
332         if (this.cls) {
333             cfg.cls = (typeof(cfg.cls) == 'undefined') ? this.cls : cfg.cls + ' ' + this.cls;
334         }
335         
336         if (this.style) { // fixme needs to support more complex style data.
337             cfg.style = this.style;
338         }
339         
340         if(this.name){
341             cfg.name = this.name;
342         }
343         
344         this.el = ct.createChild(cfg, position);
345         
346         if (this.tooltip) {
347             this.tooltipEl().attr('tooltip', this.tooltip);
348         }
349         
350         if(this.tabIndex !== undefined){
351             this.el.dom.setAttribute('tabIndex', this.tabIndex);
352         }
353         
354         this.initEvents();
355         
356     },
357     /**
358      * Fetch the element to add children to
359      * @return {Roo.Element} defaults to this.el
360      */
361     getChildContainer : function()
362     {
363         return this.el;
364     },
365     /**
366      * Fetch the element to display the tooltip on.
367      * @return {Roo.Element} defaults to this.el
368      */
369     tooltipEl : function()
370     {
371         return this.el;
372     },
373         
374     addxtype  : function(tree,cntr)
375     {
376         var cn = this;
377         
378         cn = Roo.factory(tree);
379         //Roo.log(['addxtype', cn]);
380            
381         cn.parentType = this.xtype; //??
382         cn.parentId = this.id;
383         
384         cntr = (typeof(cntr) == 'undefined' ) ? 'getChildContainer' : cntr;
385         if (typeof(cn.container_method) == 'string') {
386             cntr = cn.container_method;
387         }
388         
389         
390         var has_flexy_each =  (typeof(tree['flexy:foreach']) != 'undefined');
391         
392         var has_flexy_if =  (typeof(tree['flexy:if']) != 'undefined');
393         
394         var build_from_html =  Roo.XComponent.build_from_html;
395           
396         var is_body  = (tree.xtype == 'Body') ;
397           
398         var page_has_body = (Roo.get(document.body).attr('xtype') == 'Roo.bootstrap.Body');
399           
400         var self_cntr_el = Roo.get(this[cntr](false));
401         
402         // do not try and build conditional elements 
403         if ((has_flexy_each || has_flexy_if || this.can_build_overlaid == false ) && build_from_html) {
404             return false;
405         }
406         
407         if (!has_flexy_each || !build_from_html || is_body || !page_has_body) {
408             if(!has_flexy_if || typeof(tree.name) == 'undefined' || !build_from_html || is_body || !page_has_body){
409                 return this.addxtypeChild(tree,cntr, is_body);
410             }
411             
412             var echild =self_cntr_el ? self_cntr_el.child('>*[name=' + tree.name + ']') : false;
413                 
414             if(echild){
415                 return this.addxtypeChild(Roo.apply({}, tree),cntr);
416             }
417             
418             Roo.log('skipping render');
419             return cn;
420             
421         }
422         
423         var ret = false;
424         if (!build_from_html) {
425             return false;
426         }
427         
428         // this i think handles overlaying multiple children of the same type
429         // with the sam eelement.. - which might be buggy..
430         while (true) {
431             var echild =self_cntr_el ? self_cntr_el.child('>*[xtype]') : false;
432             
433             if (!echild) {
434                 break;
435             }
436             
437             if (echild && echild.attr('xtype').split('.').pop() != cn.xtype) {
438                 break;
439             }
440             
441             ret = this.addxtypeChild(Roo.apply({}, tree),cntr);
442         }
443        
444         return ret;
445     },
446     
447     
448     addxtypeChild : function (tree, cntr, is_body)
449     {
450         Roo.debug && Roo.log('addxtypeChild:' + cntr);
451         var cn = this;
452         cntr = (typeof(cntr) == 'undefined' ) ? 'getChildContainer' : cntr;
453         
454         
455         var has_flexy = (typeof(tree['flexy:if']) != 'undefined') ||
456                     (typeof(tree['flexy:foreach']) != 'undefined');
457           
458     
459         
460         skip_children = false;
461         // render the element if it's not BODY.
462         if (!is_body) {
463             
464             // if parent was disabled, then do not try and create the children..
465             if(!this[cntr](true)){
466                 tree.items = [];
467                 return tree;
468             }
469            
470             cn = Roo.factory(tree);
471            
472             cn.parentType = this.xtype; //??
473             cn.parentId = this.id;
474             
475             var build_from_html =  Roo.XComponent.build_from_html;
476             
477             
478             // does the container contain child eleemnts with 'xtype' attributes.
479             // that match this xtype..
480             // note - when we render we create these as well..
481             // so we should check to see if body has xtype set.
482             if (build_from_html && Roo.get(document.body).attr('xtype') == 'Roo.bootstrap.Body') {
483                
484                 var self_cntr_el = Roo.get(this[cntr](false));
485                 var echild =self_cntr_el ? self_cntr_el.child('>*[xtype]') : false;
486                 if (echild) { 
487                     //Roo.log(Roo.XComponent.build_from_html);
488                     //Roo.log("got echild:");
489                     //Roo.log(echild);
490                 }
491                 // there is a scenario where some of the child elements are flexy:if (and all of the same type)
492                 // and are not displayed -this causes this to use up the wrong element when matching.
493                 // at present the only work around for this is to nest flexy:if elements in another element that is always rendered.
494                 
495                 
496                 if (echild && echild.attr('xtype').split('.').pop() == cn.xtype) {
497                   //  Roo.log("found child for " + this.xtype +": " + echild.attr('xtype') );
498                   
499                   
500                   
501                     cn.el = echild;
502                   //  Roo.log("GOT");
503                     //echild.dom.removeAttribute('xtype');
504                 } else {
505                     Roo.debug && Roo.log("MISSING " + cn.xtype + " on child of " + (this.el ? this.el.attr('xbuilderid') : 'no parent'));
506                     Roo.debug && Roo.log(self_cntr_el);
507                     Roo.debug && Roo.log(echild);
508                     Roo.debug && Roo.log(cn);
509                 }
510             }
511            
512             
513            
514             // if object has flexy:if - then it may or may not be rendered.
515             if (build_from_html && has_flexy && !cn.el &&  cn.can_build_overlaid) {
516                 // skip a flexy if element.
517                 Roo.debug && Roo.log('skipping render');
518                 Roo.debug && Roo.log(tree);
519                 if (!cn.el) {
520                     Roo.debug && Roo.log('skipping all children');
521                     skip_children = true;
522                 }
523                 
524              } else {
525                  
526                 // actually if flexy:foreach is found, we really want to create 
527                 // multiple copies here...
528                 //Roo.log('render');
529                 //Roo.log(this[cntr]());
530                 // some elements do not have render methods.. like the layouts...
531                 /*
532                 if(this[cntr](true) === false){
533                     cn.items = [];
534                     return cn;
535                 }
536                 */
537                 cn.render && cn.render(this[cntr](true));
538                 
539              }
540             // then add the element..
541         }
542          
543         // handle the kids..
544         
545         var nitems = [];
546         /*
547         if (typeof (tree.menu) != 'undefined') {
548             tree.menu.parentType = cn.xtype;
549             tree.menu.triggerEl = cn.el;
550             nitems.push(cn.addxtype(Roo.apply({}, tree.menu)));
551             
552         }
553         */
554         if (!tree.items || !tree.items.length) {
555             cn.items = nitems;
556             //Roo.log(["no children", this]);
557             
558             return cn;
559         }
560          
561         var items = tree.items;
562         delete tree.items;
563         
564         //Roo.log(items.length);
565             // add the items..
566         if (!skip_children) {    
567             for(var i =0;i < items.length;i++) {
568               //  Roo.log(['add child', items[i]]);
569                 nitems.push(cn.addxtype(Roo.apply({}, items[i])));
570             }
571         }
572         
573         cn.items = nitems;
574         
575         //Roo.log("fire childrenrendered");
576         
577         cn.fireEvent('childrenrendered', this);
578         
579         return cn;
580     },
581     
582     /**
583      * Set the element that will be used to show or hide
584      */
585     setVisibilityEl : function(el)
586     {
587         this.visibilityEl = el;
588     },
589     
590      /**
591      * Get the element that will be used to show or hide
592      */
593     getVisibilityEl : function()
594     {
595         if (typeof(this.visibilityEl) == 'object') {
596             return this.visibilityEl;
597         }
598         
599         if (typeof(this.visibilityEl) == 'string') {
600             return this.visibilityEl == 'parent' ? this.parent().getEl() : this.getEl();
601         }
602         
603         return this.getEl();
604     },
605     
606     /**
607      * Show a component - removes 'hidden' class
608      */
609     show : function()
610     {
611         if(!this.getVisibilityEl()){
612             return;
613         }
614          
615         this.getVisibilityEl().removeClass(['hidden','d-none']);
616         
617         this.fireEvent('show', this);
618         
619         
620     },
621     /**
622      * Hide a component - adds 'hidden' class
623      */
624     hide: function()
625     {
626         if(!this.getVisibilityEl()){
627             return;
628         }
629         
630         this.getVisibilityEl().addClass(['hidden','d-none']);
631         
632         this.fireEvent('hide', this);
633         
634     }
635 });
636
637  /*
638  * - LGPL
639  *
640  * element
641  * 
642  */
643
644 /**
645  * @class Roo.bootstrap.Element
646  * @extends Roo.bootstrap.Component
647  * Bootstrap Element class
648  * @cfg {String} html contents of the element
649  * @cfg {String} tag tag of the element
650  * @cfg {String} cls class of the element
651  * @cfg {Boolean} preventDefault (true|false) default false
652  * @cfg {Boolean} clickable (true|false) default false
653  * 
654  * @constructor
655  * Create a new Element
656  * @param {Object} config The config object
657  */
658
659 Roo.bootstrap.Element = function(config){
660     Roo.bootstrap.Element.superclass.constructor.call(this, config);
661     
662     this.addEvents({
663         // raw events
664         /**
665          * @event click
666          * When a element is chick
667          * @param {Roo.bootstrap.Element} this
668          * @param {Roo.EventObject} e
669          */
670         "click" : true
671     });
672 };
673
674 Roo.extend(Roo.bootstrap.Element, Roo.bootstrap.Component,  {
675     
676     tag: 'div',
677     cls: '',
678     html: '',
679     preventDefault: false, 
680     clickable: false,
681     
682     getAutoCreate : function(){
683         
684         var cfg = {
685             tag: this.tag,
686             // cls: this.cls, double assign in parent class Component.js :: onRender
687             html: this.html
688         };
689         
690         return cfg;
691     },
692     
693     initEvents: function() 
694     {
695         Roo.bootstrap.Element.superclass.initEvents.call(this);
696         
697         if(this.clickable){
698             this.el.on('click', this.onClick, this);
699         }
700         
701     },
702     
703     onClick : function(e)
704     {
705         if(this.preventDefault){
706             e.preventDefault();
707         }
708         
709         this.fireEvent('click', this, e);
710     },
711     
712     getValue : function()
713     {
714         return this.el.dom.innerHTML;
715     },
716     
717     setValue : function(value)
718     {
719         this.el.dom.innerHTML = value;
720     }
721    
722 });
723
724  
725
726  /*
727  * - LGPL
728  *
729  * dropable area
730  * 
731  */
732
733 /**
734  * @class Roo.bootstrap.DropTarget
735  * @extends Roo.bootstrap.Element
736  * Bootstrap DropTarget class
737  
738  * @cfg {string} name dropable name
739  * 
740  * @constructor
741  * Create a new Dropable Area
742  * @param {Object} config The config object
743  */
744
745 Roo.bootstrap.DropTarget = function(config){
746     Roo.bootstrap.DropTarget.superclass.constructor.call(this, config);
747     
748     this.addEvents({
749         // raw events
750         /**
751          * @event click
752          * When a element is chick
753          * @param {Roo.bootstrap.Element} this
754          * @param {Roo.EventObject} e
755          */
756         "drop" : true
757     });
758 };
759
760 Roo.extend(Roo.bootstrap.DropTarget, Roo.bootstrap.Element,  {
761     
762     
763     getAutoCreate : function(){
764         
765          
766     },
767     
768     initEvents: function() 
769     {
770         Roo.bootstrap.DropTarget.superclass.initEvents.call(this);
771         this.dropZone = new Roo.dd.DropTarget(this.getEl(), {
772             ddGroup: this.name,
773             listeners : {
774                 drop : this.dragDrop.createDelegate(this),
775                 enter : this.dragEnter.createDelegate(this),
776                 out : this.dragOut.createDelegate(this),
777                 over : this.dragOver.createDelegate(this)
778             }
779             
780         });
781         this.dropZone.DDM.useCache = false // so data gets refreshed when we resize stuff
782     },
783     
784     dragDrop : function(source,e,data)
785     {
786         // user has to decide how to impliment this.
787         Roo.log('drop');
788         Roo.log(this);
789         //this.fireEvent('drop', this, source, e ,data);
790         return false;
791     },
792     
793     dragEnter : function(n, dd, e, data)
794     {
795         // probably want to resize the element to match the dropped element..
796         Roo.log("enter");
797         this.originalSize = this.el.getSize();
798         this.el.setSize( n.el.getSize());
799         this.dropZone.DDM.refreshCache(this.name);
800         Roo.log([n, dd, e, data]);
801     },
802     
803     dragOut : function(value)
804     {
805         // resize back to normal
806         Roo.log("out");
807         this.el.setSize(this.originalSize);
808         this.dropZone.resetConstraints();
809     },
810     
811     dragOver : function()
812     {
813         // ??? do nothing?
814     }
815    
816 });
817
818  
819
820  /*
821  * - LGPL
822  *
823  * Body
824  *
825  */
826
827 /**
828  * @class Roo.bootstrap.Body
829  * @extends Roo.bootstrap.Component
830  * Bootstrap Body class
831  *
832  * @constructor
833  * Create a new body
834  * @param {Object} config The config object
835  */
836
837 Roo.bootstrap.Body = function(config){
838
839     config = config || {};
840
841     Roo.bootstrap.Body.superclass.constructor.call(this, config);
842     this.el = Roo.get(config.el ? config.el : document.body );
843     if (this.cls && this.cls.length) {
844         Roo.get(document.body).addClass(this.cls);
845     }
846 };
847
848 Roo.extend(Roo.bootstrap.Body, Roo.bootstrap.Component,  {
849
850     is_body : true,// just to make sure it's constructed?
851
852         autoCreate : {
853         cls: 'container'
854     },
855     onRender : function(ct, position)
856     {
857        /* Roo.log("Roo.bootstrap.Body - onRender");
858         if (this.cls && this.cls.length) {
859             Roo.get(document.body).addClass(this.cls);
860         }
861         // style??? xttr???
862         */
863     }
864
865
866
867
868 });
869 /*
870  * - LGPL
871  *
872  * button group
873  * 
874  */
875
876
877 /**
878  * @class Roo.bootstrap.ButtonGroup
879  * @extends Roo.bootstrap.Component
880  * Bootstrap ButtonGroup class
881  * @cfg {String} size lg | sm | xs (default empty normal)
882  * @cfg {String} align vertical | justified  (default none)
883  * @cfg {String} direction up | down (default down)
884  * @cfg {Boolean} toolbar false | true
885  * @cfg {Boolean} btn true | false
886  * 
887  * 
888  * @constructor
889  * Create a new Input
890  * @param {Object} config The config object
891  */
892
893 Roo.bootstrap.ButtonGroup = function(config){
894     Roo.bootstrap.ButtonGroup.superclass.constructor.call(this, config);
895 };
896
897 Roo.extend(Roo.bootstrap.ButtonGroup, Roo.bootstrap.Component,  {
898     
899     size: '',
900     align: '',
901     direction: '',
902     toolbar: false,
903     btn: true,
904
905     getAutoCreate : function(){
906         var cfg = {
907             cls: 'btn-group',
908             html : null
909         };
910         
911         cfg.html = this.html || cfg.html;
912         
913         if (this.toolbar) {
914             cfg = {
915                 cls: 'btn-toolbar',
916                 html: null
917             };
918             
919             return cfg;
920         }
921         
922         if (['vertical','justified'].indexOf(this.align)!==-1) {
923             cfg.cls = 'btn-group-' + this.align;
924             
925             if (this.align == 'justified') {
926                 console.log(this.items);
927             }
928         }
929         
930         if (['lg','sm','xs'].indexOf(this.size)!==-1) {
931             cfg.cls += ' btn-group-' + this.size;
932         }
933         
934         if (this.direction == 'up') {
935             cfg.cls += ' dropup' ;
936         }
937         
938         return cfg;
939     },
940     /**
941      * Add a button to the group (similar to NavItem API.)
942      */
943     addItem : function(cfg)
944     {
945         var cn = new Roo.bootstrap.Button(cfg);
946         //this.register(cn);
947         cn.parentId = this.id;
948         cn.onRender(this.el, null);
949         return cn;
950     }
951    
952 });
953
954  /*
955  * - LGPL
956  *
957  * button
958  * 
959  */
960
961 /**
962  * @class Roo.bootstrap.Button
963  * @extends Roo.bootstrap.Component
964  * Bootstrap Button class
965  * @cfg {String} html The button content
966  * @cfg {String} weight (default|primary|secondary|success|info|warning|danger|link|light|dark) default
967  * @cfg {String} badge_weight (default|primary|secondary|success|info|warning|danger|link|light|dark) default (same as button)
968  * @cfg {Boolean} outline default false (except for weight=default which emulates old behaveiour with an outline)
969  * @cfg {String} size (lg|sm|xs)
970  * @cfg {String} tag (a|input|submit)
971  * @cfg {String} href empty or href
972  * @cfg {Boolean} disabled default false;
973  * @cfg {Boolean} isClose default false;
974  * @cfg {String} glyphicon depricated - use fa
975  * @cfg {String} fa fontawesome icon - eg. 'comment' - without the fa/fas etc..
976  * @cfg {String} badge text for badge
977  * @cfg {String} theme (default|glow)  
978  * @cfg {Boolean} inverse dark themed version
979  * @cfg {Boolean} toggle is it a slidy toggle button
980  * @cfg {Boolean} pressed   default null - if the button ahs active state
981  * @cfg {String} ontext text for on slidy toggle state
982  * @cfg {String} offtext text for off slidy toggle state
983  * @cfg {Boolean} preventDefault  default true (stop click event triggering the URL if it's a link.)
984  * @cfg {Boolean} removeClass remove the standard class..
985  * @cfg {String} target (_self|_blank|_parent|_top|other) target for a href. 
986  * @cfg {Boolean} grpup if parent is a btn group - then it turns it into a toogleGroup.
987  * 
988  * @constructor
989  * Create a new button
990  * @param {Object} config The config object
991  */
992
993
994 Roo.bootstrap.Button = function(config){
995     Roo.bootstrap.Button.superclass.constructor.call(this, config);
996     
997     this.addEvents({
998         // raw events
999         /**
1000          * @event click
1001          * When a button is pressed
1002          * @param {Roo.bootstrap.Button} btn
1003          * @param {Roo.EventObject} e
1004          */
1005         "click" : true,
1006         /**
1007          * @event dblclick
1008          * When a button is double clicked
1009          * @param {Roo.bootstrap.Button} btn
1010          * @param {Roo.EventObject} e
1011          */
1012         "dblclick" : true,
1013          /**
1014          * @event toggle
1015          * After the button has been toggles
1016          * @param {Roo.bootstrap.Button} btn
1017          * @param {Roo.EventObject} e
1018          * @param {boolean} pressed (also available as button.pressed)
1019          */
1020         "toggle" : true
1021     });
1022 };
1023
1024 Roo.extend(Roo.bootstrap.Button, Roo.bootstrap.Component,  {
1025     html: false,
1026     active: false,
1027     weight: '',
1028     badge_weight: '',
1029     outline : false,
1030     size: '',
1031     tag: 'button',
1032     href: '',
1033     disabled: false,
1034     isClose: false,
1035     glyphicon: '',
1036     fa: '',
1037     badge: '',
1038     theme: 'default',
1039     inverse: false,
1040     
1041     toggle: false,
1042     ontext: 'ON',
1043     offtext: 'OFF',
1044     defaulton: true,
1045     preventDefault: true,
1046     removeClass: false,
1047     name: false,
1048     target: false,
1049     group : false,
1050      
1051     pressed : null,
1052      
1053     
1054     getAutoCreate : function(){
1055         
1056         var cfg = {
1057             tag : 'button',
1058             cls : 'roo-button',
1059             html: ''
1060         };
1061         
1062         if (['a', 'button', 'input', 'submit'].indexOf(this.tag) < 0) {
1063             throw "Invalid value for tag: " + this.tag + ". must be a, button, input or submit.";
1064             this.tag = 'button';
1065         } else {
1066             cfg.tag = this.tag;
1067         }
1068         cfg.html = '<span class="roo-button-text">' + (this.html || cfg.html) + '</span>';
1069         
1070         if (this.toggle == true) {
1071             cfg={
1072                 tag: 'div',
1073                 cls: 'slider-frame roo-button',
1074                 cn: [
1075                     {
1076                         tag: 'span',
1077                         'data-on-text':'ON',
1078                         'data-off-text':'OFF',
1079                         cls: 'slider-button',
1080                         html: this.offtext
1081                     }
1082                 ]
1083             };
1084             // why are we validating the weights?
1085             if (Roo.bootstrap.Button.weights.indexOf(this.weight) > -1) {
1086                 cfg.cls +=  ' ' + this.weight;
1087             }
1088             
1089             return cfg;
1090         }
1091         
1092         if (this.isClose) {
1093             cfg.cls += ' close';
1094             
1095             cfg["aria-hidden"] = true;
1096             
1097             cfg.html = "&times;";
1098             
1099             return cfg;
1100         }
1101              
1102         
1103         if (this.theme==='default') {
1104             cfg.cls = 'btn roo-button';
1105             
1106             //if (this.parentType != 'Navbar') {
1107             this.weight = this.weight.length ?  this.weight : 'default';
1108             //}
1109             if (Roo.bootstrap.Button.weights.indexOf(this.weight) > -1) {
1110                 
1111                 var outline = this.outline || this.weight == 'default' ? 'outline-' : '';
1112                 var weight = this.weight == 'default' ? 'secondary' : this.weight;
1113                 cfg.cls += ' btn-' + outline + weight;
1114                 if (this.weight == 'default') {
1115                     // BC
1116                     cfg.cls += ' btn-' + this.weight;
1117                 }
1118             }
1119         } else if (this.theme==='glow') {
1120             
1121             cfg.tag = 'a';
1122             cfg.cls = 'btn-glow roo-button';
1123             
1124             if (Roo.bootstrap.Button.weights.indexOf(this.weight) > -1) {
1125                 
1126                 cfg.cls += ' ' + this.weight;
1127             }
1128         }
1129    
1130         
1131         if (this.inverse) {
1132             this.cls += ' inverse';
1133         }
1134         
1135         
1136         if (this.active || this.pressed === true) {
1137             cfg.cls += ' active';
1138         }
1139         
1140         if (this.disabled) {
1141             cfg.disabled = 'disabled';
1142         }
1143         
1144         if (this.items) {
1145             Roo.log('changing to ul' );
1146             cfg.tag = 'ul';
1147             this.glyphicon = 'caret';
1148             if (Roo.bootstrap.version == 4) {
1149                 this.fa = 'caret-down';
1150             }
1151             
1152         }
1153         
1154         cfg.cls += this.size.length ? (' btn-' + this.size) : '';
1155          
1156         //gsRoo.log(this.parentType);
1157         if (this.parentType === 'Navbar' && !this.parent().bar) {
1158             Roo.log('changing to li?');
1159             
1160             cfg.tag = 'li';
1161             
1162             cfg.cls = '';
1163             cfg.cn =  [{
1164                 tag : 'a',
1165                 cls : 'roo-button',
1166                 html : this.html,
1167                 href : this.href || '#'
1168             }];
1169             if (this.menu) {
1170                 cfg.cn[0].html = this.html  + ' <span class="caret"></span>';
1171                 cfg.cls += ' dropdown';
1172             }   
1173             
1174             delete cfg.html;
1175             
1176         }
1177         
1178        cfg.cls += this.parentType === 'Navbar' ?  ' navbar-btn' : '';
1179         
1180         if (this.glyphicon) {
1181             cfg.html = ' ' + cfg.html;
1182             
1183             cfg.cn = [
1184                 {
1185                     tag: 'span',
1186                     cls: 'glyphicon glyphicon-' + this.glyphicon
1187                 }
1188             ];
1189         }
1190         if (this.fa) {
1191             cfg.html = ' ' + cfg.html;
1192             
1193             cfg.cn = [
1194                 {
1195                     tag: 'i',
1196                     cls: 'fa fas fa-' + this.fa
1197                 }
1198             ];
1199         }
1200         
1201         if (this.badge) {
1202             cfg.html += ' ';
1203             
1204             cfg.tag = 'a';
1205             
1206 //            cfg.cls='btn roo-button';
1207             
1208             cfg.href=this.href;
1209             
1210             var value = cfg.html;
1211             
1212             if(this.glyphicon){
1213                 value = {
1214                     tag: 'span',
1215                     cls: 'glyphicon glyphicon-' + this.glyphicon,
1216                     html: this.html
1217                 };
1218             }
1219             if(this.fa){
1220                 value = {
1221                     tag: 'i',
1222                     cls: 'fa fas fa-' + this.fa,
1223                     html: this.html
1224                 };
1225             }
1226             
1227             var bw = this.badge_weight.length ? this.badge_weight :
1228                 (this.weight.length ? this.weight : 'secondary');
1229             bw = bw == 'default' ? 'secondary' : bw;
1230             
1231             cfg.cn = [
1232                 value,
1233                 {
1234                     tag: 'span',
1235                     cls: 'badge badge-' + bw,
1236                     html: this.badge
1237                 }
1238             ];
1239             
1240             cfg.html='';
1241         }
1242         
1243         if (this.menu) {
1244             cfg.cls += ' dropdown';
1245             cfg.html = typeof(cfg.html) != 'undefined' ?
1246                     cfg.html + ' <span class="caret"></span>' : '<span class="caret"></span>';
1247         }
1248         
1249         if (cfg.tag !== 'a' && this.href !== '') {
1250             throw "Tag must be a to set href.";
1251         } else if (this.href.length > 0) {
1252             cfg.href = this.href;
1253         }
1254         
1255         if(this.removeClass){
1256             cfg.cls = '';
1257         }
1258         
1259         if(this.target){
1260             cfg.target = this.target;
1261         }
1262         
1263         return cfg;
1264     },
1265     initEvents: function() {
1266        // Roo.log('init events?');
1267 //        Roo.log(this.el.dom);
1268         // add the menu...
1269         
1270         if (typeof (this.menu) != 'undefined') {
1271             this.menu.parentType = this.xtype;
1272             this.menu.triggerEl = this.el;
1273             this.addxtype(Roo.apply({}, this.menu));
1274         }
1275
1276
1277         if (this.el.hasClass('roo-button')) {
1278              this.el.on('click', this.onClick, this);
1279              this.el.on('dblclick', this.onDblClick, this);
1280         } else {
1281              this.el.select('.roo-button').on('click', this.onClick, this);
1282              this.el.select('.roo-button').on('dblclick', this.onDblClick, this);
1283              
1284         }
1285         // why?
1286         if(this.removeClass){
1287             this.el.on('click', this.onClick, this);
1288         }
1289         
1290         if (this.group === true) {
1291              if (this.pressed === false || this.pressed === true) {
1292                 // nothing
1293             } else {
1294                 this.pressed = false;
1295                 this.setActive(this.pressed);
1296             }
1297             
1298         }
1299         
1300         this.el.enableDisplayMode();
1301         
1302     },
1303     onClick : function(e)
1304     {
1305         if (this.disabled) {
1306             return;
1307         }
1308         
1309         Roo.log('button on click ');
1310         if(this.preventDefault){
1311             e.preventDefault();
1312         }
1313         
1314         if (this.group) {
1315             if (this.pressed) {
1316                 // do nothing -
1317                 return;
1318             }
1319             this.setActive(true);
1320             var pi = this.parent().items;
1321             for (var i = 0;i < pi.length;i++) {
1322                 if (this == pi[i]) {
1323                     continue;
1324                 }
1325                 if (pi[i].el.hasClass('roo-button')) {
1326                     pi[i].setActive(false);
1327                 }
1328             }
1329             this.fireEvent('click', this, e);            
1330             return;
1331         }
1332         
1333         if (this.pressed === true || this.pressed === false) {
1334             this.toggleActive(e);
1335         }
1336         
1337         
1338         this.fireEvent('click', this, e);
1339     },
1340     onDblClick: function(e)
1341     {
1342         if (this.disabled) {
1343             return;
1344         }
1345         if(this.preventDefault){
1346             e.preventDefault();
1347         }
1348         this.fireEvent('dblclick', this, e);
1349     },
1350     /**
1351      * Enables this button
1352      */
1353     enable : function()
1354     {
1355         this.disabled = false;
1356         this.el.removeClass('disabled');
1357     },
1358     
1359     /**
1360      * Disable this button
1361      */
1362     disable : function()
1363     {
1364         this.disabled = true;
1365         this.el.addClass('disabled');
1366     },
1367      /**
1368      * sets the active state on/off, 
1369      * @param {Boolean} state (optional) Force a particular state
1370      */
1371     setActive : function(v) {
1372         
1373         this.el[v ? 'addClass' : 'removeClass']('active');
1374         this.pressed = v;
1375     },
1376      /**
1377      * toggles the current active state 
1378      */
1379     toggleActive : function(e)
1380     {
1381         this.setActive(!this.pressed); // this modifies pressed...
1382         this.fireEvent('toggle', this, e, this.pressed);
1383     },
1384      /**
1385      * get the current active state
1386      * @return {boolean} true if it's active
1387      */
1388     isActive : function()
1389     {
1390         return this.el.hasClass('active');
1391     },
1392     /**
1393      * set the text of the first selected button
1394      */
1395     setText : function(str)
1396     {
1397         this.el.select('.roo-button-text',true).first().dom.innerHTML = str;
1398     },
1399     /**
1400      * get the text of the first selected button
1401      */
1402     getText : function()
1403     {
1404         return this.el.select('.roo-button-text',true).first().dom.innerHTML;
1405     },
1406     
1407     setWeight : function(str)
1408     {
1409         this.el.removeClass(Roo.bootstrap.Button.weights.map(function(w) { return 'btn-' + w; } ) );
1410         this.el.removeClass(Roo.bootstrap.Button.weights.map(function(w) { return 'btn-outline-' + w; } ) );
1411         this.weight = str;
1412         var outline = this.outline ? 'outline-' : '';
1413         if (str == 'default') {
1414             this.el.addClass('btn-default btn-outline-secondary');        
1415             return;
1416         }
1417         this.el.addClass('btn-' + outline + str);        
1418     }
1419     
1420     
1421 });
1422 // fixme - this is probably generic bootstrap - should go in some kind of enum file.. - like sizes.
1423
1424 Roo.bootstrap.Button.weights = [
1425     'default',
1426     'secondary' ,
1427     'primary',
1428     'success',
1429     'info',
1430     'warning',
1431     'danger',
1432     'link',
1433     'light',
1434     'dark'              
1435    
1436 ];/*
1437  * - LGPL
1438  *
1439  * column
1440  * 
1441  */
1442
1443 /**
1444  * @class Roo.bootstrap.Column
1445  * @extends Roo.bootstrap.Component
1446  * Bootstrap Column class
1447  * @cfg {Number} xs colspan out of 12 for mobile-sized screens or 0 for hidden
1448  * @cfg {Number} sm colspan out of 12 for tablet-sized screens or 0 for hidden
1449  * @cfg {Number} md colspan out of 12 for computer-sized screens or 0 for hidden
1450  * @cfg {Number} lg colspan out of 12 for large computer-sized screens or 0 for hidden
1451  * @cfg {Number} xsoff colspan offset out of 12 for mobile-sized screens or 0 for hidden
1452  * @cfg {Number} smoff colspan offset out of 12 for tablet-sized screens or 0 for hidden
1453  * @cfg {Number} mdoff colspan offset out of 12 for computer-sized screens or 0 for hidden
1454  * @cfg {Number} lgoff colspan offset out of 12 for large computer-sized screens or 0 for hidden
1455  *
1456  * 
1457  * @cfg {Boolean} hidden (true|false) hide the element
1458  * @cfg {String} alert (success|info|warning|danger) type alert (changes background / border...)
1459  * @cfg {String} fa (ban|check|...) font awesome icon
1460  * @cfg {Number} fasize (1|2|....) font awsome size
1461
1462  * @cfg {String} icon (info-sign|check|...) glyphicon name
1463
1464  * @cfg {String} html content of column.
1465  * 
1466  * @constructor
1467  * Create a new Column
1468  * @param {Object} config The config object
1469  */
1470
1471 Roo.bootstrap.Column = function(config){
1472     Roo.bootstrap.Column.superclass.constructor.call(this, config);
1473 };
1474
1475 Roo.extend(Roo.bootstrap.Column, Roo.bootstrap.Component,  {
1476     
1477     xs: false,
1478     sm: false,
1479     md: false,
1480     lg: false,
1481     xsoff: false,
1482     smoff: false,
1483     mdoff: false,
1484     lgoff: false,
1485     html: '',
1486     offset: 0,
1487     alert: false,
1488     fa: false,
1489     icon : false,
1490     hidden : false,
1491     fasize : 1,
1492     
1493     getAutoCreate : function(){
1494         var cfg = Roo.apply({}, Roo.bootstrap.Column.superclass.getAutoCreate.call(this));
1495         
1496         cfg = {
1497             tag: 'div',
1498             cls: 'column'
1499         };
1500         
1501         var settings=this;
1502         var sizes =   ['xs','sm','md','lg'];
1503         sizes.map(function(size ,ix){
1504             //Roo.log( size + ':' + settings[size]);
1505             
1506             if (settings[size+'off'] !== false) {
1507                 cfg.cls += ' col-' + size + '-offset-' + settings[size+'off'] ;
1508             }
1509             
1510             if (settings[size] === false) {
1511                 return;
1512             }
1513             
1514             if (!settings[size]) { // 0 = hidden
1515                 cfg.cls += ' hidden-' + size + ' hidden-' + size + '-down';
1516                 // bootsrap4
1517                 for (var i = ix; i > -1; i--) {
1518                     cfg.cls +=  ' d-' + sizes[i] + '-none'; 
1519                 }
1520                 
1521                 
1522                 return;
1523             }
1524             cfg.cls += ' col-' + size + '-' + settings[size] + (
1525                 size == 'xs' ? (' col-' + settings[size] ) : '' // bs4 col-{num} replaces col-xs
1526             );
1527             
1528         });
1529         
1530         if (this.hidden) {
1531             cfg.cls += ' hidden';
1532         }
1533         
1534         if (this.alert && ["success","info","warning", "danger"].indexOf(this.alert) > -1) {
1535             cfg.cls +=' alert alert-' + this.alert;
1536         }
1537         
1538         
1539         if (this.html.length) {
1540             cfg.html = this.html;
1541         }
1542         if (this.fa) {
1543             var fasize = '';
1544             if (this.fasize > 1) {
1545                 fasize = ' fa-' + this.fasize + 'x';
1546             }
1547             cfg.html = '<i class="fa fa-'+this.fa + fasize + '"></i>' + (cfg.html || '');
1548             
1549             
1550         }
1551         if (this.icon) {
1552             cfg.html = '<i class="glyphicon glyphicon-'+this.icon + '"></i>' +  (cfg.html || '');
1553         }
1554         
1555         return cfg;
1556     }
1557    
1558 });
1559
1560  
1561
1562  /*
1563  * - LGPL
1564  *
1565  * page container.
1566  * 
1567  */
1568
1569
1570 /**
1571  * @class Roo.bootstrap.Container
1572  * @extends Roo.bootstrap.Component
1573  * Bootstrap Container class
1574  * @cfg {Boolean} jumbotron is it a jumbotron element
1575  * @cfg {String} html content of element
1576  * @cfg {String} well (lg|sm|md) a well, large, small or medium.
1577  * @cfg {String} panel (default|primary|success|info|warning|danger) render as panel  - type - primary/success.....
1578  * @cfg {String} header content of header (for panel)
1579  * @cfg {String} footer content of footer (for panel)
1580  * @cfg {String} sticky (footer|wrap|push) block to use as footer or body- needs css-bootstrap/sticky-footer.css
1581  * @cfg {String} tag (header|aside|section) type of HTML tag.
1582  * @cfg {String} alert (success|info|warning|danger) type alert (changes background / border...)
1583  * @cfg {String} fa font awesome icon
1584  * @cfg {String} icon (info-sign|check|...) glyphicon name
1585  * @cfg {Boolean} hidden (true|false) hide the element
1586  * @cfg {Boolean} expandable (true|false) default false
1587  * @cfg {Boolean} expanded (true|false) default true
1588  * @cfg {String} rheader contet on the right of header
1589  * @cfg {Boolean} clickable (true|false) default false
1590
1591  *     
1592  * @constructor
1593  * Create a new Container
1594  * @param {Object} config The config object
1595  */
1596
1597 Roo.bootstrap.Container = function(config){
1598     Roo.bootstrap.Container.superclass.constructor.call(this, config);
1599     
1600     this.addEvents({
1601         // raw events
1602          /**
1603          * @event expand
1604          * After the panel has been expand
1605          * 
1606          * @param {Roo.bootstrap.Container} this
1607          */
1608         "expand" : true,
1609         /**
1610          * @event collapse
1611          * After the panel has been collapsed
1612          * 
1613          * @param {Roo.bootstrap.Container} this
1614          */
1615         "collapse" : true,
1616         /**
1617          * @event click
1618          * When a element is chick
1619          * @param {Roo.bootstrap.Container} this
1620          * @param {Roo.EventObject} e
1621          */
1622         "click" : true
1623     });
1624 };
1625
1626 Roo.extend(Roo.bootstrap.Container, Roo.bootstrap.Component,  {
1627     
1628     jumbotron : false,
1629     well: '',
1630     panel : '',
1631     header: '',
1632     footer : '',
1633     sticky: '',
1634     tag : false,
1635     alert : false,
1636     fa: false,
1637     icon : false,
1638     expandable : false,
1639     rheader : '',
1640     expanded : true,
1641     clickable: false,
1642   
1643      
1644     getChildContainer : function() {
1645         
1646         if(!this.el){
1647             return false;
1648         }
1649         
1650         if (this.panel.length) {
1651             return this.el.select('.panel-body',true).first();
1652         }
1653         
1654         return this.el;
1655     },
1656     
1657     
1658     getAutoCreate : function(){
1659         
1660         var cfg = {
1661             tag : this.tag || 'div',
1662             html : '',
1663             cls : ''
1664         };
1665         if (this.jumbotron) {
1666             cfg.cls = 'jumbotron';
1667         }
1668         
1669         
1670         
1671         // - this is applied by the parent..
1672         //if (this.cls) {
1673         //    cfg.cls = this.cls + '';
1674         //}
1675         
1676         if (this.sticky.length) {
1677             
1678             var bd = Roo.get(document.body);
1679             if (!bd.hasClass('bootstrap-sticky')) {
1680                 bd.addClass('bootstrap-sticky');
1681                 Roo.select('html',true).setStyle('height', '100%');
1682             }
1683              
1684             cfg.cls += 'bootstrap-sticky-' + this.sticky;
1685         }
1686         
1687         
1688         if (this.well.length) {
1689             switch (this.well) {
1690                 case 'lg':
1691                 case 'sm':
1692                     cfg.cls +=' well well-' +this.well;
1693                     break;
1694                 default:
1695                     cfg.cls +=' well';
1696                     break;
1697             }
1698         }
1699         
1700         if (this.hidden) {
1701             cfg.cls += ' hidden';
1702         }
1703         
1704         
1705         if (this.alert && ["success","info","warning", "danger"].indexOf(this.alert) > -1) {
1706             cfg.cls +=' alert alert-' + this.alert;
1707         }
1708         
1709         var body = cfg;
1710         
1711         if (this.panel.length) {
1712             cfg.cls += ' panel panel-' + this.panel;
1713             cfg.cn = [];
1714             if (this.header.length) {
1715                 
1716                 var h = [];
1717                 
1718                 if(this.expandable){
1719                     
1720                     cfg.cls = cfg.cls + ' expandable';
1721                     
1722                     h.push({
1723                         tag: 'i',
1724                         cls: (this.expanded ? 'fa fa-minus' : 'fa fa-plus') 
1725                     });
1726                     
1727                 }
1728                 
1729                 h.push(
1730                     {
1731                         tag: 'span',
1732                         cls : 'panel-title',
1733                         html : (this.expandable ? '&nbsp;' : '') + this.header
1734                     },
1735                     {
1736                         tag: 'span',
1737                         cls: 'panel-header-right',
1738                         html: this.rheader
1739                     }
1740                 );
1741                 
1742                 cfg.cn.push({
1743                     cls : 'panel-heading',
1744                     style : this.expandable ? 'cursor: pointer' : '',
1745                     cn : h
1746                 });
1747                 
1748             }
1749             
1750             body = false;
1751             cfg.cn.push({
1752                 cls : 'panel-body' + (this.expanded ? '' : ' hide'),
1753                 html : this.html
1754             });
1755             
1756             
1757             if (this.footer.length) {
1758                 cfg.cn.push({
1759                     cls : 'panel-footer',
1760                     html : this.footer
1761                     
1762                 });
1763             }
1764             
1765         }
1766         
1767         if (body) {
1768             body.html = this.html || cfg.html;
1769             // prefix with the icons..
1770             if (this.fa) {
1771                 body.html = '<i class="fa fa-'+this.fa + '"></i>' + body.html ;
1772             }
1773             if (this.icon) {
1774                 body.html = '<i class="glyphicon glyphicon-'+this.icon + '"></i>' + body.html ;
1775             }
1776             
1777             
1778         }
1779         if ((!this.cls || !this.cls.length) && (!cfg.cls || !cfg.cls.length)) {
1780             cfg.cls =  'container';
1781         }
1782         
1783         return cfg;
1784     },
1785     
1786     initEvents: function() 
1787     {
1788         if(this.expandable){
1789             var headerEl = this.headerEl();
1790         
1791             if(headerEl){
1792                 headerEl.on('click', this.onToggleClick, this);
1793             }
1794         }
1795         
1796         if(this.clickable){
1797             this.el.on('click', this.onClick, this);
1798         }
1799         
1800     },
1801     
1802     onToggleClick : function()
1803     {
1804         var headerEl = this.headerEl();
1805         
1806         if(!headerEl){
1807             return;
1808         }
1809         
1810         if(this.expanded){
1811             this.collapse();
1812             return;
1813         }
1814         
1815         this.expand();
1816     },
1817     
1818     expand : function()
1819     {
1820         if(this.fireEvent('expand', this)) {
1821             
1822             this.expanded = true;
1823             
1824             //this.el.select('.panel-body',true).first().setVisibilityMode(Roo.Element.DISPLAY).show();
1825             
1826             this.el.select('.panel-body',true).first().removeClass('hide');
1827             
1828             var toggleEl = this.toggleEl();
1829
1830             if(!toggleEl){
1831                 return;
1832             }
1833
1834             toggleEl.removeClass(['fa-minus', 'fa-plus']).addClass(['fa-minus']);
1835         }
1836         
1837     },
1838     
1839     collapse : function()
1840     {
1841         if(this.fireEvent('collapse', this)) {
1842             
1843             this.expanded = false;
1844             
1845             //this.el.select('.panel-body',true).first().setVisibilityMode(Roo.Element.DISPLAY).hide();
1846             this.el.select('.panel-body',true).first().addClass('hide');
1847         
1848             var toggleEl = this.toggleEl();
1849
1850             if(!toggleEl){
1851                 return;
1852             }
1853
1854             toggleEl.removeClass(['fa-minus', 'fa-plus']).addClass(['fa-plus']);
1855         }
1856     },
1857     
1858     toggleEl : function()
1859     {
1860         if(!this.el || !this.panel.length || !this.header.length || !this.expandable){
1861             return;
1862         }
1863         
1864         return this.el.select('.panel-heading .fa',true).first();
1865     },
1866     
1867     headerEl : function()
1868     {
1869         if(!this.el || !this.panel.length || !this.header.length){
1870             return;
1871         }
1872         
1873         return this.el.select('.panel-heading',true).first()
1874     },
1875     
1876     bodyEl : function()
1877     {
1878         if(!this.el || !this.panel.length){
1879             return;
1880         }
1881         
1882         return this.el.select('.panel-body',true).first()
1883     },
1884     
1885     titleEl : function()
1886     {
1887         if(!this.el || !this.panel.length || !this.header.length){
1888             return;
1889         }
1890         
1891         return this.el.select('.panel-title',true).first();
1892     },
1893     
1894     setTitle : function(v)
1895     {
1896         var titleEl = this.titleEl();
1897         
1898         if(!titleEl){
1899             return;
1900         }
1901         
1902         titleEl.dom.innerHTML = v;
1903     },
1904     
1905     getTitle : function()
1906     {
1907         
1908         var titleEl = this.titleEl();
1909         
1910         if(!titleEl){
1911             return '';
1912         }
1913         
1914         return titleEl.dom.innerHTML;
1915     },
1916     
1917     setRightTitle : function(v)
1918     {
1919         var t = this.el.select('.panel-header-right',true).first();
1920         
1921         if(!t){
1922             return;
1923         }
1924         
1925         t.dom.innerHTML = v;
1926     },
1927     
1928     onClick : function(e)
1929     {
1930         e.preventDefault();
1931         
1932         this.fireEvent('click', this, e);
1933     }
1934 });
1935
1936  /*
1937  *  - LGPL
1938  *
1939  *  This is BS4's Card element.. - similar to our containers probably..
1940  * 
1941  */
1942 /**
1943  * @class Roo.bootstrap.Card
1944  * @extends Roo.bootstrap.Component
1945  * Bootstrap Card class
1946  *
1947  *
1948  * possible... may not be implemented..
1949  * @cfg {String} header_image  src url of image.
1950  * @cfg {String|Object} header
1951  * @cfg {Number} header_size (0|1|2|3|4|5) H1 or H2 etc.. 0 indicates default
1952  * 
1953  * @cfg {String} title
1954  * @cfg {String} subtitle
1955  * @cfg {String|Boolean} html -- html contents - or just use children.. use false to hide it..
1956  * @cfg {String} footer
1957  
1958  * @cfg {String} weight (primary|warning|info|danger|secondary|success|light|dark)
1959  * 
1960  * @cfg {String} margin (0|1|2|3|4|5|auto)
1961  * @cfg {String} margin_top (0|1|2|3|4|5|auto)
1962  * @cfg {String} margin_bottom (0|1|2|3|4|5|auto)
1963  * @cfg {String} margin_left (0|1|2|3|4|5|auto)
1964  * @cfg {String} margin_right (0|1|2|3|4|5|auto)
1965  * @cfg {String} margin_x (0|1|2|3|4|5|auto)
1966  * @cfg {String} margin_y (0|1|2|3|4|5|auto)
1967  *
1968  * @cfg {String} padding (0|1|2|3|4|5)
1969  * @cfg {String} padding_top (0|1|2|3|4|5)next_to_card
1970  * @cfg {String} padding_bottom (0|1|2|3|4|5)
1971  * @cfg {String} padding_left (0|1|2|3|4|5)
1972  * @cfg {String} padding_right (0|1|2|3|4|5)
1973  * @cfg {String} padding_x (0|1|2|3|4|5)
1974  * @cfg {String} padding_y (0|1|2|3|4|5)
1975  *
1976  * @cfg {String} display (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
1977  * @cfg {String} display_xs (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
1978  * @cfg {String} display_sm (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
1979  * @cfg {String} display_lg (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
1980  * @cfg {String} display_xl (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
1981  
1982  * @config {Boolean} dragable  if this card can be dragged.
1983  * @config {String} drag_group  group for drag
1984  * @config {Boolean} dropable  if this card can recieve other cards being dropped onto it..
1985  * @config {String} drop_group  group for drag
1986  * 
1987  * @config {Boolean} collapsable can the body be collapsed.
1988  * @config {Boolean} collapsed is the body collapsed when rendered...
1989  * @config {Boolean} rotateable can the body be rotated by clicking on it..
1990  * @config {Boolean} rotated is the body rotated when rendered...
1991  * 
1992  * @constructor
1993  * Create a new Container
1994  * @param {Object} config The config object
1995  */
1996
1997 Roo.bootstrap.Card = function(config){
1998     Roo.bootstrap.Card.superclass.constructor.call(this, config);
1999     
2000     this.addEvents({
2001          // raw events
2002         /**
2003          * @event drop
2004          * When a element a card is dropped
2005          * @param {Roo.bootstrap.Card} this
2006          *
2007          * 
2008          * @param {Roo.bootstrap.Card} move_card the card being dropped?
2009          * @param {String} position 'above' or 'below'
2010          * @param {Roo.bootstrap.Card} next_to_card What card position is relative to of 'false' for empty list.
2011         
2012          */
2013         'drop' : true,
2014          /**
2015          * @event rotate
2016          * When a element a card is rotate
2017          * @param {Roo.bootstrap.Element} this
2018          * @param {Roo.Element} n the node being dropped?
2019          * @param {Boolean} rotate status
2020          */
2021         'rotate' : true
2022         
2023     });
2024 };
2025
2026
2027 Roo.extend(Roo.bootstrap.Card, Roo.bootstrap.Component,  {
2028     
2029     
2030     weight : '',
2031     
2032     margin: '', /// may be better in component?
2033     margin_top: '', 
2034     margin_bottom: '', 
2035     margin_left: '',
2036     margin_right: '',
2037     margin_x: '',
2038     margin_y: '',
2039     
2040     padding : '',
2041     padding_top: '', 
2042     padding_bottom: '', 
2043     padding_left: '',
2044     padding_right: '',
2045     padding_x: '',
2046     padding_y: '',
2047     
2048     display: '', 
2049     display_xs: '', 
2050     display_sm: '', 
2051     display_lg: '',
2052     display_xl: '',
2053  
2054     header_image  : '',
2055     header : '',
2056     header_size : 0,
2057     title : '',
2058     subtitle : '',
2059     html : '',
2060     footer: '',
2061
2062     collapsable : false,
2063     collapsed : false,
2064     rotateable : false,
2065     rotated : false,
2066     
2067     dragable : false,
2068     drag_group : false,
2069     dropable : false,
2070     drop_group : false,
2071     childContainer : false,
2072     dropEl : false, /// the dom placeholde element that indicates drop location.
2073     containerEl: false, // body container
2074     bodyEl: false, // card-body
2075     headerContainerEl : false, //
2076     headerEl : false,
2077     
2078     layoutCls : function()
2079     {
2080         var cls = '';
2081         var t = this;
2082         Roo.log(this.margin_bottom.length);
2083         ['', 'top', 'bottom', 'left', 'right', 'x', 'y' ].forEach(function(v) {
2084             // in theory these can do margin_top : ml-xs-3 ??? but we don't support that yet
2085             
2086             if (('' + t['margin' + (v.length ? '_' : '') + v]).length) {
2087                 cls += ' m' +  (v.length ? v[0]  : '') + '-' +  t['margin' + (v.length ? '_' : '') + v];
2088             }
2089             if (('' + t['padding' + (v.length ? '_' : '') + v]).length) {
2090                 cls += ' p' +  (v.length ? v[0]  : '') + '-' +  t['padding' + (v.length ? '_' : '') + v];
2091             }
2092         });
2093         
2094         ['', 'xs', 'sm', 'lg', 'xl'].forEach(function(v) {
2095             if (('' + t['display' + (v.length ? '_' : '') + v]).length) {
2096                 cls += ' d' +  (v.length ? '-' : '') + v + '-' + t['margin' + (v.length ? '_' : '') + v]
2097             }
2098         });
2099         
2100         // more generic support?
2101         if (this.hidden) {
2102             cls += ' d-none';
2103         }
2104         
2105         return cls;
2106     },
2107  
2108        // Roo.log("Call onRender: " + this.xtype);
2109         /*  We are looking at something like this.
2110 <div class="card">
2111     <img src="..." class="card-img-top" alt="...">
2112     <div class="card-body">
2113         <h5 class="card-title">Card title</h5>
2114          <h6 class="card-subtitle mb-2 text-muted">Card subtitle</h6>
2115
2116         >> this bit is really the body...
2117         <div> << we will ad dthis in hopefully it will not break shit.
2118         
2119         ** card text does not actually have any styling...
2120         
2121             <p class="card-text">This is a wider card with supporting text below as a natural lead-in to additional content. This content is a little bit longer.</p>
2122         
2123         </div> <<
2124           <a href="#" class="card-link">Card link</a>
2125           
2126     </div>
2127     <div class="card-footer">
2128         <small class="text-muted">Last updated 3 mins ago</small>
2129     </div>
2130 </div>
2131          */
2132     getAutoCreate : function(){
2133         
2134         var cfg = {
2135             tag : 'div',
2136             cls : 'card',
2137             cn : [ ]
2138         };
2139         
2140         if (this.weight.length && this.weight != 'light') {
2141             cfg.cls += ' text-white';
2142         } else {
2143             cfg.cls += ' text-dark'; // need as it's nested..
2144         }
2145         if (this.weight.length) {
2146             cfg.cls += ' bg-' + this.weight;
2147         }
2148         
2149         cfg.cls += this.layoutCls(); 
2150         
2151         var hdr = false;
2152         var hdr_ctr = false;
2153         if (this.header.length) {
2154             hdr = {
2155                 tag : this.header_size > 0 ? 'h' + this.header_size : 'div',
2156                 cls : 'card-header',
2157                 cn : []
2158             };
2159             cfg.cn.push(hdr);
2160             hdr_ctr = hdr;
2161         } else {
2162             hdr = {
2163                 tag : 'div',
2164                 cls : 'card-header d-none',
2165                 cn : []
2166             };
2167             cfg.cn.push(hdr);
2168             hdr_ctr = hdr;
2169         }
2170         if (this.collapsable) {
2171             hdr_ctr = {
2172             tag : 'a',
2173             cls : 'd-block user-select-none',
2174             cn: [
2175                     {
2176                         tag: 'i',
2177                         cls : 'roo-collapse-toggle fa fa-chevron-down float-right ' + (this.collapsed ? 'collapsed' : '')
2178                     }
2179                    
2180                 ]
2181             };
2182             hdr.cn.push(hdr_ctr);
2183         }
2184         
2185         hdr_ctr.cn.push(        {
2186             tag: 'span',
2187             cls: 'roo-card-header-ctr' + ( this.header.length ? '' : ' d-none'),
2188             html : this.header
2189         });
2190         
2191         
2192         if (this.header_image.length) {
2193             cfg.cn.push({
2194                 tag : 'img',
2195                 cls : 'card-img-top',
2196                 src: this.header_image // escape?
2197             });
2198         } else {
2199             cfg.cn.push({
2200                     tag : 'div',
2201                     cls : 'card-img-top d-none' 
2202                 });
2203         }
2204             
2205         var body = {
2206             tag : 'div',
2207             cls : 'card-body' + (this.html === false  ? ' d-none' : ''),
2208             cn : []
2209         };
2210         var obody = body;
2211         if (this.collapsable || this.rotateable) {
2212             obody = {
2213                 tag: 'div',
2214                 cls : 'roo-collapsable collapse ' + (this.collapsed || this.rotated ? '' : 'show'),
2215                 cn : [  body ]
2216             };
2217         }
2218         
2219         cfg.cn.push(obody);
2220         
2221         if (this.title.length) {
2222             body.cn.push({
2223                 tag : 'div',
2224                 cls : 'card-title',
2225                 src: this.title // escape?
2226             });
2227         }  
2228         
2229         if (this.subtitle.length) {
2230             body.cn.push({
2231                 tag : 'div',
2232                 cls : 'card-title',
2233                 src: this.subtitle // escape?
2234             });
2235         }
2236         
2237         body.cn.push({
2238             tag : 'div',
2239             cls : 'roo-card-body-ctr'
2240         });
2241         
2242         if (this.html.length) {
2243             body.cn.push({
2244                 tag: 'div',
2245                 html : this.html
2246             });
2247         }
2248         // fixme ? handle objects?
2249         
2250         if (this.footer.length) {
2251            
2252             cfg.cn.push({
2253                 cls : 'card-footer ' + (this.rotated ? 'd-none' : ''),
2254                 html : this.footer
2255             });
2256             
2257         } else {
2258             cfg.cn.push({cls : 'card-footer d-none'});
2259         }
2260         
2261         // footer...
2262         
2263         return cfg;
2264     },
2265     
2266     
2267     getCardHeader : function()
2268     {
2269         var  ret = this.el.select('.card-header',true).first();
2270         if (ret.hasClass('d-none')) {
2271             ret.removeClass('d-none');
2272         }
2273         
2274         return ret;
2275     },
2276     getCardFooter : function()
2277     {
2278         var  ret = this.el.select('.card-footer',true).first();
2279         if (ret.hasClass('d-none')) {
2280             ret.removeClass('d-none');
2281         }
2282         
2283         return ret;
2284     },
2285     getCardImageTop : function()
2286     {
2287         var  ret = this.el.select('.card-img-top',true).first();
2288         if (ret.hasClass('d-none')) {
2289             ret.removeClass('d-none');
2290         }
2291             
2292         return ret;
2293     },
2294     
2295     getChildContainer : function()
2296     {
2297         
2298         if(!this.el){
2299             return false;
2300         }
2301         return this.el.select('.roo-card-body-ctr',true).first();    
2302     },
2303     
2304     initEvents: function() 
2305     {
2306         this.bodyEl = this.el.select('.card-body',true).first(); 
2307         this.containerEl = this.getChildContainer();
2308         if(this.dragable){
2309             this.dragZone = new Roo.dd.DragZone(this.getEl(), {
2310                     containerScroll: true,
2311                     ddGroup: this.drag_group || 'default_card_drag_group'
2312             });
2313             this.dragZone.getDragData = this.getDragData.createDelegate(this);
2314         }
2315         if (this.dropable) {
2316             this.dropZone = new Roo.dd.DropZone(this.el.select('.card-body',true).first() , {
2317                 containerScroll: true,
2318                 ddGroup: this.drop_group || 'default_card_drag_group'
2319             });
2320             this.dropZone.getTargetFromEvent = this.getTargetFromEvent.createDelegate(this);
2321             this.dropZone.onNodeEnter = this.onNodeEnter.createDelegate(this);
2322             this.dropZone.onNodeOver = this.onNodeOver.createDelegate(this);
2323             this.dropZone.onNodeOut = this.onNodeOut.createDelegate(this);
2324             this.dropZone.onNodeDrop = this.onNodeDrop.createDelegate(this);
2325         }
2326         
2327         if (this.collapsable) {
2328             this.el.select('.card-header',true).on('click', this.onToggleCollapse, this);
2329         }
2330         if (this.rotateable) {
2331             this.el.select('.card-header',true).on('click', this.onToggleRotate, this);
2332         }
2333         this.collapsableEl = this.el.select('.roo-collapsable').first();
2334          
2335         this.footerEl = this.el.select('.card-footer').first();
2336         this.collapsableToggleEl = this.el.select('.roo-collapse-toggle');
2337         this.headerContainerEl = this.el.select('.roo-card-header-ctr').first();
2338         this.headerEl = this.el.select('.card-header',true).first();
2339         
2340         if (this.rotated) {
2341             this.el.addClass('roo-card-rotated');
2342             this.fireEvent('rotate', this, true);
2343         }
2344         
2345     },
2346     getDragData : function(e)
2347     {
2348         var target = this.getEl();
2349         if (target) {
2350             //this.handleSelection(e);
2351             
2352             var dragData = {
2353                 source: this,
2354                 copy: false,
2355                 nodes: this.getEl(),
2356                 records: []
2357             };
2358             
2359             
2360             dragData.ddel = target.dom ;    // the div element
2361             Roo.log(target.getWidth( ));
2362             dragData.ddel.style.width = target.getWidth() + 'px';
2363             
2364             return dragData;
2365         }
2366         return false;
2367     },
2368     /**
2369     *    Part of the Roo.dd.DropZone interface. If no target node is found, the
2370     *    whole Element becomes the target, and this causes the drop gesture to append.
2371     */
2372     getTargetFromEvent : function(e, dragged_card_el)
2373     {
2374         var target = e.getTarget();
2375         while ((target !== null) && (target.parentNode != this.containerEl.dom)) {
2376             target = target.parentNode;
2377         }
2378         
2379         var ret = {
2380             position: '',
2381             cards : [],
2382             card_n : -1,
2383             items_n : -1,
2384             card : false 
2385         };
2386         
2387         //Roo.log([ 'target' , target ? target.id : '--nothing--']);
2388         // see if target is one of the 'cards'...
2389         
2390         
2391         //Roo.log(this.items.length);
2392         var pos = false;
2393         
2394         var last_card_n = 0;
2395         var cards_len  = 0;
2396         for (var i = 0;i< this.items.length;i++) {
2397             
2398             if (!this.items[i].el.hasClass('card')) {
2399                  continue;
2400             }
2401             pos = this.getDropPoint(e, this.items[i].el.dom);
2402             
2403             cards_len = ret.cards.length;
2404             //Roo.log(this.items[i].el.dom.id);
2405             ret.cards.push(this.items[i]);
2406             last_card_n  = i;
2407             if (ret.card_n < 0 && pos == 'above') {
2408                 ret.position = cards_len > 0 ? 'below' : pos;
2409                 ret.items_n = i > 0 ? i - 1 : 0;
2410                 ret.card_n  = cards_len  > 0 ? cards_len - 1 : 0;
2411                 ret.card = ret.cards[ret.card_n];
2412             }
2413         }
2414         if (!ret.cards.length) {
2415             ret.card = true;
2416             ret.position = 'below';
2417             ret.items_n;
2418             return ret;
2419         }
2420         // could not find a card.. stick it at the end..
2421         if (ret.card_n < 0) {
2422             ret.card_n = last_card_n;
2423             ret.card = ret.cards[last_card_n];
2424             ret.items_n = this.items.indexOf(ret.cards[last_card_n]);
2425             ret.position = 'below';
2426         }
2427         
2428         if (this.items[ret.items_n].el == dragged_card_el) {
2429             return false;
2430         }
2431         
2432         if (ret.position == 'below') {
2433             var card_after = ret.card_n+1 == ret.cards.length ? false : ret.cards[ret.card_n+1];
2434             
2435             if (card_after  && card_after.el == dragged_card_el) {
2436                 return false;
2437             }
2438             return ret;
2439         }
2440         
2441         // its's after ..
2442         var card_before = ret.card_n > 0 ? ret.cards[ret.card_n-1] : false;
2443         
2444         if (card_before  && card_before.el == dragged_card_el) {
2445             return false;
2446         }
2447         
2448         return ret;
2449     },
2450     
2451     onNodeEnter : function(n, dd, e, data){
2452         return false;
2453     },
2454     onNodeOver : function(n, dd, e, data)
2455     {
2456        
2457         var target_info = this.getTargetFromEvent(e,data.source.el);
2458         if (target_info === false) {
2459             this.dropPlaceHolder('hide');
2460             return false;
2461         }
2462         Roo.log(['getTargetFromEvent', target_info ]);
2463         
2464          
2465         this.dropPlaceHolder('show', target_info,data);
2466         
2467         return false; 
2468     },
2469     onNodeOut : function(n, dd, e, data){
2470         this.dropPlaceHolder('hide');
2471      
2472     },
2473     onNodeDrop : function(n, dd, e, data)
2474     {
2475         
2476         // call drop - return false if
2477         
2478         // this could actually fail - if the Network drops..
2479         // we will ignore this at present..- client should probably reload
2480         // the whole set of cards if stuff like that fails.
2481         
2482         
2483         var info = this.getTargetFromEvent(e,data.source.el);
2484         if (info === false) {
2485             return false;
2486         }
2487         this.dropPlaceHolder('hide');
2488   
2489          
2490     
2491     
2492     
2493         this.acceptCard(data.source, info.position, info.card, info.items_n);
2494         return true;
2495          
2496     },
2497     firstChildCard : function()
2498     {
2499         for (var i = 0;i< this.items.length;i++) {
2500             
2501             if (!this.items[i].el.hasClass('card')) {
2502                  continue;
2503             }
2504             return this.items[i];
2505         }
2506         return this.items.length ? this.items[this.items.length-1] : false; // don't try and put stuff after the cards...
2507     },
2508     /**
2509      * accept card
2510      *
2511      * -        card.acceptCard(move_card, info.position, info.card, info.items_n);
2512      */
2513     acceptCard : function(move_card,  position, next_to_card )
2514     {
2515         if (this.fireEvent("drop", this, move_card, position, next_to_card) === false) {
2516             return false;
2517         }
2518         
2519         var to_items_n = next_to_card ? this.items.indexOf(next_to_card) : 0;
2520         
2521         
2522         var dom = move_card.el.dom;
2523         dom.parentNode.removeChild(dom);
2524         dom.style.width = ''; // clear with - which is set by drag.
2525         
2526         if (next_to_card !== false && next_to_card !== true && next_to_card.el.dom.parentNode) {
2527             var cardel = next_to_card.el.dom;
2528             
2529             if (position == 'above' ) {
2530                 cardel.parentNode.insertBefore(dom, cardel);
2531             } else if (cardel.nextSibling) {
2532                 cardel.parentNode.insertBefore(dom,cardel.nextSibling);
2533             } else {
2534                 cardel.parentNode.append(dom);
2535             }
2536         } else {
2537             // card container???
2538             this.containerEl.dom.append(dom);
2539         }
2540         
2541         //FIXME HANDLE card = true 
2542         
2543         // add this to the correct place in items.
2544         
2545         
2546         
2547         // remove Card from items.
2548         
2549         var old_parent = move_card.parent();
2550         
2551         old_parent.items = old_parent.items.filter(function(e) { return e != move_card });
2552         
2553         if (this.items.length) {
2554             var nitems = [];
2555             //Roo.log([info.items_n, info.position, this.items.length]);
2556             for (var i =0; i < this.items.length; i++) {
2557                 if (i == to_items_n && position == 'above') {
2558                     nitems.push(move_card);
2559                 }
2560                 nitems.push(this.items[i]);
2561                 if (i == to_items_n && position == 'below') {
2562                     nitems.push(move_card);
2563                 }
2564             }
2565             this.items = nitems;
2566             Roo.log(this.items);
2567         } else {
2568             this.items.push(move_card);
2569         }
2570         
2571         move_card.parentId = this.id;
2572         
2573         return true;
2574         
2575         
2576     },
2577     
2578     
2579     /**    Decide whether to drop above or below a View node. */
2580     getDropPoint : function(e, n, dd)
2581     {
2582         if (dd) {
2583              return false;
2584         }
2585         if (n == this.containerEl.dom) {
2586             return "above";
2587         }
2588         var t = Roo.lib.Dom.getY(n), b = t + n.offsetHeight;
2589         var c = t + (b - t) / 2;
2590         var y = Roo.lib.Event.getPageY(e);
2591         if(y <= c) {
2592             return "above";
2593         }else{
2594             return "below";
2595         }
2596     },
2597     onToggleCollapse : function(e)
2598         {
2599         if (this.collapsed) {
2600             this.el.select('.roo-collapse-toggle').removeClass('collapsed');
2601             this.collapsableEl.addClass('show');
2602             this.collapsed = false;
2603             return;
2604         }
2605         this.el.select('.roo-collapse-toggle').addClass('collapsed');
2606         this.collapsableEl.removeClass('show');
2607         this.collapsed = true;
2608         
2609     
2610     },
2611     
2612     onToggleRotate : function(e)
2613     {
2614         this.collapsableEl.removeClass('show');
2615         this.footerEl.removeClass('d-none');
2616         this.el.removeClass('roo-card-rotated');
2617         this.el.removeClass('d-none');
2618         if (this.rotated) {
2619             
2620             this.collapsableEl.addClass('show');
2621             this.rotated = false;
2622             this.fireEvent('rotate', this, this.rotated);
2623             return;
2624         }
2625         this.el.addClass('roo-card-rotated');
2626         this.footerEl.addClass('d-none');
2627         this.el.select('.roo-collapsable').removeClass('show');
2628         
2629         this.rotated = true;
2630         this.fireEvent('rotate', this, this.rotated);
2631     
2632     },
2633     
2634     dropPlaceHolder: function (action, info, data)
2635     {
2636         if (this.dropEl === false) {
2637             this.dropEl = Roo.DomHelper.append(this.containerEl, {
2638             cls : 'd-none'
2639             },true);
2640         }
2641         this.dropEl.removeClass(['d-none', 'd-block']);        
2642         if (action == 'hide') {
2643             
2644             this.dropEl.addClass('d-none');
2645             return;
2646         }
2647         // FIXME - info.card == true!!!
2648         this.dropEl.dom.parentNode.removeChild(this.dropEl.dom);
2649         
2650         if (info.card !== true) {
2651             var cardel = info.card.el.dom;
2652             
2653             if (info.position == 'above') {
2654                 cardel.parentNode.insertBefore(this.dropEl.dom, cardel);
2655             } else if (cardel.nextSibling) {
2656                 cardel.parentNode.insertBefore(this.dropEl.dom,cardel.nextSibling);
2657             } else {
2658                 cardel.parentNode.append(this.dropEl.dom);
2659             }
2660         } else {
2661             // card container???
2662             this.containerEl.dom.append(this.dropEl.dom);
2663         }
2664         
2665         this.dropEl.addClass('d-block roo-card-dropzone');
2666         
2667         this.dropEl.setHeight( Roo.get(data.ddel).getHeight() );
2668         
2669         
2670     
2671     
2672     
2673     },
2674     setHeaderText: function(html)
2675     {
2676         this.headerContainerEl.dom.innerHTML = html;
2677     }
2678
2679     
2680 });
2681
2682 /*
2683  * - LGPL
2684  *
2685  * Card header - holder for the card header elements.
2686  * 
2687  */
2688
2689 /**
2690  * @class Roo.bootstrap.CardHeader
2691  * @extends Roo.bootstrap.Element
2692  * Bootstrap CardHeader class
2693  * @constructor
2694  * Create a new Card Header - that you can embed children into
2695  * @param {Object} config The config object
2696  */
2697
2698 Roo.bootstrap.CardHeader = function(config){
2699     Roo.bootstrap.CardHeader.superclass.constructor.call(this, config);
2700 };
2701
2702 Roo.extend(Roo.bootstrap.CardHeader, Roo.bootstrap.Element,  {
2703     
2704     
2705     container_method : 'getCardHeader' 
2706     
2707      
2708     
2709     
2710    
2711 });
2712
2713  
2714
2715  /*
2716  * - LGPL
2717  *
2718  * Card footer - holder for the card footer elements.
2719  * 
2720  */
2721
2722 /**
2723  * @class Roo.bootstrap.CardFooter
2724  * @extends Roo.bootstrap.Element
2725  * Bootstrap CardFooter class
2726  * @constructor
2727  * Create a new Card Footer - that you can embed children into
2728  * @param {Object} config The config object
2729  */
2730
2731 Roo.bootstrap.CardFooter = function(config){
2732     Roo.bootstrap.CardFooter.superclass.constructor.call(this, config);
2733 };
2734
2735 Roo.extend(Roo.bootstrap.CardFooter, Roo.bootstrap.Element,  {
2736     
2737     
2738     container_method : 'getCardFooter' 
2739     
2740      
2741     
2742     
2743    
2744 });
2745
2746  
2747
2748  /*
2749  * - LGPL
2750  *
2751  * Card header - holder for the card header elements.
2752  * 
2753  */
2754
2755 /**
2756  * @class Roo.bootstrap.CardImageTop
2757  * @extends Roo.bootstrap.Element
2758  * Bootstrap CardImageTop class
2759  * @constructor
2760  * Create a new Card Image Top container
2761  * @param {Object} config The config object
2762  */
2763
2764 Roo.bootstrap.CardImageTop = function(config){
2765     Roo.bootstrap.CardImageTop.superclass.constructor.call(this, config);
2766 };
2767
2768 Roo.extend(Roo.bootstrap.CardImageTop, Roo.bootstrap.Element,  {
2769     
2770    
2771     container_method : 'getCardImageTop' 
2772     
2773      
2774     
2775    
2776 });
2777
2778  
2779
2780  /*
2781  * - LGPL
2782  *
2783  * image
2784  * 
2785  */
2786
2787
2788 /**
2789  * @class Roo.bootstrap.Img
2790  * @extends Roo.bootstrap.Component
2791  * Bootstrap Img class
2792  * @cfg {Boolean} imgResponsive false | true
2793  * @cfg {String} border rounded | circle | thumbnail
2794  * @cfg {String} src image source
2795  * @cfg {String} alt image alternative text
2796  * @cfg {String} href a tag href
2797  * @cfg {String} target (_self|_blank|_parent|_top)target for a href.
2798  * @cfg {String} xsUrl xs image source
2799  * @cfg {String} smUrl sm image source
2800  * @cfg {String} mdUrl md image source
2801  * @cfg {String} lgUrl lg image source
2802  * 
2803  * @constructor
2804  * Create a new Input
2805  * @param {Object} config The config object
2806  */
2807
2808 Roo.bootstrap.Img = function(config){
2809     Roo.bootstrap.Img.superclass.constructor.call(this, config);
2810     
2811     this.addEvents({
2812         // img events
2813         /**
2814          * @event click
2815          * The img click event for the img.
2816          * @param {Roo.EventObject} e
2817          */
2818         "click" : true
2819     });
2820 };
2821
2822 Roo.extend(Roo.bootstrap.Img, Roo.bootstrap.Component,  {
2823     
2824     imgResponsive: true,
2825     border: '',
2826     src: 'about:blank',
2827     href: false,
2828     target: false,
2829     xsUrl: '',
2830     smUrl: '',
2831     mdUrl: '',
2832     lgUrl: '',
2833
2834     getAutoCreate : function()
2835     {   
2836         if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
2837             return this.createSingleImg();
2838         }
2839         
2840         var cfg = {
2841             tag: 'div',
2842             cls: 'roo-image-responsive-group',
2843             cn: []
2844         };
2845         var _this = this;
2846         
2847         Roo.each(['xs', 'sm', 'md', 'lg'], function(size){
2848             
2849             if(!_this[size + 'Url']){
2850                 return;
2851             }
2852             
2853             var img = {
2854                 tag: 'img',
2855                 cls: (_this.imgResponsive) ? 'img-responsive' : '',
2856                 html: _this.html || cfg.html,
2857                 src: _this[size + 'Url']
2858             };
2859             
2860             img.cls += ' roo-image-responsive-' + size;
2861             
2862             var s = ['xs', 'sm', 'md', 'lg'];
2863             
2864             s.splice(s.indexOf(size), 1);
2865             
2866             Roo.each(s, function(ss){
2867                 img.cls += ' hidden-' + ss;
2868             });
2869             
2870             if (['rounded','circle','thumbnail'].indexOf(_this.border)>-1) {
2871                 cfg.cls += ' img-' + _this.border;
2872             }
2873             
2874             if(_this.alt){
2875                 cfg.alt = _this.alt;
2876             }
2877             
2878             if(_this.href){
2879                 var a = {
2880                     tag: 'a',
2881                     href: _this.href,
2882                     cn: [
2883                         img
2884                     ]
2885                 };
2886
2887                 if(this.target){
2888                     a.target = _this.target;
2889                 }
2890             }
2891             
2892             cfg.cn.push((_this.href) ? a : img);
2893             
2894         });
2895         
2896         return cfg;
2897     },
2898     
2899     createSingleImg : function()
2900     {
2901         var cfg = {
2902             tag: 'img',
2903             cls: (this.imgResponsive) ? 'img-responsive' : '',
2904             html : null,
2905             src : 'about:blank'  // just incase src get's set to undefined?!?
2906         };
2907         
2908         cfg.html = this.html || cfg.html;
2909         
2910         cfg.src = this.src || cfg.src;
2911         
2912         if (['rounded','circle','thumbnail'].indexOf(this.border)>-1) {
2913             cfg.cls += ' img-' + this.border;
2914         }
2915         
2916         if(this.alt){
2917             cfg.alt = this.alt;
2918         }
2919         
2920         if(this.href){
2921             var a = {
2922                 tag: 'a',
2923                 href: this.href,
2924                 cn: [
2925                     cfg
2926                 ]
2927             };
2928             
2929             if(this.target){
2930                 a.target = this.target;
2931             }
2932             
2933         }
2934         
2935         return (this.href) ? a : cfg;
2936     },
2937     
2938     initEvents: function() 
2939     {
2940         if(!this.href){
2941             this.el.on('click', this.onClick, this);
2942         }
2943         
2944     },
2945     
2946     onClick : function(e)
2947     {
2948         Roo.log('img onclick');
2949         this.fireEvent('click', this, e);
2950     },
2951     /**
2952      * Sets the url of the image - used to update it
2953      * @param {String} url the url of the image
2954      */
2955     
2956     setSrc : function(url)
2957     {
2958         this.src =  url;
2959         
2960         if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
2961             this.el.dom.src =  url;
2962             return;
2963         }
2964         
2965         this.el.select('img', true).first().dom.src =  url;
2966     }
2967     
2968     
2969    
2970 });
2971
2972  /*
2973  * - LGPL
2974  *
2975  * image
2976  * 
2977  */
2978
2979
2980 /**
2981  * @class Roo.bootstrap.Link
2982  * @extends Roo.bootstrap.Component
2983  * Bootstrap Link Class
2984  * @cfg {String} alt image alternative text
2985  * @cfg {String} href a tag href
2986  * @cfg {String} target (_self|_blank|_parent|_top) target for a href.
2987  * @cfg {String} html the content of the link.
2988  * @cfg {String} anchor name for the anchor link
2989  * @cfg {String} fa - favicon
2990
2991  * @cfg {Boolean} preventDefault (true | false) default false
2992
2993  * 
2994  * @constructor
2995  * Create a new Input
2996  * @param {Object} config The config object
2997  */
2998
2999 Roo.bootstrap.Link = function(config){
3000     Roo.bootstrap.Link.superclass.constructor.call(this, config);
3001     
3002     this.addEvents({
3003         // img events
3004         /**
3005          * @event click
3006          * The img click event for the img.
3007          * @param {Roo.EventObject} e
3008          */
3009         "click" : true
3010     });
3011 };
3012
3013 Roo.extend(Roo.bootstrap.Link, Roo.bootstrap.Component,  {
3014     
3015     href: false,
3016     target: false,
3017     preventDefault: false,
3018     anchor : false,
3019     alt : false,
3020     fa: false,
3021
3022
3023     getAutoCreate : function()
3024     {
3025         var html = this.html || '';
3026         
3027         if (this.fa !== false) {
3028             html = '<i class="fa fa-' + this.fa + '"></i>';
3029         }
3030         var cfg = {
3031             tag: 'a'
3032         };
3033         // anchor's do not require html/href...
3034         if (this.anchor === false) {
3035             cfg.html = html;
3036             cfg.href = this.href || '#';
3037         } else {
3038             cfg.name = this.anchor;
3039             if (this.html !== false || this.fa !== false) {
3040                 cfg.html = html;
3041             }
3042             if (this.href !== false) {
3043                 cfg.href = this.href;
3044             }
3045         }
3046         
3047         if(this.alt !== false){
3048             cfg.alt = this.alt;
3049         }
3050         
3051         
3052         if(this.target !== false) {
3053             cfg.target = this.target;
3054         }
3055         
3056         return cfg;
3057     },
3058     
3059     initEvents: function() {
3060         
3061         if(!this.href || this.preventDefault){
3062             this.el.on('click', this.onClick, this);
3063         }
3064     },
3065     
3066     onClick : function(e)
3067     {
3068         if(this.preventDefault){
3069             e.preventDefault();
3070         }
3071         //Roo.log('img onclick');
3072         this.fireEvent('click', this, e);
3073     }
3074    
3075 });
3076
3077  /*
3078  * - LGPL
3079  *
3080  * header
3081  * 
3082  */
3083
3084 /**
3085  * @class Roo.bootstrap.Header
3086  * @extends Roo.bootstrap.Component
3087  * Bootstrap Header class
3088  * @cfg {String} html content of header
3089  * @cfg {Number} level (1|2|3|4|5|6) default 1
3090  * 
3091  * @constructor
3092  * Create a new Header
3093  * @param {Object} config The config object
3094  */
3095
3096
3097 Roo.bootstrap.Header  = function(config){
3098     Roo.bootstrap.Header.superclass.constructor.call(this, config);
3099 };
3100
3101 Roo.extend(Roo.bootstrap.Header, Roo.bootstrap.Component,  {
3102     
3103     //href : false,
3104     html : false,
3105     level : 1,
3106     
3107     
3108     
3109     getAutoCreate : function(){
3110         
3111         
3112         
3113         var cfg = {
3114             tag: 'h' + (1 *this.level),
3115             html: this.html || ''
3116         } ;
3117         
3118         return cfg;
3119     }
3120    
3121 });
3122
3123  
3124
3125  /*
3126  * Based on:
3127  * Ext JS Library 1.1.1
3128  * Copyright(c) 2006-2007, Ext JS, LLC.
3129  *
3130  * Originally Released Under LGPL - original licence link has changed is not relivant.
3131  *
3132  * Fork - LGPL
3133  * <script type="text/javascript">
3134  */
3135  
3136 /**
3137  * @class Roo.bootstrap.MenuMgr
3138  * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
3139  * @singleton
3140  */
3141 Roo.bootstrap.MenuMgr = function(){
3142    var menus, active, groups = {}, attached = false, lastShow = new Date();
3143
3144    // private - called when first menu is created
3145    function init(){
3146        menus = {};
3147        active = new Roo.util.MixedCollection();
3148        Roo.get(document).addKeyListener(27, function(){
3149            if(active.length > 0){
3150                hideAll();
3151            }
3152        });
3153    }
3154
3155    // private
3156    function hideAll(){
3157        if(active && active.length > 0){
3158            var c = active.clone();
3159            c.each(function(m){
3160                m.hide();
3161            });
3162        }
3163    }
3164
3165    // private
3166    function onHide(m){
3167        active.remove(m);
3168        if(active.length < 1){
3169            Roo.get(document).un("mouseup", onMouseDown);
3170             
3171            attached = false;
3172        }
3173    }
3174
3175    // private
3176    function onShow(m){
3177        var last = active.last();
3178        lastShow = new Date();
3179        active.add(m);
3180        if(!attached){
3181           Roo.get(document).on("mouseup", onMouseDown);
3182            
3183            attached = true;
3184        }
3185        if(m.parentMenu){
3186           //m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
3187           m.parentMenu.activeChild = m;
3188        }else if(last && last.isVisible()){
3189           //m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
3190        }
3191    }
3192
3193    // private
3194    function onBeforeHide(m){
3195        if(m.activeChild){
3196            m.activeChild.hide();
3197        }
3198        if(m.autoHideTimer){
3199            clearTimeout(m.autoHideTimer);
3200            delete m.autoHideTimer;
3201        }
3202    }
3203
3204    // private
3205    function onBeforeShow(m){
3206        var pm = m.parentMenu;
3207        if(!pm && !m.allowOtherMenus){
3208            hideAll();
3209        }else if(pm && pm.activeChild && active != m){
3210            pm.activeChild.hide();
3211        }
3212    }
3213
3214    // private this should really trigger on mouseup..
3215    function onMouseDown(e){
3216         Roo.log("on Mouse Up");
3217         
3218         if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".dropdown-menu") && !e.getTarget('.user-menu')){
3219             Roo.log("MenuManager hideAll");
3220             hideAll();
3221             e.stopEvent();
3222         }
3223         
3224         
3225    }
3226
3227    // private
3228    function onBeforeCheck(mi, state){
3229        if(state){
3230            var g = groups[mi.group];
3231            for(var i = 0, l = g.length; i < l; i++){
3232                if(g[i] != mi){
3233                    g[i].setChecked(false);
3234                }
3235            }
3236        }
3237    }
3238
3239    return {
3240
3241        /**
3242         * Hides all menus that are currently visible
3243         */
3244        hideAll : function(){
3245             hideAll();  
3246        },
3247
3248        // private
3249        register : function(menu){
3250            if(!menus){
3251                init();
3252            }
3253            menus[menu.id] = menu;
3254            menu.on("beforehide", onBeforeHide);
3255            menu.on("hide", onHide);
3256            menu.on("beforeshow", onBeforeShow);
3257            menu.on("show", onShow);
3258            var g = menu.group;
3259            if(g && menu.events["checkchange"]){
3260                if(!groups[g]){
3261                    groups[g] = [];
3262                }
3263                groups[g].push(menu);
3264                menu.on("checkchange", onCheck);
3265            }
3266        },
3267
3268         /**
3269          * Returns a {@link Roo.menu.Menu} object
3270          * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
3271          * be used to generate and return a new Menu instance.
3272          */
3273        get : function(menu){
3274            if(typeof menu == "string"){ // menu id
3275                return menus[menu];
3276            }else if(menu.events){  // menu instance
3277                return menu;
3278            }
3279            /*else if(typeof menu.length == 'number'){ // array of menu items?
3280                return new Roo.bootstrap.Menu({items:menu});
3281            }else{ // otherwise, must be a config
3282                return new Roo.bootstrap.Menu(menu);
3283            }
3284            */
3285            return false;
3286        },
3287
3288        // private
3289        unregister : function(menu){
3290            delete menus[menu.id];
3291            menu.un("beforehide", onBeforeHide);
3292            menu.un("hide", onHide);
3293            menu.un("beforeshow", onBeforeShow);
3294            menu.un("show", onShow);
3295            var g = menu.group;
3296            if(g && menu.events["checkchange"]){
3297                groups[g].remove(menu);
3298                menu.un("checkchange", onCheck);
3299            }
3300        },
3301
3302        // private
3303        registerCheckable : function(menuItem){
3304            var g = menuItem.group;
3305            if(g){
3306                if(!groups[g]){
3307                    groups[g] = [];
3308                }
3309                groups[g].push(menuItem);
3310                menuItem.on("beforecheckchange", onBeforeCheck);
3311            }
3312        },
3313
3314        // private
3315        unregisterCheckable : function(menuItem){
3316            var g = menuItem.group;
3317            if(g){
3318                groups[g].remove(menuItem);
3319                menuItem.un("beforecheckchange", onBeforeCheck);
3320            }
3321        }
3322    };
3323 }();/*
3324  * - LGPL
3325  *
3326  * menu
3327  * 
3328  */
3329
3330 /**
3331  * @class Roo.bootstrap.Menu
3332  * @extends Roo.bootstrap.Component
3333  * Bootstrap Menu class - container for MenuItems
3334  * @cfg {String} type (dropdown|treeview|submenu) type of menu
3335  * @cfg {bool} hidden  if the menu should be hidden when rendered.
3336  * @cfg {bool} stopEvent (true|false)  Stop event after trigger press (default true)
3337  * @cfg {bool} isLink (true|false)  the menu has link disable auto expand and collaspe (default false)
3338  * 
3339  * @constructor
3340  * Create a new Menu
3341  * @param {Object} config The config object
3342  */
3343
3344
3345 Roo.bootstrap.Menu = function(config){
3346     Roo.bootstrap.Menu.superclass.constructor.call(this, config);
3347     if (this.registerMenu && this.type != 'treeview')  {
3348         Roo.bootstrap.MenuMgr.register(this);
3349     }
3350     
3351     
3352     this.addEvents({
3353         /**
3354          * @event beforeshow
3355          * Fires before this menu is displayed (return false to block)
3356          * @param {Roo.menu.Menu} this
3357          */
3358         beforeshow : true,
3359         /**
3360          * @event beforehide
3361          * Fires before this menu is hidden (return false to block)
3362          * @param {Roo.menu.Menu} this
3363          */
3364         beforehide : true,
3365         /**
3366          * @event show
3367          * Fires after this menu is displayed
3368          * @param {Roo.menu.Menu} this
3369          */
3370         show : true,
3371         /**
3372          * @event hide
3373          * Fires after this menu is hidden
3374          * @param {Roo.menu.Menu} this
3375          */
3376         hide : true,
3377         /**
3378          * @event click
3379          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
3380          * @param {Roo.menu.Menu} this
3381          * @param {Roo.menu.Item} menuItem The menu item that was clicked
3382          * @param {Roo.EventObject} e
3383          */
3384         click : true,
3385         /**
3386          * @event mouseover
3387          * Fires when the mouse is hovering over this menu
3388          * @param {Roo.menu.Menu} this
3389          * @param {Roo.EventObject} e
3390          * @param {Roo.menu.Item} menuItem The menu item that was clicked
3391          */
3392         mouseover : true,
3393         /**
3394          * @event mouseout
3395          * Fires when the mouse exits this menu
3396          * @param {Roo.menu.Menu} this
3397          * @param {Roo.EventObject} e
3398          * @param {Roo.menu.Item} menuItem The menu item that was clicked
3399          */
3400         mouseout : true,
3401         /**
3402          * @event itemclick
3403          * Fires when a menu item contained in this menu is clicked
3404          * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
3405          * @param {Roo.EventObject} e
3406          */
3407         itemclick: true
3408     });
3409     this.menuitems = new Roo.util.MixedCollection(false, function(o) { return o.el.id; });
3410 };
3411
3412 Roo.extend(Roo.bootstrap.Menu, Roo.bootstrap.Component,  {
3413     
3414    /// html : false,
3415     //align : '',
3416     triggerEl : false,  // is this set by component builder? -- it should really be fetched from parent()???
3417     type: false,
3418     /**
3419      * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
3420      */
3421     registerMenu : true,
3422     
3423     menuItems :false, // stores the menu items..
3424     
3425     hidden:true,
3426         
3427     parentMenu : false,
3428     
3429     stopEvent : true,
3430     
3431     isLink : false,
3432     
3433     getChildContainer : function() {
3434         return this.el;  
3435     },
3436     
3437     getAutoCreate : function(){
3438          
3439         //if (['right'].indexOf(this.align)!==-1) {
3440         //    cfg.cn[1].cls += ' pull-right'
3441         //}
3442         
3443         
3444         var cfg = {
3445             tag : 'ul',
3446             cls : 'dropdown-menu' ,
3447             style : 'z-index:1000'
3448             
3449         };
3450         
3451         if (this.type === 'submenu') {
3452             cfg.cls = 'submenu active';
3453         }
3454         if (this.type === 'treeview') {
3455             cfg.cls = 'treeview-menu';
3456         }
3457         
3458         return cfg;
3459     },
3460     initEvents : function() {
3461         
3462        // Roo.log("ADD event");
3463        // Roo.log(this.triggerEl.dom);
3464         
3465         this.triggerEl.on('click', this.onTriggerClick, this);
3466         
3467         this.triggerEl.on(Roo.isTouch ? 'touchstart' : 'mouseup', this.onTriggerPress, this);
3468         
3469         
3470         if (this.triggerEl.hasClass('nav-item')) {
3471             // dropdown toggle on the 'a' in BS4?
3472             this.triggerEl.select('.nav-link',true).first().addClass('dropdown-toggle');
3473         } else {
3474             this.triggerEl.addClass('dropdown-toggle');
3475         }
3476         if (Roo.isTouch) {
3477             this.el.on('touchstart'  , this.onTouch, this);
3478         }
3479         this.el.on('click' , this.onClick, this);
3480
3481         this.el.on("mouseover", this.onMouseOver, this);
3482         this.el.on("mouseout", this.onMouseOut, this);
3483         
3484     },
3485     
3486     findTargetItem : function(e)
3487     {
3488         var t = e.getTarget(".dropdown-menu-item", this.el,  true);
3489         if(!t){
3490             return false;
3491         }
3492         //Roo.log(t);         Roo.log(t.id);
3493         if(t && t.id){
3494             //Roo.log(this.menuitems);
3495             return this.menuitems.get(t.id);
3496             
3497             //return this.items.get(t.menuItemId);
3498         }
3499         
3500         return false;
3501     },
3502     
3503     onTouch : function(e) 
3504     {
3505         Roo.log("menu.onTouch");
3506         //e.stopEvent(); this make the user popdown broken
3507         this.onClick(e);
3508     },
3509     
3510     onClick : function(e)
3511     {
3512         Roo.log("menu.onClick");
3513         
3514         var t = this.findTargetItem(e);
3515         if(!t || t.isContainer){
3516             return;
3517         }
3518         Roo.log(e);
3519         /*
3520         if (Roo.isTouch && e.type == 'touchstart' && t.menu  && !t.disabled) {
3521             if(t == this.activeItem && t.shouldDeactivate(e)){
3522                 this.activeItem.deactivate();
3523                 delete this.activeItem;
3524                 return;
3525             }
3526             if(t.canActivate){
3527                 this.setActiveItem(t, true);
3528             }
3529             return;
3530             
3531             
3532         }
3533         */
3534        
3535         Roo.log('pass click event');
3536         
3537         t.onClick(e);
3538         
3539         this.fireEvent("click", this, t, e);
3540         
3541         var _this = this;
3542         
3543         if(!t.href.length || t.href == '#'){
3544             (function() { _this.hide(); }).defer(100);
3545         }
3546         
3547     },
3548     
3549     onMouseOver : function(e){
3550         var t  = this.findTargetItem(e);
3551         //Roo.log(t);
3552         //if(t){
3553         //    if(t.canActivate && !t.disabled){
3554         //        this.setActiveItem(t, true);
3555         //    }
3556         //}
3557         
3558         this.fireEvent("mouseover", this, e, t);
3559     },
3560     isVisible : function(){
3561         return !this.hidden;
3562     },
3563     onMouseOut : function(e){
3564         var t  = this.findTargetItem(e);
3565         
3566         //if(t ){
3567         //    if(t == this.activeItem && t.shouldDeactivate(e)){
3568         //        this.activeItem.deactivate();
3569         //        delete this.activeItem;
3570         //    }
3571         //}
3572         this.fireEvent("mouseout", this, e, t);
3573     },
3574     
3575     
3576     /**
3577      * Displays this menu relative to another element
3578      * @param {String/HTMLElement/Roo.Element} element The element to align to
3579      * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
3580      * the element (defaults to this.defaultAlign)
3581      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
3582      */
3583     show : function(el, pos, parentMenu)
3584     {
3585         if (false === this.fireEvent("beforeshow", this)) {
3586             Roo.log("show canceled");
3587             return;
3588         }
3589         this.parentMenu = parentMenu;
3590         if(!this.el){
3591             this.render();
3592         }
3593         
3594         this.showAt(this.el.getAlignToXY(el, pos || this.defaultAlign), parentMenu, false);
3595     },
3596      /**
3597      * Displays this menu at a specific xy position
3598      * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
3599      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
3600      */
3601     showAt : function(xy, parentMenu, /* private: */_e){
3602         this.parentMenu = parentMenu;
3603         if(!this.el){
3604             this.render();
3605         }
3606         if(_e !== false){
3607             this.fireEvent("beforeshow", this);
3608             //xy = this.el.adjustForConstraints(xy);
3609         }
3610         
3611         //this.el.show();
3612         this.hideMenuItems();
3613         this.hidden = false;
3614         this.triggerEl.addClass('open');
3615         this.el.addClass('show');
3616         
3617         // reassign x when hitting right
3618         if(this.el.getWidth() + xy[0] >= Roo.lib.Dom.getViewWidth()){
3619             xy[0] = xy[0] - this.el.getWidth() + this.triggerEl.getWidth();
3620         }
3621         
3622         // reassign y when hitting bottom
3623         if(this.el.getHeight() + xy[1] >= Roo.lib.Dom.getViewHeight()){
3624             xy[1] = xy[1] - this.el.getHeight() - this.triggerEl.getHeight();
3625         }
3626         
3627         // but the list may align on trigger left or trigger top... should it be a properity?
3628         
3629         if(this.el.getStyle('top') != 'auto' && this.el.getStyle('top').slice(-1) != "%"){
3630             this.el.setXY(xy);
3631         }
3632         
3633         this.focus();
3634         this.fireEvent("show", this);
3635     },
3636     
3637     focus : function(){
3638         return;
3639         if(!this.hidden){
3640             this.doFocus.defer(50, this);
3641         }
3642     },
3643
3644     doFocus : function(){
3645         if(!this.hidden){
3646             this.focusEl.focus();
3647         }
3648     },
3649
3650     /**
3651      * Hides this menu and optionally all parent menus
3652      * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
3653      */
3654     hide : function(deep)
3655     {
3656         if (false === this.fireEvent("beforehide", this)) {
3657             Roo.log("hide canceled");
3658             return;
3659         }
3660         this.hideMenuItems();
3661         if(this.el && this.isVisible()){
3662            
3663             if(this.activeItem){
3664                 this.activeItem.deactivate();
3665                 this.activeItem = null;
3666             }
3667             this.triggerEl.removeClass('open');;
3668             this.el.removeClass('show');
3669             this.hidden = true;
3670             this.fireEvent("hide", this);
3671         }
3672         if(deep === true && this.parentMenu){
3673             this.parentMenu.hide(true);
3674         }
3675     },
3676     
3677     onTriggerClick : function(e)
3678     {
3679         Roo.log('trigger click');
3680         
3681         var target = e.getTarget();
3682         
3683         Roo.log(target.nodeName.toLowerCase());
3684         
3685         if(target.nodeName.toLowerCase() === 'i'){
3686             e.preventDefault();
3687         }
3688         
3689     },
3690     
3691     onTriggerPress  : function(e)
3692     {
3693         Roo.log('trigger press');
3694         //Roo.log(e.getTarget());
3695        // Roo.log(this.triggerEl.dom);
3696        
3697         // trigger only occurs on normal menu's -- if it's a treeview or dropdown... do not hide/show..
3698         var pel = Roo.get(e.getTarget());
3699         if (pel.findParent('.dropdown-menu') || pel.findParent('.treeview-menu') ) {
3700             Roo.log('is treeview or dropdown?');
3701             return;
3702         }
3703         
3704         if(e.getTarget().nodeName.toLowerCase() !== 'i' && this.isLink){
3705             return;
3706         }
3707         
3708         if (this.isVisible()) {
3709             Roo.log('hide');
3710             this.hide();
3711         } else {
3712             Roo.log('show');
3713             this.show(this.triggerEl, '?', false);
3714         }
3715         
3716         if(this.stopEvent || e.getTarget().nodeName.toLowerCase() === 'i'){
3717             e.stopEvent();
3718         }
3719         
3720     },
3721        
3722     
3723     hideMenuItems : function()
3724     {
3725         Roo.log("hide Menu Items");
3726         if (!this.el) { 
3727             return;
3728         }
3729         
3730         this.el.select('.open',true).each(function(aa) {
3731             
3732             aa.removeClass('open');
3733          
3734         });
3735     },
3736     addxtypeChild : function (tree, cntr) {
3737         var comp= Roo.bootstrap.Menu.superclass.addxtypeChild.call(this, tree, cntr);
3738           
3739         this.menuitems.add(comp);
3740         return comp;
3741
3742     },
3743     getEl : function()
3744     {
3745         Roo.log(this.el);
3746         return this.el;
3747     },
3748     
3749     clear : function()
3750     {
3751         this.getEl().dom.innerHTML = '';
3752         this.menuitems.clear();
3753     }
3754 });
3755
3756  
3757  /*
3758  * - LGPL
3759  *
3760  * menu item
3761  * 
3762  */
3763
3764
3765 /**
3766  * @class Roo.bootstrap.MenuItem
3767  * @extends Roo.bootstrap.Component
3768  * Bootstrap MenuItem class
3769  * @cfg {String} html the menu label
3770  * @cfg {String} href the link
3771  * @cfg {Boolean} preventDefault do not trigger A href on clicks (default false).
3772  * @cfg {Boolean} isContainer is it a container - just returns a drop down item..
3773  * @cfg {Boolean} active  used on sidebars to highlight active itesm
3774  * @cfg {String} fa favicon to show on left of menu item.
3775  * @cfg {Roo.bootsrap.Menu} menu the child menu.
3776  * 
3777  * 
3778  * @constructor
3779  * Create a new MenuItem
3780  * @param {Object} config The config object
3781  */
3782
3783
3784 Roo.bootstrap.MenuItem = function(config){
3785     Roo.bootstrap.MenuItem.superclass.constructor.call(this, config);
3786     this.addEvents({
3787         // raw events
3788         /**
3789          * @event click
3790          * The raw click event for the entire grid.
3791          * @param {Roo.bootstrap.MenuItem} this
3792          * @param {Roo.EventObject} e
3793          */
3794         "click" : true
3795     });
3796 };
3797
3798 Roo.extend(Roo.bootstrap.MenuItem, Roo.bootstrap.Component,  {
3799     
3800     href : false,
3801     html : false,
3802     preventDefault: false,
3803     isContainer : false,
3804     active : false,
3805     fa: false,
3806     
3807     getAutoCreate : function(){
3808         
3809         if(this.isContainer){
3810             return {
3811                 tag: 'li',
3812                 cls: 'dropdown-menu-item '
3813             };
3814         }
3815         var ctag = {
3816             tag: 'span',
3817             html: 'Link'
3818         };
3819         
3820         var anc = {
3821             tag : 'a',
3822             cls : 'dropdown-item',
3823             href : '#',
3824             cn : [  ]
3825         };
3826         
3827         if (this.fa !== false) {
3828             anc.cn.push({
3829                 tag : 'i',
3830                 cls : 'fa fa-' + this.fa
3831             });
3832         }
3833         
3834         anc.cn.push(ctag);
3835         
3836         
3837         var cfg= {
3838             tag: 'li',
3839             cls: 'dropdown-menu-item',
3840             cn: [ anc ]
3841         };
3842         if (this.parent().type == 'treeview') {
3843             cfg.cls = 'treeview-menu';
3844         }
3845         if (this.active) {
3846             cfg.cls += ' active';
3847         }
3848         
3849         
3850         
3851         anc.href = this.href || cfg.cn[0].href ;
3852         ctag.html = this.html || cfg.cn[0].html ;
3853         return cfg;
3854     },
3855     
3856     initEvents: function()
3857     {
3858         if (this.parent().type == 'treeview') {
3859             this.el.select('a').on('click', this.onClick, this);
3860         }
3861         
3862         if (this.menu) {
3863             this.menu.parentType = this.xtype;
3864             this.menu.triggerEl = this.el;
3865             this.menu = this.addxtype(Roo.apply({}, this.menu));
3866         }
3867         
3868     },
3869     onClick : function(e)
3870     {
3871         Roo.log('item on click ');
3872         
3873         if(this.preventDefault){
3874             e.preventDefault();
3875         }
3876         //this.parent().hideMenuItems();
3877         
3878         this.fireEvent('click', this, e);
3879     },
3880     getEl : function()
3881     {
3882         return this.el;
3883     } 
3884 });
3885
3886  
3887
3888  /*
3889  * - LGPL
3890  *
3891  * menu separator
3892  * 
3893  */
3894
3895
3896 /**
3897  * @class Roo.bootstrap.MenuSeparator
3898  * @extends Roo.bootstrap.Component
3899  * Bootstrap MenuSeparator class
3900  * 
3901  * @constructor
3902  * Create a new MenuItem
3903  * @param {Object} config The config object
3904  */
3905
3906
3907 Roo.bootstrap.MenuSeparator = function(config){
3908     Roo.bootstrap.MenuSeparator.superclass.constructor.call(this, config);
3909 };
3910
3911 Roo.extend(Roo.bootstrap.MenuSeparator, Roo.bootstrap.Component,  {
3912     
3913     getAutoCreate : function(){
3914         var cfg = {
3915             cls: 'divider',
3916             tag : 'li'
3917         };
3918         
3919         return cfg;
3920     }
3921    
3922 });
3923
3924  
3925
3926  
3927 /*
3928 * Licence: LGPL
3929 */
3930
3931 /**
3932  * @class Roo.bootstrap.Modal
3933  * @extends Roo.bootstrap.Component
3934  * Bootstrap Modal class
3935  * @cfg {String} title Title of dialog
3936  * @cfg {String} html - the body of the dialog (for simple ones) - you can also use template..
3937  * @cfg {Roo.Template} tmpl - a template with variables. to use it, add a handler in show:method  adn
3938  * @cfg {Boolean} specificTitle default false
3939  * @cfg {Array} buttons Array of buttons or standard button set..
3940  * @cfg {String} buttonPosition (left|right|center) default right (DEPRICATED) - use mr-auto on buttons to put them on the left
3941  * @cfg {Boolean} animate default true
3942  * @cfg {Boolean} allow_close default true
3943  * @cfg {Boolean} fitwindow default false
3944  * @cfg {Number} width fixed width - usefull for chrome extension only really.
3945  * @cfg {Number} height fixed height - usefull for chrome extension only really.
3946  * @cfg {String} size (sm|lg|xl) default empty
3947  * @cfg {Number} max_width set the max width of modal
3948  * @cfg {Boolean} editableTitle can the title be edited
3949
3950  *
3951  *
3952  * @constructor
3953  * Create a new Modal Dialog
3954  * @param {Object} config The config object
3955  */
3956
3957 Roo.bootstrap.Modal = function(config){
3958     Roo.bootstrap.Modal.superclass.constructor.call(this, config);
3959     this.addEvents({
3960         // raw events
3961         /**
3962          * @event btnclick
3963          * The raw btnclick event for the button
3964          * @param {Roo.EventObject} e
3965          */
3966         "btnclick" : true,
3967         /**
3968          * @event resize
3969          * Fire when dialog resize
3970          * @param {Roo.bootstrap.Modal} this
3971          * @param {Roo.EventObject} e
3972          */
3973         "resize" : true,
3974         /**
3975          * @event titlechanged
3976          * Fire when the editable title has been changed
3977          * @param {Roo.bootstrap.Modal} this
3978          * @param {Roo.EventObject} value
3979          */
3980         "titlechanged" : true 
3981         
3982     });
3983     this.buttons = this.buttons || [];
3984
3985     if (this.tmpl) {
3986         this.tmpl = Roo.factory(this.tmpl);
3987     }
3988
3989 };
3990
3991 Roo.extend(Roo.bootstrap.Modal, Roo.bootstrap.Component,  {
3992
3993     title : 'test dialog',
3994
3995     buttons : false,
3996
3997     // set on load...
3998
3999     html: false,
4000
4001     tmp: false,
4002
4003     specificTitle: false,
4004
4005     buttonPosition: 'right',
4006
4007     allow_close : true,
4008
4009     animate : true,
4010
4011     fitwindow: false,
4012     
4013      // private
4014     dialogEl: false,
4015     bodyEl:  false,
4016     footerEl:  false,
4017     titleEl:  false,
4018     closeEl:  false,
4019
4020     size: '',
4021     
4022     max_width: 0,
4023     
4024     max_height: 0,
4025     
4026     fit_content: false,
4027     editableTitle  : false,
4028
4029     onRender : function(ct, position)
4030     {
4031         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
4032
4033         if(!this.el){
4034             var cfg = Roo.apply({},  this.getAutoCreate());
4035             cfg.id = Roo.id();
4036             //if(!cfg.name){
4037             //    cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
4038             //}
4039             //if (!cfg.name.length) {
4040             //    delete cfg.name;
4041            // }
4042             if (this.cls) {
4043                 cfg.cls += ' ' + this.cls;
4044             }
4045             if (this.style) {
4046                 cfg.style = this.style;
4047             }
4048             this.el = Roo.get(document.body).createChild(cfg, position);
4049         }
4050         //var type = this.el.dom.type;
4051
4052
4053         if(this.tabIndex !== undefined){
4054             this.el.dom.setAttribute('tabIndex', this.tabIndex);
4055         }
4056
4057         this.dialogEl = this.el.select('.modal-dialog',true).first();
4058         this.bodyEl = this.el.select('.modal-body',true).first();
4059         this.closeEl = this.el.select('.modal-header .close', true).first();
4060         this.headerEl = this.el.select('.modal-header',true).first();
4061         this.titleEl = this.el.select('.modal-title',true).first();
4062         this.footerEl = this.el.select('.modal-footer',true).first();
4063
4064         this.maskEl = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
4065         
4066         //this.el.addClass("x-dlg-modal");
4067
4068         if (this.buttons.length) {
4069             Roo.each(this.buttons, function(bb) {
4070                 var b = Roo.apply({}, bb);
4071                 b.xns = b.xns || Roo.bootstrap;
4072                 b.xtype = b.xtype || 'Button';
4073                 if (typeof(b.listeners) == 'undefined') {
4074                     b.listeners = { click : this.onButtonClick.createDelegate(this)  };
4075                 }
4076
4077                 var btn = Roo.factory(b);
4078
4079                 btn.render(this.getButtonContainer());
4080
4081             },this);
4082         }
4083         // render the children.
4084         var nitems = [];
4085
4086         if(typeof(this.items) != 'undefined'){
4087             var items = this.items;
4088             delete this.items;
4089
4090             for(var i =0;i < items.length;i++) {
4091                 nitems.push(this.addxtype(Roo.apply({}, items[i])));
4092             }
4093         }
4094
4095         this.items = nitems;
4096
4097         // where are these used - they used to be body/close/footer
4098
4099
4100         this.initEvents();
4101         //this.el.addClass([this.fieldClass, this.cls]);
4102
4103     },
4104
4105     getAutoCreate : function()
4106     {
4107         // we will default to modal-body-overflow - might need to remove or make optional later.
4108         var bdy = {
4109                 cls : 'modal-body enable-modal-body-overflow ', 
4110                 html : this.html || ''
4111         };
4112
4113         var title = {
4114             tag: 'h4',
4115             cls : 'modal-title',
4116             html : this.title
4117         };
4118
4119         if(this.specificTitle){ // WTF is this?
4120             title = this.title;
4121         }
4122
4123         var header = [];
4124         if (this.allow_close && Roo.bootstrap.version == 3) {
4125             header.push({
4126                 tag: 'button',
4127                 cls : 'close',
4128                 html : '&times'
4129             });
4130         }
4131
4132         header.push(title);
4133
4134         if (this.editableTitle) {
4135             header.push({
4136                 cls: 'form-control roo-editable-title d-none',
4137                 tag: 'input',
4138                 type: 'text'
4139             });
4140         }
4141         
4142         if (this.allow_close && Roo.bootstrap.version == 4) {
4143             header.push({
4144                 tag: 'button',
4145                 cls : 'close',
4146                 html : '&times'
4147             });
4148         }
4149         
4150         var size = '';
4151
4152         if(this.size.length){
4153             size = 'modal-' + this.size;
4154         }
4155         
4156         var footer = Roo.bootstrap.version == 3 ?
4157             {
4158                 cls : 'modal-footer',
4159                 cn : [
4160                     {
4161                         tag: 'div',
4162                         cls: 'btn-' + this.buttonPosition
4163                     }
4164                 ]
4165
4166             } :
4167             {  // BS4 uses mr-auto on left buttons....
4168                 cls : 'modal-footer'
4169             };
4170
4171             
4172
4173         
4174         
4175         var modal = {
4176             cls: "modal",
4177              cn : [
4178                 {
4179                     cls: "modal-dialog " + size,
4180                     cn : [
4181                         {
4182                             cls : "modal-content",
4183                             cn : [
4184                                 {
4185                                     cls : 'modal-header',
4186                                     cn : header
4187                                 },
4188                                 bdy,
4189                                 footer
4190                             ]
4191
4192                         }
4193                     ]
4194
4195                 }
4196             ]
4197         };
4198
4199         if(this.animate){
4200             modal.cls += ' fade';
4201         }
4202
4203         return modal;
4204
4205     },
4206     getChildContainer : function() {
4207
4208          return this.bodyEl;
4209
4210     },
4211     getButtonContainer : function() {
4212         
4213          return Roo.bootstrap.version == 4 ?
4214             this.el.select('.modal-footer',true).first()
4215             : this.el.select('.modal-footer div',true).first();
4216
4217     },
4218     initEvents : function()
4219     {
4220         if (this.allow_close) {
4221             this.closeEl.on('click', this.hide, this);
4222         }
4223         Roo.EventManager.onWindowResize(this.resize, this, true);
4224         if (this.editableTitle) {
4225             this.headerEditEl =  this.headerEl.select('.form-control',true).first();
4226             this.headerEl.on('click', function() { this.toggleHeaderInput(true) } , this);
4227             this.headerEditEl.on('keyup', function(e) {
4228                     if([  e.RETURN , e.TAB , e.ESC ].indexOf(e.keyCode) > -1) {
4229                         this.toggleHeaderInput(false)
4230                     }
4231                 }, this);
4232             this.headerEditEl.on('blur', function(e) {
4233                 this.toggleHeaderInput(false)
4234             },this);
4235         }
4236
4237     },
4238   
4239
4240     resize : function()
4241     {
4242         this.maskEl.setSize(
4243             Roo.lib.Dom.getViewWidth(true),
4244             Roo.lib.Dom.getViewHeight(true)
4245         );
4246         
4247         if (this.fitwindow) {
4248             
4249            
4250             this.setSize(
4251                 this.width || Roo.lib.Dom.getViewportWidth(true) - 30,
4252                 this.height || Roo.lib.Dom.getViewportHeight(true) // catering margin-top 30 margin-bottom 30
4253             );
4254             return;
4255         }
4256         
4257         if(this.max_width !== 0) {
4258             
4259             var w = Math.min(this.max_width, Roo.lib.Dom.getViewportWidth(true) - 30);
4260             
4261             if(this.height) {
4262                 this.setSize(w, this.height);
4263                 return;
4264             }
4265             
4266             if(this.max_height) {
4267                 this.setSize(w,Math.min(
4268                     this.max_height,
4269                     Roo.lib.Dom.getViewportHeight(true) - 60
4270                 ));
4271                 
4272                 return;
4273             }
4274             
4275             if(!this.fit_content) {
4276                 this.setSize(w, Roo.lib.Dom.getViewportHeight(true) - 60);
4277                 return;
4278             }
4279             
4280             this.setSize(w, Math.min(
4281                 60 +
4282                 this.headerEl.getHeight() + 
4283                 this.footerEl.getHeight() + 
4284                 this.getChildHeight(this.bodyEl.dom.childNodes),
4285                 Roo.lib.Dom.getViewportHeight(true) - 60)
4286             );
4287         }
4288         
4289     },
4290
4291     setSize : function(w,h)
4292     {
4293         if (!w && !h) {
4294             return;
4295         }
4296         
4297         this.resizeTo(w,h);
4298     },
4299
4300     show : function() {
4301
4302         if (!this.rendered) {
4303             this.render();
4304         }
4305
4306         //this.el.setStyle('display', 'block');
4307         this.el.removeClass('hideing');
4308         this.el.dom.style.display='block';
4309         
4310         Roo.get(document.body).addClass('modal-open');
4311  
4312         if(this.animate){  // element has 'fade'  - so stuff happens after .3s ?- not sure why the delay?
4313             
4314             (function(){
4315                 this.el.addClass('show');
4316                 this.el.addClass('in');
4317             }).defer(50, this);
4318         }else{
4319             this.el.addClass('show');
4320             this.el.addClass('in');
4321         }
4322
4323         // not sure how we can show data in here..
4324         //if (this.tmpl) {
4325         //    this.getChildContainer().dom.innerHTML = this.tmpl.applyTemplate(this);
4326         //}
4327
4328         Roo.get(document.body).addClass("x-body-masked");
4329         
4330         this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true),   Roo.lib.Dom.getViewHeight(true));
4331         this.maskEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
4332         this.maskEl.dom.style.display = 'block';
4333         this.maskEl.addClass('show');
4334         
4335         
4336         this.resize();
4337         
4338         this.fireEvent('show', this);
4339
4340         // set zindex here - otherwise it appears to be ignored...
4341         this.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
4342
4343         (function () {
4344             this.items.forEach( function(e) {
4345                 e.layout ? e.layout() : false;
4346
4347             });
4348         }).defer(100,this);
4349
4350     },
4351     hide : function()
4352     {
4353         if(this.fireEvent("beforehide", this) !== false){
4354             
4355             this.maskEl.removeClass('show');
4356             
4357             this.maskEl.dom.style.display = '';
4358             Roo.get(document.body).removeClass("x-body-masked");
4359             this.el.removeClass('in');
4360             this.el.select('.modal-dialog', true).first().setStyle('transform','');
4361
4362             if(this.animate){ // why
4363                 this.el.addClass('hideing');
4364                 this.el.removeClass('show');
4365                 (function(){
4366                     if (!this.el.hasClass('hideing')) {
4367                         return; // it's been shown again...
4368                     }
4369                     
4370                     this.el.dom.style.display='';
4371
4372                     Roo.get(document.body).removeClass('modal-open');
4373                     this.el.removeClass('hideing');
4374                 }).defer(150,this);
4375                 
4376             }else{
4377                 this.el.removeClass('show');
4378                 this.el.dom.style.display='';
4379                 Roo.get(document.body).removeClass('modal-open');
4380
4381             }
4382             this.fireEvent('hide', this);
4383         }
4384     },
4385     isVisible : function()
4386     {
4387         
4388         return this.el.hasClass('show') && !this.el.hasClass('hideing');
4389         
4390     },
4391
4392     addButton : function(str, cb)
4393     {
4394
4395
4396         var b = Roo.apply({}, { html : str } );
4397         b.xns = b.xns || Roo.bootstrap;
4398         b.xtype = b.xtype || 'Button';
4399         if (typeof(b.listeners) == 'undefined') {
4400             b.listeners = { click : cb.createDelegate(this)  };
4401         }
4402
4403         var btn = Roo.factory(b);
4404
4405         btn.render(this.getButtonContainer());
4406
4407         return btn;
4408
4409     },
4410
4411     setDefaultButton : function(btn)
4412     {
4413         //this.el.select('.modal-footer').()
4414     },
4415
4416     resizeTo: function(w,h)
4417     {
4418         this.dialogEl.setWidth(w);
4419         
4420         var diff = this.headerEl.getHeight() + this.footerEl.getHeight() + 60; // dialog margin-bottom: 30  
4421
4422         this.bodyEl.setHeight(h - diff);
4423         
4424         this.fireEvent('resize', this);
4425     },
4426     
4427     setContentSize  : function(w, h)
4428     {
4429
4430     },
4431     onButtonClick: function(btn,e)
4432     {
4433         //Roo.log([a,b,c]);
4434         this.fireEvent('btnclick', btn.name, e);
4435     },
4436      /**
4437      * Set the title of the Dialog
4438      * @param {String} str new Title
4439      */
4440     setTitle: function(str) {
4441         this.titleEl.dom.innerHTML = str;
4442         this.title = str;
4443     },
4444     /**
4445      * Set the body of the Dialog
4446      * @param {String} str new Title
4447      */
4448     setBody: function(str) {
4449         this.bodyEl.dom.innerHTML = str;
4450     },
4451     /**
4452      * Set the body of the Dialog using the template
4453      * @param {Obj} data - apply this data to the template and replace the body contents.
4454      */
4455     applyBody: function(obj)
4456     {
4457         if (!this.tmpl) {
4458             Roo.log("Error - using apply Body without a template");
4459             //code
4460         }
4461         this.tmpl.overwrite(this.bodyEl, obj);
4462     },
4463     
4464     getChildHeight : function(child_nodes)
4465     {
4466         if(
4467             !child_nodes ||
4468             child_nodes.length == 0
4469         ) {
4470             return 0;
4471         }
4472         
4473         var child_height = 0;
4474         
4475         for(var i = 0; i < child_nodes.length; i++) {
4476             
4477             /*
4478             * for modal with tabs...
4479             if(child_nodes[i].classList.contains('roo-layout-panel')) {
4480                 
4481                 var layout_childs = child_nodes[i].childNodes;
4482                 
4483                 for(var j = 0; j < layout_childs.length; j++) {
4484                     
4485                     if(layout_childs[j].classList.contains('roo-layout-panel-body')) {
4486                         
4487                         var layout_body_childs = layout_childs[j].childNodes;
4488                         
4489                         for(var k = 0; k < layout_body_childs.length; k++) {
4490                             
4491                             if(layout_body_childs[k].classList.contains('navbar')) {
4492                                 child_height += layout_body_childs[k].offsetHeight;
4493                                 continue;
4494                             }
4495                             
4496                             if(layout_body_childs[k].classList.contains('roo-layout-tabs-body')) {
4497                                 
4498                                 var layout_body_tab_childs = layout_body_childs[k].childNodes;
4499                                 
4500                                 for(var m = 0; m < layout_body_tab_childs.length; m++) {
4501                                     
4502                                     if(layout_body_tab_childs[m].classList.contains('roo-layout-active-content')) {
4503                                         child_height += this.getChildHeight(layout_body_tab_childs[m].childNodes);
4504                                         continue;
4505                                     }
4506                                     
4507                                 }
4508                                 
4509                             }
4510                             
4511                         }
4512                     }
4513                 }
4514                 continue;
4515             }
4516             */
4517             
4518             child_height += child_nodes[i].offsetHeight;
4519             // Roo.log(child_nodes[i].offsetHeight);
4520         }
4521         
4522         return child_height;
4523     },
4524     toggleHeaderInput : function(is_edit)
4525     {
4526         
4527         if (is_edit && this.is_header_editing) {
4528             return; // already editing..
4529         }
4530         if (is_edit) {
4531     
4532             this.headerEditEl.dom.value = this.title;
4533             this.headerEditEl.removeClass('d-none');
4534             this.headerEditEl.dom.focus();
4535             this.titleEl.addClass('d-none');
4536             
4537             this.is_header_editing = true;
4538             return
4539         }
4540         // flip back to not editing.
4541         this.title = this.headerEditEl.dom.value;
4542         this.headerEditEl.addClass('d-none');
4543         this.titleEl.removeClass('d-none');
4544         this.titleEl.dom.innerHTML = String.format('{0}', this.title);
4545         this.is_header_editing = false;
4546         this.fireEvent('titlechanged', this, this.title);
4547     
4548             
4549         
4550     }
4551
4552 });
4553
4554
4555 Roo.apply(Roo.bootstrap.Modal,  {
4556     /**
4557          * Button config that displays a single OK button
4558          * @type Object
4559          */
4560         OK :  [{
4561             name : 'ok',
4562             weight : 'primary',
4563             html : 'OK'
4564         }],
4565         /**
4566          * Button config that displays Yes and No buttons
4567          * @type Object
4568          */
4569         YESNO : [
4570             {
4571                 name  : 'no',
4572                 html : 'No'
4573             },
4574             {
4575                 name  :'yes',
4576                 weight : 'primary',
4577                 html : 'Yes'
4578             }
4579         ],
4580
4581         /**
4582          * Button config that displays OK and Cancel buttons
4583          * @type Object
4584          */
4585         OKCANCEL : [
4586             {
4587                name : 'cancel',
4588                 html : 'Cancel'
4589             },
4590             {
4591                 name : 'ok',
4592                 weight : 'primary',
4593                 html : 'OK'
4594             }
4595         ],
4596         /**
4597          * Button config that displays Yes, No and Cancel buttons
4598          * @type Object
4599          */
4600         YESNOCANCEL : [
4601             {
4602                 name : 'yes',
4603                 weight : 'primary',
4604                 html : 'Yes'
4605             },
4606             {
4607                 name : 'no',
4608                 html : 'No'
4609             },
4610             {
4611                 name : 'cancel',
4612                 html : 'Cancel'
4613             }
4614         ],
4615         
4616         zIndex : 10001
4617 });
4618
4619 /*
4620  * - LGPL
4621  *
4622  * messagebox - can be used as a replace
4623  * 
4624  */
4625 /**
4626  * @class Roo.MessageBox
4627  * Utility class for generating different styles of message boxes.  The alias Roo.Msg can also be used.
4628  * Example usage:
4629  *<pre><code>
4630 // Basic alert:
4631 Roo.Msg.alert('Status', 'Changes saved successfully.');
4632
4633 // Prompt for user data:
4634 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
4635     if (btn == 'ok'){
4636         // process text value...
4637     }
4638 });
4639
4640 // Show a dialog using config options:
4641 Roo.Msg.show({
4642    title:'Save Changes?',
4643    msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
4644    buttons: Roo.Msg.YESNOCANCEL,
4645    fn: processResult,
4646    animEl: 'elId'
4647 });
4648 </code></pre>
4649  * @singleton
4650  */
4651 Roo.bootstrap.MessageBox = function(){
4652     var dlg, opt, mask, waitTimer;
4653     var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
4654     var buttons, activeTextEl, bwidth;
4655
4656     
4657     // private
4658     var handleButton = function(button){
4659         dlg.hide();
4660         Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
4661     };
4662
4663     // private
4664     var handleHide = function(){
4665         if(opt && opt.cls){
4666             dlg.el.removeClass(opt.cls);
4667         }
4668         //if(waitTimer){
4669         //    Roo.TaskMgr.stop(waitTimer);
4670         //    waitTimer = null;
4671         //}
4672     };
4673
4674     // private
4675     var updateButtons = function(b){
4676         var width = 0;
4677         if(!b){
4678             buttons["ok"].hide();
4679             buttons["cancel"].hide();
4680             buttons["yes"].hide();
4681             buttons["no"].hide();
4682             dlg.footerEl.hide();
4683             
4684             return width;
4685         }
4686         dlg.footerEl.show();
4687         for(var k in buttons){
4688             if(typeof buttons[k] != "function"){
4689                 if(b[k]){
4690                     buttons[k].show();
4691                     buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.bootstrap.MessageBox.buttonText[k]);
4692                     width += buttons[k].el.getWidth()+15;
4693                 }else{
4694                     buttons[k].hide();
4695                 }
4696             }
4697         }
4698         return width;
4699     };
4700
4701     // private
4702     var handleEsc = function(d, k, e){
4703         if(opt && opt.closable !== false){
4704             dlg.hide();
4705         }
4706         if(e){
4707             e.stopEvent();
4708         }
4709     };
4710
4711     return {
4712         /**
4713          * Returns a reference to the underlying {@link Roo.BasicDialog} element
4714          * @return {Roo.BasicDialog} The BasicDialog element
4715          */
4716         getDialog : function(){
4717            if(!dlg){
4718                 dlg = new Roo.bootstrap.Modal( {
4719                     //draggable: true,
4720                     //resizable:false,
4721                     //constraintoviewport:false,
4722                     //fixedcenter:true,
4723                     //collapsible : false,
4724                     //shim:true,
4725                     //modal: true,
4726                 //    width: 'auto',
4727                   //  height:100,
4728                     //buttonAlign:"center",
4729                     closeClick : function(){
4730                         if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
4731                             handleButton("no");
4732                         }else{
4733                             handleButton("cancel");
4734                         }
4735                     }
4736                 });
4737                 dlg.render();
4738                 dlg.on("hide", handleHide);
4739                 mask = dlg.mask;
4740                 //dlg.addKeyListener(27, handleEsc);
4741                 buttons = {};
4742                 this.buttons = buttons;
4743                 var bt = this.buttonText;
4744                 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
4745                 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
4746                 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
4747                 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
4748                 //Roo.log(buttons);
4749                 bodyEl = dlg.bodyEl.createChild({
4750
4751                     html:'<span class="roo-mb-text"></span><br /><input type="text" class="roo-mb-input" />' +
4752                         '<textarea class="roo-mb-textarea"></textarea>' +
4753                         '<div class="roo-mb-progress-wrap"><div class="roo-mb-progress"><div class="roo-mb-progress-bar">&#160;</div></div></div>'
4754                 });
4755                 msgEl = bodyEl.dom.firstChild;
4756                 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
4757                 textboxEl.enableDisplayMode();
4758                 textboxEl.addKeyListener([10,13], function(){
4759                     if(dlg.isVisible() && opt && opt.buttons){
4760                         if(opt.buttons.ok){
4761                             handleButton("ok");
4762                         }else if(opt.buttons.yes){
4763                             handleButton("yes");
4764                         }
4765                     }
4766                 });
4767                 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
4768                 textareaEl.enableDisplayMode();
4769                 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
4770                 progressEl.enableDisplayMode();
4771                 
4772                 // This is supposed to be the progessElement.. but I think it's controlling the height of everything..
4773                 var pf = progressEl.dom.firstChild;
4774                 if (pf) {
4775                     pp = Roo.get(pf.firstChild);
4776                     pp.setHeight(pf.offsetHeight);
4777                 }
4778                 
4779             }
4780             return dlg;
4781         },
4782
4783         /**
4784          * Updates the message box body text
4785          * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
4786          * the XHTML-compliant non-breaking space character '&amp;#160;')
4787          * @return {Roo.MessageBox} This message box
4788          */
4789         updateText : function(text)
4790         {
4791             if(!dlg.isVisible() && !opt.width){
4792                 dlg.dialogEl.setStyle({ 'max-width' : this.maxWidth});
4793                 // dlg.resizeTo(this.maxWidth, 100); // forcing the height breaks long alerts()
4794             }
4795             msgEl.innerHTML = text || '&#160;';
4796       
4797             var cw =  Math.max(msgEl.offsetWidth, msgEl.parentNode.scrollWidth);
4798             //Roo.log("guesed size: " + JSON.stringify([cw,msgEl.offsetWidth, msgEl.parentNode.scrollWidth]));
4799             var w = Math.max(
4800                     Math.min(opt.width || cw , this.maxWidth), 
4801                     Math.max(opt.minWidth || this.minWidth, bwidth)
4802             );
4803             if(opt.prompt){
4804                 activeTextEl.setWidth(w);
4805             }
4806             if(dlg.isVisible()){
4807                 dlg.fixedcenter = false;
4808             }
4809             // to big, make it scroll. = But as usual stupid IE does not support
4810             // !important..
4811             
4812             if ( bodyEl.getHeight() > (Roo.lib.Dom.getViewHeight() - 100)) {
4813                 bodyEl.setHeight ( Roo.lib.Dom.getViewHeight() - 100 );
4814                 bodyEl.dom.style.overflowY = 'auto' + ( Roo.isIE ? '' : ' !important');
4815             } else {
4816                 bodyEl.dom.style.height = '';
4817                 bodyEl.dom.style.overflowY = '';
4818             }
4819             if (cw > w) {
4820                 bodyEl.dom.style.get = 'auto' + ( Roo.isIE ? '' : ' !important');
4821             } else {
4822                 bodyEl.dom.style.overflowX = '';
4823             }
4824             
4825             dlg.setContentSize(w, bodyEl.getHeight());
4826             if(dlg.isVisible()){
4827                 dlg.fixedcenter = true;
4828             }
4829             return this;
4830         },
4831
4832         /**
4833          * Updates a progress-style message box's text and progress bar.  Only relevant on message boxes
4834          * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
4835          * @param {Number} value Any number between 0 and 1 (e.g., .5)
4836          * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
4837          * @return {Roo.MessageBox} This message box
4838          */
4839         updateProgress : function(value, text){
4840             if(text){
4841                 this.updateText(text);
4842             }
4843             
4844             if (pp) { // weird bug on my firefox - for some reason this is not defined
4845                 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
4846                 pp.setHeight(Math.floor(progressEl.dom.firstChild.offsetHeight));
4847             }
4848             return this;
4849         },        
4850
4851         /**
4852          * Returns true if the message box is currently displayed
4853          * @return {Boolean} True if the message box is visible, else false
4854          */
4855         isVisible : function(){
4856             return dlg && dlg.isVisible();  
4857         },
4858
4859         /**
4860          * Hides the message box if it is displayed
4861          */
4862         hide : function(){
4863             if(this.isVisible()){
4864                 dlg.hide();
4865             }  
4866         },
4867
4868         /**
4869          * Displays a new message box, or reinitializes an existing message box, based on the config options
4870          * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
4871          * The following config object properties are supported:
4872          * <pre>
4873 Property    Type             Description
4874 ----------  ---------------  ------------------------------------------------------------------------------------
4875 animEl            String/Element   An id or Element from which the message box should animate as it opens and
4876                                    closes (defaults to undefined)
4877 buttons           Object/Boolean   A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
4878                                    cancel:'Bar'}), or false to not show any buttons (defaults to false)
4879 closable          Boolean          False to hide the top-right close button (defaults to true).  Note that
4880                                    progress and wait dialogs will ignore this property and always hide the
4881                                    close button as they can only be closed programmatically.
4882 cls               String           A custom CSS class to apply to the message box element
4883 defaultTextHeight Number           The default height in pixels of the message box's multiline textarea if
4884                                    displayed (defaults to 75)
4885 fn                Function         A callback function to execute after closing the dialog.  The arguments to the
4886                                    function will be btn (the name of the button that was clicked, if applicable,
4887                                    e.g. "ok"), and text (the value of the active text field, if applicable).
4888                                    Progress and wait dialogs will ignore this option since they do not respond to
4889                                    user actions and can only be closed programmatically, so any required function
4890                                    should be called by the same code after it closes the dialog.
4891 icon              String           A CSS class that provides a background image to be used as an icon for
4892                                    the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
4893 maxWidth          Number           The maximum width in pixels of the message box (defaults to 600)
4894 minWidth          Number           The minimum width in pixels of the message box (defaults to 100)
4895 modal             Boolean          False to allow user interaction with the page while the message box is
4896                                    displayed (defaults to true)
4897 msg               String           A string that will replace the existing message box body text (defaults
4898                                    to the XHTML-compliant non-breaking space character '&#160;')
4899 multiline         Boolean          True to prompt the user to enter multi-line text (defaults to false)
4900 progress          Boolean          True to display a progress bar (defaults to false)
4901 progressText      String           The text to display inside the progress bar if progress = true (defaults to '')
4902 prompt            Boolean          True to prompt the user to enter single-line text (defaults to false)
4903 proxyDrag         Boolean          True to display a lightweight proxy while dragging (defaults to false)
4904 title             String           The title text
4905 value             String           The string value to set into the active textbox element if displayed
4906 wait              Boolean          True to display a progress bar (defaults to false)
4907 width             Number           The width of the dialog in pixels
4908 </pre>
4909          *
4910          * Example usage:
4911          * <pre><code>
4912 Roo.Msg.show({
4913    title: 'Address',
4914    msg: 'Please enter your address:',
4915    width: 300,
4916    buttons: Roo.MessageBox.OKCANCEL,
4917    multiline: true,
4918    fn: saveAddress,
4919    animEl: 'addAddressBtn'
4920 });
4921 </code></pre>
4922          * @param {Object} config Configuration options
4923          * @return {Roo.MessageBox} This message box
4924          */
4925         show : function(options)
4926         {
4927             
4928             // this causes nightmares if you show one dialog after another
4929             // especially on callbacks..
4930              
4931             if(this.isVisible()){
4932                 
4933                 this.hide();
4934                 Roo.log("[Roo.Messagebox] Show called while message displayed:" );
4935                 Roo.log("Old Dialog Message:" +  msgEl.innerHTML );
4936                 Roo.log("New Dialog Message:" +  options.msg )
4937                 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
4938                 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
4939                 
4940             }
4941             var d = this.getDialog();
4942             opt = options;
4943             d.setTitle(opt.title || "&#160;");
4944             d.closeEl.setDisplayed(opt.closable !== false);
4945             activeTextEl = textboxEl;
4946             opt.prompt = opt.prompt || (opt.multiline ? true : false);
4947             if(opt.prompt){
4948                 if(opt.multiline){
4949                     textboxEl.hide();
4950                     textareaEl.show();
4951                     textareaEl.setHeight(typeof opt.multiline == "number" ?
4952                         opt.multiline : this.defaultTextHeight);
4953                     activeTextEl = textareaEl;
4954                 }else{
4955                     textboxEl.show();
4956                     textareaEl.hide();
4957                 }
4958             }else{
4959                 textboxEl.hide();
4960                 textareaEl.hide();
4961             }
4962             progressEl.setDisplayed(opt.progress === true);
4963             if (opt.progress) {
4964                 d.animate = false; // do not animate progress, as it may not have finished animating before we close it..
4965             }
4966             this.updateProgress(0);
4967             activeTextEl.dom.value = opt.value || "";
4968             if(opt.prompt){
4969                 dlg.setDefaultButton(activeTextEl);
4970             }else{
4971                 var bs = opt.buttons;
4972                 var db = null;
4973                 if(bs && bs.ok){
4974                     db = buttons["ok"];
4975                 }else if(bs && bs.yes){
4976                     db = buttons["yes"];
4977                 }
4978                 dlg.setDefaultButton(db);
4979             }
4980             bwidth = updateButtons(opt.buttons);
4981             this.updateText(opt.msg);
4982             if(opt.cls){
4983                 d.el.addClass(opt.cls);
4984             }
4985             d.proxyDrag = opt.proxyDrag === true;
4986             d.modal = opt.modal !== false;
4987             d.mask = opt.modal !== false ? mask : false;
4988             if(!d.isVisible()){
4989                 // force it to the end of the z-index stack so it gets a cursor in FF
4990                 document.body.appendChild(dlg.el.dom);
4991                 d.animateTarget = null;
4992                 d.show(options.animEl);
4993             }
4994             return this;
4995         },
4996
4997         /**
4998          * Displays a message box with a progress bar.  This message box has no buttons and is not closeable by
4999          * the user.  You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
5000          * and closing the message box when the process is complete.
5001          * @param {String} title The title bar text
5002          * @param {String} msg The message box body text
5003          * @return {Roo.MessageBox} This message box
5004          */
5005         progress : function(title, msg){
5006             this.show({
5007                 title : title,
5008                 msg : msg,
5009                 buttons: false,
5010                 progress:true,
5011                 closable:false,
5012                 minWidth: this.minProgressWidth,
5013                 modal : true
5014             });
5015             return this;
5016         },
5017
5018         /**
5019          * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
5020          * If a callback function is passed it will be called after the user clicks the button, and the
5021          * id of the button that was clicked will be passed as the only parameter to the callback
5022          * (could also be the top-right close button).
5023          * @param {String} title The title bar text
5024          * @param {String} msg The message box body text
5025          * @param {Function} fn (optional) The callback function invoked after the message box is closed
5026          * @param {Object} scope (optional) The scope of the callback function
5027          * @return {Roo.MessageBox} This message box
5028          */
5029         alert : function(title, msg, fn, scope)
5030         {
5031             this.show({
5032                 title : title,
5033                 msg : msg,
5034                 buttons: this.OK,
5035                 fn: fn,
5036                 closable : false,
5037                 scope : scope,
5038                 modal : true
5039             });
5040             return this;
5041         },
5042
5043         /**
5044          * Displays a message box with an infinitely auto-updating progress bar.  This can be used to block user
5045          * interaction while waiting for a long-running process to complete that does not have defined intervals.
5046          * You are responsible for closing the message box when the process is complete.
5047          * @param {String} msg The message box body text
5048          * @param {String} title (optional) The title bar text
5049          * @return {Roo.MessageBox} This message box
5050          */
5051         wait : function(msg, title){
5052             this.show({
5053                 title : title,
5054                 msg : msg,
5055                 buttons: false,
5056                 closable:false,
5057                 progress:true,
5058                 modal:true,
5059                 width:300,
5060                 wait:true
5061             });
5062             waitTimer = Roo.TaskMgr.start({
5063                 run: function(i){
5064                     Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
5065                 },
5066                 interval: 1000
5067             });
5068             return this;
5069         },
5070
5071         /**
5072          * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
5073          * If a callback function is passed it will be called after the user clicks either button, and the id of the
5074          * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
5075          * @param {String} title The title bar text
5076          * @param {String} msg The message box body text
5077          * @param {Function} fn (optional) The callback function invoked after the message box is closed
5078          * @param {Object} scope (optional) The scope of the callback function
5079          * @return {Roo.MessageBox} This message box
5080          */
5081         confirm : function(title, msg, fn, scope){
5082             this.show({
5083                 title : title,
5084                 msg : msg,
5085                 buttons: this.YESNO,
5086                 fn: fn,
5087                 scope : scope,
5088                 modal : true
5089             });
5090             return this;
5091         },
5092
5093         /**
5094          * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
5095          * JavaScript's Window.prompt).  The prompt can be a single-line or multi-line textbox.  If a callback function
5096          * is passed it will be called after the user clicks either button, and the id of the button that was clicked
5097          * (could also be the top-right close button) and the text that was entered will be passed as the two
5098          * parameters to the callback.
5099          * @param {String} title The title bar text
5100          * @param {String} msg The message box body text
5101          * @param {Function} fn (optional) The callback function invoked after the message box is closed
5102          * @param {Object} scope (optional) The scope of the callback function
5103          * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
5104          * property, or the height in pixels to create the textbox (defaults to false / single-line)
5105          * @return {Roo.MessageBox} This message box
5106          */
5107         prompt : function(title, msg, fn, scope, multiline){
5108             this.show({
5109                 title : title,
5110                 msg : msg,
5111                 buttons: this.OKCANCEL,
5112                 fn: fn,
5113                 minWidth:250,
5114                 scope : scope,
5115                 prompt:true,
5116                 multiline: multiline,
5117                 modal : true
5118             });
5119             return this;
5120         },
5121
5122         /**
5123          * Button config that displays a single OK button
5124          * @type Object
5125          */
5126         OK : {ok:true},
5127         /**
5128          * Button config that displays Yes and No buttons
5129          * @type Object
5130          */
5131         YESNO : {yes:true, no:true},
5132         /**
5133          * Button config that displays OK and Cancel buttons
5134          * @type Object
5135          */
5136         OKCANCEL : {ok:true, cancel:true},
5137         /**
5138          * Button config that displays Yes, No and Cancel buttons
5139          * @type Object
5140          */
5141         YESNOCANCEL : {yes:true, no:true, cancel:true},
5142
5143         /**
5144          * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
5145          * @type Number
5146          */
5147         defaultTextHeight : 75,
5148         /**
5149          * The maximum width in pixels of the message box (defaults to 600)
5150          * @type Number
5151          */
5152         maxWidth : 600,
5153         /**
5154          * The minimum width in pixels of the message box (defaults to 100)
5155          * @type Number
5156          */
5157         minWidth : 100,
5158         /**
5159          * The minimum width in pixels of the message box if it is a progress-style dialog.  This is useful
5160          * for setting a different minimum width than text-only dialogs may need (defaults to 250)
5161          * @type Number
5162          */
5163         minProgressWidth : 250,
5164         /**
5165          * An object containing the default button text strings that can be overriden for localized language support.
5166          * Supported properties are: ok, cancel, yes and no.
5167          * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
5168          * @type Object
5169          */
5170         buttonText : {
5171             ok : "OK",
5172             cancel : "Cancel",
5173             yes : "Yes",
5174             no : "No"
5175         }
5176     };
5177 }();
5178
5179 /**
5180  * Shorthand for {@link Roo.MessageBox}
5181  */
5182 Roo.MessageBox = Roo.MessageBox || Roo.bootstrap.MessageBox;
5183 Roo.Msg = Roo.Msg || Roo.MessageBox;
5184 /*
5185  * - LGPL
5186  *
5187  * navbar
5188  * 
5189  */
5190
5191 /**
5192  * @class Roo.bootstrap.Navbar
5193  * @extends Roo.bootstrap.Component
5194  * Bootstrap Navbar class
5195
5196  * @constructor
5197  * Create a new Navbar
5198  * @param {Object} config The config object
5199  */
5200
5201
5202 Roo.bootstrap.Navbar = function(config){
5203     Roo.bootstrap.Navbar.superclass.constructor.call(this, config);
5204     this.addEvents({
5205         // raw events
5206         /**
5207          * @event beforetoggle
5208          * Fire before toggle the menu
5209          * @param {Roo.EventObject} e
5210          */
5211         "beforetoggle" : true
5212     });
5213 };
5214
5215 Roo.extend(Roo.bootstrap.Navbar, Roo.bootstrap.Component,  {
5216     
5217     
5218    
5219     // private
5220     navItems : false,
5221     loadMask : false,
5222     
5223     
5224     getAutoCreate : function(){
5225         
5226         
5227         throw { message : "nav bar is now a abstract base class - use NavSimplebar / NavHeaderbar / NavSidebar etc..."};
5228         
5229     },
5230     
5231     initEvents :function ()
5232     {
5233         //Roo.log(this.el.select('.navbar-toggle',true));
5234         this.el.select('.navbar-toggle',true).on('click', this.onToggle , this);
5235         
5236         var mark = {
5237             tag: "div",
5238             cls:"x-dlg-mask"
5239         };
5240         
5241         this.maskEl = Roo.DomHelper.append(this.el, mark, true);
5242         
5243         var size = this.el.getSize();
5244         this.maskEl.setSize(size.width, size.height);
5245         this.maskEl.enableDisplayMode("block");
5246         this.maskEl.hide();
5247         
5248         if(this.loadMask){
5249             this.maskEl.show();
5250         }
5251     },
5252     
5253     
5254     getChildContainer : function()
5255     {
5256         if (this.el && this.el.select('.collapse').getCount()) {
5257             return this.el.select('.collapse',true).first();
5258         }
5259         
5260         return this.el;
5261     },
5262     
5263     mask : function()
5264     {
5265         this.maskEl.show();
5266     },
5267     
5268     unmask : function()
5269     {
5270         this.maskEl.hide();
5271     },
5272     onToggle : function()
5273     {
5274         
5275         if(this.fireEvent('beforetoggle', this) === false){
5276             return;
5277         }
5278         var ce = this.el.select('.navbar-collapse',true).first();
5279       
5280         if (!ce.hasClass('show')) {
5281            this.expand();
5282         } else {
5283             this.collapse();
5284         }
5285         
5286         
5287     
5288     },
5289     /**
5290      * Expand the navbar pulldown 
5291      */
5292     expand : function ()
5293     {
5294        
5295         var ce = this.el.select('.navbar-collapse',true).first();
5296         if (ce.hasClass('collapsing')) {
5297             return;
5298         }
5299         ce.dom.style.height = '';
5300                // show it...
5301         ce.addClass('in'); // old...
5302         ce.removeClass('collapse');
5303         ce.addClass('show');
5304         var h = ce.getHeight();
5305         Roo.log(h);
5306         ce.removeClass('show');
5307         // at this point we should be able to see it..
5308         ce.addClass('collapsing');
5309         
5310         ce.setHeight(0); // resize it ...
5311         ce.on('transitionend', function() {
5312             //Roo.log('done transition');
5313             ce.removeClass('collapsing');
5314             ce.addClass('show');
5315             ce.removeClass('collapse');
5316
5317             ce.dom.style.height = '';
5318         }, this, { single: true} );
5319         ce.setHeight(h);
5320         ce.dom.scrollTop = 0;
5321     },
5322     /**
5323      * Collapse the navbar pulldown 
5324      */
5325     collapse : function()
5326     {
5327          var ce = this.el.select('.navbar-collapse',true).first();
5328        
5329         if (ce.hasClass('collapsing') || ce.hasClass('collapse') ) {
5330             // it's collapsed or collapsing..
5331             return;
5332         }
5333         ce.removeClass('in'); // old...
5334         ce.setHeight(ce.getHeight());
5335         ce.removeClass('show');
5336         ce.addClass('collapsing');
5337         
5338         ce.on('transitionend', function() {
5339             ce.dom.style.height = '';
5340             ce.removeClass('collapsing');
5341             ce.addClass('collapse');
5342         }, this, { single: true} );
5343         ce.setHeight(0);
5344     }
5345     
5346     
5347     
5348 });
5349
5350
5351
5352  
5353
5354  /*
5355  * - LGPL
5356  *
5357  * navbar
5358  * 
5359  */
5360
5361 /**
5362  * @class Roo.bootstrap.NavSimplebar
5363  * @extends Roo.bootstrap.Navbar
5364  * Bootstrap Sidebar class
5365  *
5366  * @cfg {Boolean} inverse is inverted color
5367  * 
5368  * @cfg {String} type (nav | pills | tabs)
5369  * @cfg {Boolean} arrangement stacked | justified
5370  * @cfg {String} align (left | right) alignment
5371  * 
5372  * @cfg {Boolean} main (true|false) main nav bar? default false
5373  * @cfg {Boolean} loadMask (true|false) loadMask on the bar
5374  * 
5375  * @cfg {String} tag (header|footer|nav|div) default is nav 
5376
5377  * @cfg {String} weight (light|primary|secondary|success|danger|warning|info|dark|white) default is light.
5378  * 
5379  * 
5380  * @constructor
5381  * Create a new Sidebar
5382  * @param {Object} config The config object
5383  */
5384
5385
5386 Roo.bootstrap.NavSimplebar = function(config){
5387     Roo.bootstrap.NavSimplebar.superclass.constructor.call(this, config);
5388 };
5389
5390 Roo.extend(Roo.bootstrap.NavSimplebar, Roo.bootstrap.Navbar,  {
5391     
5392     inverse: false,
5393     
5394     type: false,
5395     arrangement: '',
5396     align : false,
5397     
5398     weight : 'light',
5399     
5400     main : false,
5401     
5402     
5403     tag : false,
5404     
5405     
5406     getAutoCreate : function(){
5407         
5408         
5409         var cfg = {
5410             tag : this.tag || 'div',
5411             cls : 'navbar roo-navbar-simple' //navbar-expand-lg ??
5412         };
5413         if (['light','white'].indexOf(this.weight) > -1) {
5414             cfg.cls += ['light','white'].indexOf(this.weight) > -1 ? ' navbar-light' : ' navbar-dark';
5415         }
5416         cfg.cls += ' bg-' + this.weight;
5417         
5418         if (this.inverse) {
5419             cfg.cls += ' navbar-inverse';
5420             
5421         }
5422         
5423         // i'm not actually sure these are really used - normally we add a navGroup to a navbar
5424         
5425         if (Roo.bootstrap.version == 4 && this.xtype == 'NavSimplebar') {
5426             return cfg;
5427         }
5428         
5429         
5430     
5431         
5432         cfg.cn = [
5433             {
5434                 cls: 'nav nav-' + this.xtype,
5435                 tag : 'ul'
5436             }
5437         ];
5438         
5439          
5440         this.type = this.type || 'nav';
5441         if (['tabs','pills'].indexOf(this.type) != -1) {
5442             cfg.cn[0].cls += ' nav-' + this.type
5443         
5444         
5445         } else {
5446             if (this.type!=='nav') {
5447                 Roo.log('nav type must be nav/tabs/pills')
5448             }
5449             cfg.cn[0].cls += ' navbar-nav'
5450         }
5451         
5452         
5453         
5454         
5455         if (['stacked','justified'].indexOf(this.arrangement) != -1) {
5456             cfg.cn[0].cls += ' nav-' + this.arrangement;
5457         }
5458         
5459         
5460         if (this.align === 'right') {
5461             cfg.cn[0].cls += ' navbar-right';
5462         }
5463         
5464         
5465         
5466         
5467         return cfg;
5468     
5469         
5470     }
5471     
5472     
5473     
5474 });
5475
5476
5477
5478  
5479
5480  
5481        /*
5482  * - LGPL
5483  *
5484  * navbar
5485  * navbar-fixed-top
5486  * navbar-expand-md  fixed-top 
5487  */
5488
5489 /**
5490  * @class Roo.bootstrap.NavHeaderbar
5491  * @extends Roo.bootstrap.NavSimplebar
5492  * Bootstrap Sidebar class
5493  *
5494  * @cfg {String} brand what is brand
5495  * @cfg {String} position (fixed-top|fixed-bottom|static-top) position
5496  * @cfg {String} brand_href href of the brand
5497  * @cfg {Boolean} srButton generate the (screen reader / mobile) sr-only button   default true
5498  * @cfg {Boolean} autohide a top nav bar header that hides on scroll.
5499  * @cfg {Boolean} desktopCenter should the header be centered on desktop using a container class
5500  * @cfg {Roo.bootstrap.Row} mobilerow - a row to display on mobile only..
5501  * 
5502  * @constructor
5503  * Create a new Sidebar
5504  * @param {Object} config The config object
5505  */
5506
5507
5508 Roo.bootstrap.NavHeaderbar = function(config){
5509     Roo.bootstrap.NavHeaderbar.superclass.constructor.call(this, config);
5510       
5511 };
5512
5513 Roo.extend(Roo.bootstrap.NavHeaderbar, Roo.bootstrap.NavSimplebar,  {
5514     
5515     position: '',
5516     brand: '',
5517     brand_href: false,
5518     srButton : true,
5519     autohide : false,
5520     desktopCenter : false,
5521    
5522     
5523     getAutoCreate : function(){
5524         
5525         var   cfg = {
5526             tag: this.nav || 'nav',
5527             cls: 'navbar navbar-expand-md',
5528             role: 'navigation',
5529             cn: []
5530         };
5531         
5532         var cn = cfg.cn;
5533         if (this.desktopCenter) {
5534             cn.push({cls : 'container', cn : []});
5535             cn = cn[0].cn;
5536         }
5537         
5538         if(this.srButton){
5539             var btn = {
5540                 tag: 'button',
5541                 type: 'button',
5542                 cls: 'navbar-toggle navbar-toggler',
5543                 'data-toggle': 'collapse',
5544                 cn: [
5545                     {
5546                         tag: 'span',
5547                         cls: 'sr-only',
5548                         html: 'Toggle navigation'
5549                     },
5550                     {
5551                         tag: 'span',
5552                         cls: 'icon-bar navbar-toggler-icon'
5553                     },
5554                     {
5555                         tag: 'span',
5556                         cls: 'icon-bar'
5557                     },
5558                     {
5559                         tag: 'span',
5560                         cls: 'icon-bar'
5561                     }
5562                 ]
5563             };
5564             
5565             cn.push( Roo.bootstrap.version == 4 ? btn : {
5566                 tag: 'div',
5567                 cls: 'navbar-header',
5568                 cn: [
5569                     btn
5570                 ]
5571             });
5572         }
5573         
5574         cn.push({
5575             tag: 'div',
5576             cls: Roo.bootstrap.version == 4  ? 'nav flex-row roo-navbar-collapse collapse navbar-collapse' : 'collapse navbar-collapse roo-navbar-collapse',
5577             cn : []
5578         });
5579         
5580         cfg.cls += this.inverse ? ' navbar-inverse navbar-dark bg-dark' : ' navbar-default';
5581         
5582         if (['light','white'].indexOf(this.weight) > -1) {
5583             cfg.cls += ['light','white'].indexOf(this.weight) > -1 ? ' navbar-light' : ' navbar-dark';
5584         }
5585         cfg.cls += ' bg-' + this.weight;
5586         
5587         
5588         if (['fixed-top','fixed-bottom','static-top'].indexOf(this.position)>-1) {
5589             cfg.cls += ' navbar-' + this.position + ' ' + this.position ;
5590             
5591             // tag can override this..
5592             
5593             cfg.tag = this.tag || (this.position  == 'fixed-bottom' ? 'footer' : 'header');
5594         }
5595         
5596         if (this.brand !== '') {
5597             var cp =  Roo.bootstrap.version == 4 ? cn : cn[0].cn;
5598             cp.unshift({ // changed from push ?? BS4 needs it at the start? - does this break or exsiting?
5599                 tag: 'a',
5600                 href: this.brand_href ? this.brand_href : '#',
5601                 cls: 'navbar-brand',
5602                 cn: [
5603                 this.brand
5604                 ]
5605             });
5606         }
5607         
5608         if(this.main){
5609             cfg.cls += ' main-nav';
5610         }
5611         
5612         
5613         return cfg;
5614
5615         
5616     },
5617     getHeaderChildContainer : function()
5618     {
5619         if (this.srButton && this.el.select('.navbar-header').getCount()) {
5620             return this.el.select('.navbar-header',true).first();
5621         }
5622         
5623         return this.getChildContainer();
5624     },
5625     
5626     getChildContainer : function()
5627     {
5628          
5629         return this.el.select('.roo-navbar-collapse',true).first();
5630          
5631         
5632     },
5633     
5634     initEvents : function()
5635     {
5636         Roo.bootstrap.NavHeaderbar.superclass.initEvents.call(this);
5637         
5638         if (this.autohide) {
5639             
5640             var prevScroll = 0;
5641             var ft = this.el;
5642             
5643             Roo.get(document).on('scroll',function(e) {
5644                 var ns = Roo.get(document).getScroll().top;
5645                 var os = prevScroll;
5646                 prevScroll = ns;
5647                 
5648                 if(ns > os){
5649                     ft.removeClass('slideDown');
5650                     ft.addClass('slideUp');
5651                     return;
5652                 }
5653                 ft.removeClass('slideUp');
5654                 ft.addClass('slideDown');
5655                  
5656               
5657           },this);
5658         }
5659     }    
5660     
5661 });
5662
5663
5664
5665  
5666
5667  /*
5668  * - LGPL
5669  *
5670  * navbar
5671  * 
5672  */
5673
5674 /**
5675  * @class Roo.bootstrap.NavSidebar
5676  * @extends Roo.bootstrap.Navbar
5677  * Bootstrap Sidebar class
5678  * 
5679  * @constructor
5680  * Create a new Sidebar
5681  * @param {Object} config The config object
5682  */
5683
5684
5685 Roo.bootstrap.NavSidebar = function(config){
5686     Roo.bootstrap.NavSidebar.superclass.constructor.call(this, config);
5687 };
5688
5689 Roo.extend(Roo.bootstrap.NavSidebar, Roo.bootstrap.Navbar,  {
5690     
5691     sidebar : true, // used by Navbar Item and NavbarGroup at present...
5692     
5693     getAutoCreate : function(){
5694         
5695         
5696         return  {
5697             tag: 'div',
5698             cls: 'sidebar sidebar-nav'
5699         };
5700     
5701         
5702     }
5703     
5704     
5705     
5706 });
5707
5708
5709
5710  
5711
5712  /*
5713  * - LGPL
5714  *
5715  * nav group
5716  * 
5717  */
5718
5719 /**
5720  * @class Roo.bootstrap.NavGroup
5721  * @extends Roo.bootstrap.Component
5722  * Bootstrap NavGroup class
5723  * @cfg {String} align (left|right)
5724  * @cfg {Boolean} inverse
5725  * @cfg {String} type (nav|pills|tab) default nav
5726  * @cfg {String} navId - reference Id for navbar.
5727
5728  * 
5729  * @constructor
5730  * Create a new nav group
5731  * @param {Object} config The config object
5732  */
5733
5734 Roo.bootstrap.NavGroup = function(config){
5735     Roo.bootstrap.NavGroup.superclass.constructor.call(this, config);
5736     this.navItems = [];
5737    
5738     Roo.bootstrap.NavGroup.register(this);
5739      this.addEvents({
5740         /**
5741              * @event changed
5742              * Fires when the active item changes
5743              * @param {Roo.bootstrap.NavGroup} this
5744              * @param {Roo.bootstrap.Navbar.Item} selected The item selected
5745              * @param {Roo.bootstrap.Navbar.Item} prev The previously selected item 
5746          */
5747         'changed': true
5748      });
5749     
5750 };
5751
5752 Roo.extend(Roo.bootstrap.NavGroup, Roo.bootstrap.Component,  {
5753     
5754     align: '',
5755     inverse: false,
5756     form: false,
5757     type: 'nav',
5758     navId : '',
5759     // private
5760     
5761     navItems : false, 
5762     
5763     getAutoCreate : function()
5764     {
5765         var cfg = Roo.apply({}, Roo.bootstrap.NavGroup.superclass.getAutoCreate.call(this));
5766         
5767         cfg = {
5768             tag : 'ul',
5769             cls: 'nav' 
5770         };
5771         if (Roo.bootstrap.version == 4) {
5772             if (['tabs','pills'].indexOf(this.type) != -1) {
5773                 cfg.cls += ' nav-' + this.type; 
5774             } else {
5775                 // trying to remove so header bar can right align top?
5776                 if (this.parent() && this.parent().xtype != 'NavHeaderbar') {
5777                     // do not use on header bar... 
5778                     cfg.cls += ' navbar-nav';
5779                 }
5780             }
5781             
5782         } else {
5783             if (['tabs','pills'].indexOf(this.type) != -1) {
5784                 cfg.cls += ' nav-' + this.type
5785             } else {
5786                 if (this.type !== 'nav') {
5787                     Roo.log('nav type must be nav/tabs/pills')
5788                 }
5789                 cfg.cls += ' navbar-nav'
5790             }
5791         }
5792         
5793         if (this.parent() && this.parent().sidebar) {
5794             cfg = {
5795                 tag: 'ul',
5796                 cls: 'dashboard-menu sidebar-menu'
5797             };
5798             
5799             return cfg;
5800         }
5801         
5802         if (this.form === true) {
5803             cfg = {
5804                 tag: 'form',
5805                 cls: 'navbar-form form-inline'
5806             };
5807             //nav navbar-right ml-md-auto
5808             if (this.align === 'right') {
5809                 cfg.cls += ' navbar-right ml-md-auto';
5810             } else {
5811                 cfg.cls += ' navbar-left';
5812             }
5813         }
5814         
5815         if (this.align === 'right') {
5816             cfg.cls += ' navbar-right ml-md-auto';
5817         } else {
5818             cfg.cls += ' mr-auto';
5819         }
5820         
5821         if (this.inverse) {
5822             cfg.cls += ' navbar-inverse';
5823             
5824         }
5825         
5826         
5827         return cfg;
5828     },
5829     /**
5830     * sets the active Navigation item
5831     * @param {Roo.bootstrap.NavItem} the new current navitem
5832     */
5833     setActiveItem : function(item)
5834     {
5835         var prev = false;
5836         Roo.each(this.navItems, function(v){
5837             if (v == item) {
5838                 return ;
5839             }
5840             if (v.isActive()) {
5841                 v.setActive(false, true);
5842                 prev = v;
5843                 
5844             }
5845             
5846         });
5847
5848         item.setActive(true, true);
5849         this.fireEvent('changed', this, item, prev);
5850         
5851         
5852     },
5853     /**
5854     * gets the active Navigation item
5855     * @return {Roo.bootstrap.NavItem} the current navitem
5856     */
5857     getActive : function()
5858     {
5859         
5860         var prev = false;
5861         Roo.each(this.navItems, function(v){
5862             
5863             if (v.isActive()) {
5864                 prev = v;
5865                 
5866             }
5867             
5868         });
5869         return prev;
5870     },
5871     
5872     indexOfNav : function()
5873     {
5874         
5875         var prev = false;
5876         Roo.each(this.navItems, function(v,i){
5877             
5878             if (v.isActive()) {
5879                 prev = i;
5880                 
5881             }
5882             
5883         });
5884         return prev;
5885     },
5886     /**
5887     * adds a Navigation item
5888     * @param {Roo.bootstrap.NavItem} the navitem to add
5889     */
5890     addItem : function(cfg)
5891     {
5892         if (this.form && Roo.bootstrap.version == 4) {
5893             cfg.tag = 'div';
5894         }
5895         var cn = new Roo.bootstrap.NavItem(cfg);
5896         this.register(cn);
5897         cn.parentId = this.id;
5898         cn.onRender(this.el, null);
5899         return cn;
5900     },
5901     /**
5902     * register a Navigation item
5903     * @param {Roo.bootstrap.NavItem} the navitem to add
5904     */
5905     register : function(item)
5906     {
5907         this.navItems.push( item);
5908         item.navId = this.navId;
5909     
5910     },
5911     
5912     /**
5913     * clear all the Navigation item
5914     */
5915    
5916     clearAll : function()
5917     {
5918         this.navItems = [];
5919         this.el.dom.innerHTML = '';
5920     },
5921     
5922     getNavItem: function(tabId)
5923     {
5924         var ret = false;
5925         Roo.each(this.navItems, function(e) {
5926             if (e.tabId == tabId) {
5927                ret =  e;
5928                return false;
5929             }
5930             return true;
5931             
5932         });
5933         return ret;
5934     },
5935     
5936     setActiveNext : function()
5937     {
5938         var i = this.indexOfNav(this.getActive());
5939         if (i > this.navItems.length) {
5940             return;
5941         }
5942         this.setActiveItem(this.navItems[i+1]);
5943     },
5944     setActivePrev : function()
5945     {
5946         var i = this.indexOfNav(this.getActive());
5947         if (i  < 1) {
5948             return;
5949         }
5950         this.setActiveItem(this.navItems[i-1]);
5951     },
5952     clearWasActive : function(except) {
5953         Roo.each(this.navItems, function(e) {
5954             if (e.tabId != except.tabId && e.was_active) {
5955                e.was_active = false;
5956                return false;
5957             }
5958             return true;
5959             
5960         });
5961     },
5962     getWasActive : function ()
5963     {
5964         var r = false;
5965         Roo.each(this.navItems, function(e) {
5966             if (e.was_active) {
5967                r = e;
5968                return false;
5969             }
5970             return true;
5971             
5972         });
5973         return r;
5974     }
5975     
5976     
5977 });
5978
5979  
5980 Roo.apply(Roo.bootstrap.NavGroup, {
5981     
5982     groups: {},
5983      /**
5984     * register a Navigation Group
5985     * @param {Roo.bootstrap.NavGroup} the navgroup to add
5986     */
5987     register : function(navgrp)
5988     {
5989         this.groups[navgrp.navId] = navgrp;
5990         
5991     },
5992     /**
5993     * fetch a Navigation Group based on the navigation ID
5994     * @param {string} the navgroup to add
5995     * @returns {Roo.bootstrap.NavGroup} the navgroup 
5996     */
5997     get: function(navId) {
5998         if (typeof(this.groups[navId]) == 'undefined') {
5999             return false;
6000             //this.register(new Roo.bootstrap.NavGroup({ navId : navId }));
6001         }
6002         return this.groups[navId] ;
6003     }
6004     
6005     
6006     
6007 });
6008
6009  /*
6010  * - LGPL
6011  *
6012  * row
6013  * 
6014  */
6015
6016 /**
6017  * @class Roo.bootstrap.NavItem
6018  * @extends Roo.bootstrap.Component
6019  * Bootstrap Navbar.NavItem class
6020  * @cfg {String} href  link to
6021  * @cfg {String} button_weight (default | primary | secondary | success | info | warning | danger | link ) default none
6022
6023  * @cfg {String} html content of button
6024  * @cfg {String} badge text inside badge
6025  * @cfg {String} badgecls (bg-green|bg-red|bg-yellow)the extra classes for the badge
6026  * @cfg {String} glyphicon DEPRICATED - use fa
6027  * @cfg {String} icon DEPRICATED - use fa
6028  * @cfg {String} fa - Fontawsome icon name (can add stuff to it like fa-2x)
6029  * @cfg {Boolean} active Is item active
6030  * @cfg {Boolean} disabled Is item disabled
6031  
6032  * @cfg {Boolean} preventDefault (true | false) default false
6033  * @cfg {String} tabId the tab that this item activates.
6034  * @cfg {String} tagtype (a|span) render as a href or span?
6035  * @cfg {Boolean} animateRef (true|false) link to element default false  
6036   
6037  * @constructor
6038  * Create a new Navbar Item
6039  * @param {Object} config The config object
6040  */
6041 Roo.bootstrap.NavItem = function(config){
6042     Roo.bootstrap.NavItem.superclass.constructor.call(this, config);
6043     this.addEvents({
6044         // raw events
6045         /**
6046          * @event click
6047          * The raw click event for the entire grid.
6048          * @param {Roo.EventObject} e
6049          */
6050         "click" : true,
6051          /**
6052             * @event changed
6053             * Fires when the active item active state changes
6054             * @param {Roo.bootstrap.NavItem} this
6055             * @param {boolean} state the new state
6056              
6057          */
6058         'changed': true,
6059         /**
6060             * @event scrollto
6061             * Fires when scroll to element
6062             * @param {Roo.bootstrap.NavItem} this
6063             * @param {Object} options
6064             * @param {Roo.EventObject} e
6065              
6066          */
6067         'scrollto': true
6068     });
6069    
6070 };
6071
6072 Roo.extend(Roo.bootstrap.NavItem, Roo.bootstrap.Component,  {
6073     
6074     href: false,
6075     html: '',
6076     badge: '',
6077     icon: false,
6078     fa : false,
6079     glyphicon: false,
6080     active: false,
6081     preventDefault : false,
6082     tabId : false,
6083     tagtype : 'a',
6084     tag: 'li',
6085     disabled : false,
6086     animateRef : false,
6087     was_active : false,
6088     button_weight : '',
6089     button_outline : false,
6090     
6091     navLink: false,
6092     
6093     getAutoCreate : function(){
6094          
6095         var cfg = {
6096             tag: this.tag,
6097             cls: 'nav-item'
6098         };
6099         
6100         if (this.active) {
6101             cfg.cls = typeof(cfg.cls) == 'undefined' ? 'active' : cfg.cls + ' active';
6102         }
6103         if (this.disabled) {
6104             cfg.cls += ' disabled';
6105         }
6106         
6107         // BS4 only?
6108         if (this.button_weight.length) {
6109             cfg.tag = this.href ? 'a' : 'button';
6110             cfg.html = this.html || '';
6111             cfg.cls += ' btn btn' + (this.button_outline ? '-outline' : '') + '-' + this.button_weight;
6112             if (this.href) {
6113                 cfg.href = this.href;
6114             }
6115             if (this.fa) {
6116                 cfg.html = '<i class="fa fas fa-'+this.fa+'"></i> <span>' + this.html + '</span>';
6117             }
6118             
6119             // menu .. should add dropdown-menu class - so no need for carat..
6120             
6121             if (this.badge !== '') {
6122                  
6123                 cfg.html += ' <span class="badge badge-secondary">' + this.badge + '</span>';
6124             }
6125             return cfg;
6126         }
6127         
6128         if (this.href || this.html || this.glyphicon || this.icon || this.fa) {
6129             cfg.cn = [
6130                 {
6131                     tag: this.tagtype,
6132                     href : this.href || "#",
6133                     html: this.html || ''
6134                 }
6135             ];
6136             if (this.tagtype == 'a') {
6137                 cfg.cn[0].cls = 'nav-link';
6138             }
6139             if (this.icon) {
6140                 cfg.cn[0].html = '<i class="'+this.icon+'"></i> <span>' + cfg.cn[0].html + '</span>';
6141             }
6142             if (this.fa) {
6143                 cfg.cn[0].html = '<i class="fa fas fa-'+this.fa+'"></i> <span>' + cfg.cn[0].html + '</span>';
6144             }
6145             if(this.glyphicon) {
6146                 cfg.cn[0].html = '<span class="glyphicon glyphicon-' + this.glyphicon + '"></span> '  + cfg.cn[0].html;
6147             }
6148             
6149             if (this.menu) {
6150                 
6151                 cfg.cn[0].html += " <span class='caret'></span>";
6152              
6153             }
6154             
6155             if (this.badge !== '') {
6156                  
6157                 cfg.cn[0].html += ' <span class="badge badge-secondary">' + this.badge + '</span>';
6158             }
6159         }
6160         
6161         
6162         
6163         return cfg;
6164     },
6165     onRender : function(ct, position)
6166     {
6167        // Roo.log("Call onRender: " + this.xtype);
6168         if (Roo.bootstrap.version == 4 && ct.dom.type != 'ul') {
6169             this.tag = 'div';
6170         }
6171         
6172         var ret = Roo.bootstrap.NavItem.superclass.onRender.call(this, ct, position);
6173         this.navLink = this.el.select('.nav-link',true).first();
6174         return ret;
6175     },
6176       
6177     
6178     initEvents: function() 
6179     {
6180         if (typeof (this.menu) != 'undefined') {
6181             this.menu.parentType = this.xtype;
6182             this.menu.triggerEl = this.el;
6183             this.menu = this.addxtype(Roo.apply({}, this.menu));
6184         }
6185         
6186         this.el.select('a',true).on('click', this.onClick, this);
6187         
6188         if(this.tagtype == 'span'){
6189             this.el.select('span',true).on('click', this.onClick, this);
6190         }
6191        
6192         // at this point parent should be available..
6193         this.parent().register(this);
6194     },
6195     
6196     onClick : function(e)
6197     {
6198         if (e.getTarget('.dropdown-menu-item')) {
6199             // did you click on a menu itemm.... - then don't trigger onclick..
6200             return;
6201         }
6202         
6203         if(
6204                 this.preventDefault || 
6205                 this.href == '#' 
6206         ){
6207             Roo.log("NavItem - prevent Default?");
6208             e.preventDefault();
6209         }
6210         
6211         if (this.disabled) {
6212             return;
6213         }
6214         
6215         var tg = Roo.bootstrap.TabGroup.get(this.navId);
6216         if (tg && tg.transition) {
6217             Roo.log("waiting for the transitionend");
6218             return;
6219         }
6220         
6221         
6222         
6223         //Roo.log("fire event clicked");
6224         if(this.fireEvent('click', this, e) === false){
6225             return;
6226         };
6227         
6228         if(this.tagtype == 'span'){
6229             return;
6230         }
6231         
6232         //Roo.log(this.href);
6233         var ael = this.el.select('a',true).first();
6234         //Roo.log(ael);
6235         
6236         if(ael && this.animateRef && this.href.indexOf('#') > -1){
6237             //Roo.log(["test:",ael.dom.href.split("#")[0], document.location.toString().split("#")[0]]);
6238             if (ael.dom.href.split("#")[0] != document.location.toString().split("#")[0]) {
6239                 return; // ignore... - it's a 'hash' to another page.
6240             }
6241             Roo.log("NavItem - prevent Default?");
6242             e.preventDefault();
6243             this.scrollToElement(e);
6244         }
6245         
6246         
6247         var p =  this.parent();
6248    
6249         if (['tabs','pills'].indexOf(p.type)!==-1) {
6250             if (typeof(p.setActiveItem) !== 'undefined') {
6251                 p.setActiveItem(this);
6252             }
6253         }
6254         
6255         // if parent is a navbarheader....- and link is probably a '#' page ref.. then remove the expanded menu.
6256         if (p.parentType == 'NavHeaderbar' && !this.menu) {
6257             // remove the collapsed menu expand...
6258             p.parent().el.select('.roo-navbar-collapse',true).removeClass('in');  
6259         }
6260     },
6261     
6262     isActive: function () {
6263         return this.active
6264     },
6265     setActive : function(state, fire, is_was_active)
6266     {
6267         if (this.active && !state && this.navId) {
6268             this.was_active = true;
6269             var nv = Roo.bootstrap.NavGroup.get(this.navId);
6270             if (nv) {
6271                 nv.clearWasActive(this);
6272             }
6273             
6274         }
6275         this.active = state;
6276         
6277         if (!state ) {
6278             this.el.removeClass('active');
6279             this.navLink ? this.navLink.removeClass('active') : false;
6280         } else if (!this.el.hasClass('active')) {
6281             
6282             this.el.addClass('active');
6283             if (Roo.bootstrap.version == 4 && this.navLink ) {
6284                 this.navLink.addClass('active');
6285             }
6286             
6287         }
6288         if (fire) {
6289             this.fireEvent('changed', this, state);
6290         }
6291         
6292         // show a panel if it's registered and related..
6293         
6294         if (!this.navId || !this.tabId || !state || is_was_active) {
6295             return;
6296         }
6297         
6298         var tg = Roo.bootstrap.TabGroup.get(this.navId);
6299         if (!tg) {
6300             return;
6301         }
6302         var pan = tg.getPanelByName(this.tabId);
6303         if (!pan) {
6304             return;
6305         }
6306         // if we can not flip to new panel - go back to old nav highlight..
6307         if (false == tg.showPanel(pan)) {
6308             var nv = Roo.bootstrap.NavGroup.get(this.navId);
6309             if (nv) {
6310                 var onav = nv.getWasActive();
6311                 if (onav) {
6312                     onav.setActive(true, false, true);
6313                 }
6314             }
6315             
6316         }
6317         
6318         
6319         
6320     },
6321      // this should not be here...
6322     setDisabled : function(state)
6323     {
6324         this.disabled = state;
6325         if (!state ) {
6326             this.el.removeClass('disabled');
6327         } else if (!this.el.hasClass('disabled')) {
6328             this.el.addClass('disabled');
6329         }
6330         
6331     },
6332     
6333     /**
6334      * Fetch the element to display the tooltip on.
6335      * @return {Roo.Element} defaults to this.el
6336      */
6337     tooltipEl : function()
6338     {
6339         return this.el.select('' + this.tagtype + '', true).first();
6340     },
6341     
6342     scrollToElement : function(e)
6343     {
6344         var c = document.body;
6345         
6346         /*
6347          * Firefox / IE places the overflow at the html level, unless specifically styled to behave differently.
6348          */
6349         if(Roo.isFirefox || Roo.isIE || Roo.isIE11){
6350             c = document.documentElement;
6351         }
6352         
6353         var target = Roo.get(c).select('a[name=' + this.href.split('#')[1] +']', true).first();
6354         
6355         if(!target){
6356             return;
6357         }
6358
6359         var o = target.calcOffsetsTo(c);
6360         
6361         var options = {
6362             target : target,
6363             value : o[1]
6364         };
6365         
6366         this.fireEvent('scrollto', this, options, e);
6367         
6368         Roo.get(c).scrollTo('top', options.value, true);
6369         
6370         return;
6371     }
6372 });
6373  
6374
6375  /*
6376  * - LGPL
6377  *
6378  * sidebar item
6379  *
6380  *  li
6381  *    <span> icon </span>
6382  *    <span> text </span>
6383  *    <span>badge </span>
6384  */
6385
6386 /**
6387  * @class Roo.bootstrap.NavSidebarItem
6388  * @extends Roo.bootstrap.NavItem
6389  * Bootstrap Navbar.NavSidebarItem class
6390  * {String} badgeWeight (default|primary|success|info|warning|danger)the extra classes for the badge
6391  * {Boolean} open is the menu open
6392  * {Boolean} buttonView use button as the tigger el rather that a (default false)
6393  * {String} buttonWeight (default|primary|success|info|warning|danger)the extra classes for the button
6394  * {String} buttonSize (sm|md|lg)the extra classes for the button
6395  * {Boolean} showArrow show arrow next to the text (default true)
6396  * @constructor
6397  * Create a new Navbar Button
6398  * @param {Object} config The config object
6399  */
6400 Roo.bootstrap.NavSidebarItem = function(config){
6401     Roo.bootstrap.NavSidebarItem.superclass.constructor.call(this, config);
6402     this.addEvents({
6403         // raw events
6404         /**
6405          * @event click
6406          * The raw click event for the entire grid.
6407          * @param {Roo.EventObject} e
6408          */
6409         "click" : true,
6410          /**
6411             * @event changed
6412             * Fires when the active item active state changes
6413             * @param {Roo.bootstrap.NavSidebarItem} this
6414             * @param {boolean} state the new state
6415              
6416          */
6417         'changed': true
6418     });
6419    
6420 };
6421
6422 Roo.extend(Roo.bootstrap.NavSidebarItem, Roo.bootstrap.NavItem,  {
6423     
6424     badgeWeight : 'default',
6425     
6426     open: false,
6427     
6428     buttonView : false,
6429     
6430     buttonWeight : 'default',
6431     
6432     buttonSize : 'md',
6433     
6434     showArrow : true,
6435     
6436     getAutoCreate : function(){
6437         
6438         
6439         var a = {
6440                 tag: 'a',
6441                 href : this.href || '#',
6442                 cls: '',
6443                 html : '',
6444                 cn : []
6445         };
6446         
6447         if(this.buttonView){
6448             a = {
6449                 tag: 'button',
6450                 href : this.href || '#',
6451                 cls: 'btn btn-' + this.buttonWeight + ' btn-' + this.buttonSize + 'roo-button-dropdown-toggle',
6452                 html : this.html,
6453                 cn : []
6454             };
6455         }
6456         
6457         var cfg = {
6458             tag: 'li',
6459             cls: '',
6460             cn: [ a ]
6461         };
6462         
6463         if (this.active) {
6464             cfg.cls += ' active';
6465         }
6466         
6467         if (this.disabled) {
6468             cfg.cls += ' disabled';
6469         }
6470         if (this.open) {
6471             cfg.cls += ' open x-open';
6472         }
6473         // left icon..
6474         if (this.glyphicon || this.icon) {
6475             var c = this.glyphicon  ? ('glyphicon glyphicon-'+this.glyphicon)  : this.icon;
6476             a.cn.push({ tag : 'i', cls : c }) ;
6477         }
6478         
6479         if(!this.buttonView){
6480             var span = {
6481                 tag: 'span',
6482                 html : this.html || ''
6483             };
6484
6485             a.cn.push(span);
6486             
6487         }
6488         
6489         if (this.badge !== '') {
6490             a.cn.push({ tag: 'span',  cls : 'badge pull-right badge-' + this.badgeWeight, html: this.badge }); 
6491         }
6492         
6493         if (this.menu) {
6494             
6495             if(this.showArrow){
6496                 a.cn.push({ tag : 'i', cls : 'glyphicon glyphicon-chevron-down pull-right'});
6497             }
6498             
6499             a.cls += ' dropdown-toggle treeview' ;
6500         }
6501         
6502         return cfg;
6503     },
6504     
6505     initEvents : function()
6506     { 
6507         if (typeof (this.menu) != 'undefined') {
6508             this.menu.parentType = this.xtype;
6509             this.menu.triggerEl = this.el;
6510             this.menu = this.addxtype(Roo.apply({}, this.menu));
6511         }
6512         
6513         this.el.on('click', this.onClick, this);
6514         
6515         if(this.badge !== ''){
6516             this.badgeEl = this.el.select('.badge', true).first().setVisibilityMode(Roo.Element.DISPLAY);
6517         }
6518         
6519     },
6520     
6521     onClick : function(e)
6522     {
6523         if(this.disabled){
6524             e.preventDefault();
6525             return;
6526         }
6527         
6528         if(this.preventDefault){
6529             e.preventDefault();
6530         }
6531         
6532         this.fireEvent('click', this, e);
6533     },
6534     
6535     disable : function()
6536     {
6537         this.setDisabled(true);
6538     },
6539     
6540     enable : function()
6541     {
6542         this.setDisabled(false);
6543     },
6544     
6545     setDisabled : function(state)
6546     {
6547         if(this.disabled == state){
6548             return;
6549         }
6550         
6551         this.disabled = state;
6552         
6553         if (state) {
6554             this.el.addClass('disabled');
6555             return;
6556         }
6557         
6558         this.el.removeClass('disabled');
6559         
6560         return;
6561     },
6562     
6563     setActive : function(state)
6564     {
6565         if(this.active == state){
6566             return;
6567         }
6568         
6569         this.active = state;
6570         
6571         if (state) {
6572             this.el.addClass('active');
6573             return;
6574         }
6575         
6576         this.el.removeClass('active');
6577         
6578         return;
6579     },
6580     
6581     isActive: function () 
6582     {
6583         return this.active;
6584     },
6585     
6586     setBadge : function(str)
6587     {
6588         if(!this.badgeEl){
6589             return;
6590         }
6591         
6592         this.badgeEl.dom.innerHTML = str;
6593     }
6594     
6595    
6596      
6597  
6598 });
6599  
6600
6601  /*
6602  * - LGPL
6603  *
6604  * row
6605  * 
6606  */
6607
6608 /**
6609  * @class Roo.bootstrap.Row
6610  * @extends Roo.bootstrap.Component
6611  * Bootstrap Row class (contains columns...)
6612  * 
6613  * @constructor
6614  * Create a new Row
6615  * @param {Object} config The config object
6616  */
6617
6618 Roo.bootstrap.Row = function(config){
6619     Roo.bootstrap.Row.superclass.constructor.call(this, config);
6620 };
6621
6622 Roo.extend(Roo.bootstrap.Row, Roo.bootstrap.Component,  {
6623     
6624     getAutoCreate : function(){
6625        return {
6626             cls: 'row clearfix'
6627        };
6628     }
6629     
6630     
6631 });
6632
6633  
6634
6635  /*
6636  * - LGPL
6637  *
6638  * pagination
6639  * 
6640  */
6641
6642 /**
6643  * @class Roo.bootstrap.Pagination
6644  * @extends Roo.bootstrap.Component
6645  * Bootstrap Pagination class
6646  * @cfg {String} size xs | sm | md | lg
6647  * @cfg {Boolean} inverse false | true
6648  * 
6649  * @constructor
6650  * Create a new Pagination
6651  * @param {Object} config The config object
6652  */
6653
6654 Roo.bootstrap.Pagination = function(config){
6655     Roo.bootstrap.Pagination.superclass.constructor.call(this, config);
6656 };
6657
6658 Roo.extend(Roo.bootstrap.Pagination, Roo.bootstrap.Component,  {
6659     
6660     cls: false,
6661     size: false,
6662     inverse: false,
6663     
6664     getAutoCreate : function(){
6665         var cfg = {
6666             tag: 'ul',
6667                 cls: 'pagination'
6668         };
6669         if (this.inverse) {
6670             cfg.cls += ' inverse';
6671         }
6672         if (this.html) {
6673             cfg.html=this.html;
6674         }
6675         if (this.cls) {
6676             cfg.cls += " " + this.cls;
6677         }
6678         return cfg;
6679     }
6680    
6681 });
6682
6683  
6684
6685  /*
6686  * - LGPL
6687  *
6688  * Pagination item
6689  * 
6690  */
6691
6692
6693 /**
6694  * @class Roo.bootstrap.PaginationItem
6695  * @extends Roo.bootstrap.Component
6696  * Bootstrap PaginationItem class
6697  * @cfg {String} html text
6698  * @cfg {String} href the link
6699  * @cfg {Boolean} preventDefault (true | false) default true
6700  * @cfg {Boolean} active (true | false) default false
6701  * @cfg {Boolean} disabled default false
6702  * 
6703  * 
6704  * @constructor
6705  * Create a new PaginationItem
6706  * @param {Object} config The config object
6707  */
6708
6709
6710 Roo.bootstrap.PaginationItem = function(config){
6711     Roo.bootstrap.PaginationItem.superclass.constructor.call(this, config);
6712     this.addEvents({
6713         // raw events
6714         /**
6715          * @event click
6716          * The raw click event for the entire grid.
6717          * @param {Roo.EventObject} e
6718          */
6719         "click" : true
6720     });
6721 };
6722
6723 Roo.extend(Roo.bootstrap.PaginationItem, Roo.bootstrap.Component,  {
6724     
6725     href : false,
6726     html : false,
6727     preventDefault: true,
6728     active : false,
6729     cls : false,
6730     disabled: false,
6731     
6732     getAutoCreate : function(){
6733         var cfg= {
6734             tag: 'li',
6735             cn: [
6736                 {
6737                     tag : 'a',
6738                     href : this.href ? this.href : '#',
6739                     html : this.html ? this.html : ''
6740                 }
6741             ]
6742         };
6743         
6744         if(this.cls){
6745             cfg.cls = this.cls;
6746         }
6747         
6748         if(this.disabled){
6749             cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' disabled' : 'disabled';
6750         }
6751         
6752         if(this.active){
6753             cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' active' : 'active';
6754         }
6755         
6756         return cfg;
6757     },
6758     
6759     initEvents: function() {
6760         
6761         this.el.on('click', this.onClick, this);
6762         
6763     },
6764     onClick : function(e)
6765     {
6766         Roo.log('PaginationItem on click ');
6767         if(this.preventDefault){
6768             e.preventDefault();
6769         }
6770         
6771         if(this.disabled){
6772             return;
6773         }
6774         
6775         this.fireEvent('click', this, e);
6776     }
6777    
6778 });
6779
6780  
6781
6782  /*
6783  * - LGPL
6784  *
6785  * slider
6786  * 
6787  */
6788
6789
6790 /**
6791  * @class Roo.bootstrap.Slider
6792  * @extends Roo.bootstrap.Component
6793  * Bootstrap Slider class
6794  *    
6795  * @constructor
6796  * Create a new Slider
6797  * @param {Object} config The config object
6798  */
6799
6800 Roo.bootstrap.Slider = function(config){
6801     Roo.bootstrap.Slider.superclass.constructor.call(this, config);
6802 };
6803
6804 Roo.extend(Roo.bootstrap.Slider, Roo.bootstrap.Component,  {
6805     
6806     getAutoCreate : function(){
6807         
6808         var cfg = {
6809             tag: 'div',
6810             cls: 'slider slider-sample1 vertical-handler ui-slider ui-slider-horizontal ui-widget ui-widget-content ui-corner-all',
6811             cn: [
6812                 {
6813                     tag: 'a',
6814                     cls: 'ui-slider-handle ui-state-default ui-corner-all'
6815                 }
6816             ]
6817         };
6818         
6819         return cfg;
6820     }
6821    
6822 });
6823
6824  /*
6825  * Based on:
6826  * Ext JS Library 1.1.1
6827  * Copyright(c) 2006-2007, Ext JS, LLC.
6828  *
6829  * Originally Released Under LGPL - original licence link has changed is not relivant.
6830  *
6831  * Fork - LGPL
6832  * <script type="text/javascript">
6833  */
6834  
6835
6836 /**
6837  * @class Roo.grid.ColumnModel
6838  * @extends Roo.util.Observable
6839  * This is the default implementation of a ColumnModel used by the Grid. It defines
6840  * the columns in the grid.
6841  * <br>Usage:<br>
6842  <pre><code>
6843  var colModel = new Roo.grid.ColumnModel([
6844         {header: "Ticker", width: 60, sortable: true, locked: true},
6845         {header: "Company Name", width: 150, sortable: true},
6846         {header: "Market Cap.", width: 100, sortable: true},
6847         {header: "$ Sales", width: 100, sortable: true, renderer: money},
6848         {header: "Employees", width: 100, sortable: true, resizable: false}
6849  ]);
6850  </code></pre>
6851  * <p>
6852  
6853  * The config options listed for this class are options which may appear in each
6854  * individual column definition.
6855  * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
6856  * @constructor
6857  * @param {Object} config An Array of column config objects. See this class's
6858  * config objects for details.
6859 */
6860 Roo.grid.ColumnModel = function(config){
6861         /**
6862      * The config passed into the constructor
6863      */
6864     this.config = config;
6865     this.lookup = {};
6866
6867     // if no id, create one
6868     // if the column does not have a dataIndex mapping,
6869     // map it to the order it is in the config
6870     for(var i = 0, len = config.length; i < len; i++){
6871         var c = config[i];
6872         if(typeof c.dataIndex == "undefined"){
6873             c.dataIndex = i;
6874         }
6875         if(typeof c.renderer == "string"){
6876             c.renderer = Roo.util.Format[c.renderer];
6877         }
6878         if(typeof c.id == "undefined"){
6879             c.id = Roo.id();
6880         }
6881         if(c.editor && c.editor.xtype){
6882             c.editor  = Roo.factory(c.editor, Roo.grid);
6883         }
6884         if(c.editor && c.editor.isFormField){
6885             c.editor = new Roo.grid.GridEditor(c.editor);
6886         }
6887         this.lookup[c.id] = c;
6888     }
6889
6890     /**
6891      * The width of columns which have no width specified (defaults to 100)
6892      * @type Number
6893      */
6894     this.defaultWidth = 100;
6895
6896     /**
6897      * Default sortable of columns which have no sortable specified (defaults to false)
6898      * @type Boolean
6899      */
6900     this.defaultSortable = false;
6901
6902     this.addEvents({
6903         /**
6904              * @event widthchange
6905              * Fires when the width of a column changes.
6906              * @param {ColumnModel} this
6907              * @param {Number} columnIndex The column index
6908              * @param {Number} newWidth The new width
6909              */
6910             "widthchange": true,
6911         /**
6912              * @event headerchange
6913              * Fires when the text of a header changes.
6914              * @param {ColumnModel} this
6915              * @param {Number} columnIndex The column index
6916              * @param {Number} newText The new header text
6917              */
6918             "headerchange": true,
6919         /**
6920              * @event hiddenchange
6921              * Fires when a column is hidden or "unhidden".
6922              * @param {ColumnModel} this
6923              * @param {Number} columnIndex The column index
6924              * @param {Boolean} hidden true if hidden, false otherwise
6925              */
6926             "hiddenchange": true,
6927             /**
6928          * @event columnmoved
6929          * Fires when a column is moved.
6930          * @param {ColumnModel} this
6931          * @param {Number} oldIndex
6932          * @param {Number} newIndex
6933          */
6934         "columnmoved" : true,
6935         /**
6936          * @event columlockchange
6937          * Fires when a column's locked state is changed
6938          * @param {ColumnModel} this
6939          * @param {Number} colIndex
6940          * @param {Boolean} locked true if locked
6941          */
6942         "columnlockchange" : true
6943     });
6944     Roo.grid.ColumnModel.superclass.constructor.call(this);
6945 };
6946 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
6947     /**
6948      * @cfg {String} header The header text to display in the Grid view.
6949      */
6950     /**
6951      * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
6952      * {@link Roo.data.Record} definition from which to draw the column's value. If not
6953      * specified, the column's index is used as an index into the Record's data Array.
6954      */
6955     /**
6956      * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
6957      * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
6958      */
6959     /**
6960      * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
6961      * Defaults to the value of the {@link #defaultSortable} property.
6962      * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
6963      */
6964     /**
6965      * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid.  Defaults to false.
6966      */
6967     /**
6968      * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed.  Defaults to false.
6969      */
6970     /**
6971      * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
6972      */
6973     /**
6974      * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
6975      */
6976     /**
6977      * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
6978      * given the cell's data value. See {@link #setRenderer}. If not specified, the
6979      * default renderer returns the escaped data value. If an object is returned (bootstrap only)
6980      * then it is treated as a Roo Component object instance, and it is rendered after the initial row is rendered
6981      */
6982        /**
6983      * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor 
6984      */
6985     /**
6986      * @cfg {String} align (Optional) Set the CSS text-align property of the column.  Defaults to undefined.
6987      */
6988     /**
6989      * @cfg {String} valign (Optional) Set the CSS vertical-align property of the column (eg. middle, top, bottom etc).  Defaults to undefined.
6990      */
6991     /**
6992      * @cfg {String} cursor (Optional)
6993      */
6994     /**
6995      * @cfg {String} tooltip (Optional)
6996      */
6997     /**
6998      * @cfg {Number} xs (Optional)
6999      */
7000     /**
7001      * @cfg {Number} sm (Optional)
7002      */
7003     /**
7004      * @cfg {Number} md (Optional)
7005      */
7006     /**
7007      * @cfg {Number} lg (Optional)
7008      */
7009     /**
7010      * Returns the id of the column at the specified index.
7011      * @param {Number} index The column index
7012      * @return {String} the id
7013      */
7014     getColumnId : function(index){
7015         return this.config[index].id;
7016     },
7017
7018     /**
7019      * Returns the column for a specified id.
7020      * @param {String} id The column id
7021      * @return {Object} the column
7022      */
7023     getColumnById : function(id){
7024         return this.lookup[id];
7025     },
7026
7027     
7028     /**
7029      * Returns the column for a specified dataIndex.
7030      * @param {String} dataIndex The column dataIndex
7031      * @return {Object|Boolean} the column or false if not found
7032      */
7033     getColumnByDataIndex: function(dataIndex){
7034         var index = this.findColumnIndex(dataIndex);
7035         return index > -1 ? this.config[index] : false;
7036     },
7037     
7038     /**
7039      * Returns the index for a specified column id.
7040      * @param {String} id The column id
7041      * @return {Number} the index, or -1 if not found
7042      */
7043     getIndexById : function(id){
7044         for(var i = 0, len = this.config.length; i < len; i++){
7045             if(this.config[i].id == id){
7046                 return i;
7047             }
7048         }
7049         return -1;
7050     },
7051     
7052     /**
7053      * Returns the index for a specified column dataIndex.
7054      * @param {String} dataIndex The column dataIndex
7055      * @return {Number} the index, or -1 if not found
7056      */
7057     
7058     findColumnIndex : function(dataIndex){
7059         for(var i = 0, len = this.config.length; i < len; i++){
7060             if(this.config[i].dataIndex == dataIndex){
7061                 return i;
7062             }
7063         }
7064         return -1;
7065     },
7066     
7067     
7068     moveColumn : function(oldIndex, newIndex){
7069         var c = this.config[oldIndex];
7070         this.config.splice(oldIndex, 1);
7071         this.config.splice(newIndex, 0, c);
7072         this.dataMap = null;
7073         this.fireEvent("columnmoved", this, oldIndex, newIndex);
7074     },
7075
7076     isLocked : function(colIndex){
7077         return this.config[colIndex].locked === true;
7078     },
7079
7080     setLocked : function(colIndex, value, suppressEvent){
7081         if(this.isLocked(colIndex) == value){
7082             return;
7083         }
7084         this.config[colIndex].locked = value;
7085         if(!suppressEvent){
7086             this.fireEvent("columnlockchange", this, colIndex, value);
7087         }
7088     },
7089
7090     getTotalLockedWidth : function(){
7091         var totalWidth = 0;
7092         for(var i = 0; i < this.config.length; i++){
7093             if(this.isLocked(i) && !this.isHidden(i)){
7094                 this.totalWidth += this.getColumnWidth(i);
7095             }
7096         }
7097         return totalWidth;
7098     },
7099
7100     getLockedCount : function(){
7101         for(var i = 0, len = this.config.length; i < len; i++){
7102             if(!this.isLocked(i)){
7103                 return i;
7104             }
7105         }
7106         
7107         return this.config.length;
7108     },
7109
7110     /**
7111      * Returns the number of columns.
7112      * @return {Number}
7113      */
7114     getColumnCount : function(visibleOnly){
7115         if(visibleOnly === true){
7116             var c = 0;
7117             for(var i = 0, len = this.config.length; i < len; i++){
7118                 if(!this.isHidden(i)){
7119                     c++;
7120                 }
7121             }
7122             return c;
7123         }
7124         return this.config.length;
7125     },
7126
7127     /**
7128      * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
7129      * @param {Function} fn
7130      * @param {Object} scope (optional)
7131      * @return {Array} result
7132      */
7133     getColumnsBy : function(fn, scope){
7134         var r = [];
7135         for(var i = 0, len = this.config.length; i < len; i++){
7136             var c = this.config[i];
7137             if(fn.call(scope||this, c, i) === true){
7138                 r[r.length] = c;
7139             }
7140         }
7141         return r;
7142     },
7143
7144     /**
7145      * Returns true if the specified column is sortable.
7146      * @param {Number} col The column index
7147      * @return {Boolean}
7148      */
7149     isSortable : function(col){
7150         if(typeof this.config[col].sortable == "undefined"){
7151             return this.defaultSortable;
7152         }
7153         return this.config[col].sortable;
7154     },
7155
7156     /**
7157      * Returns the rendering (formatting) function defined for the column.
7158      * @param {Number} col The column index.
7159      * @return {Function} The function used to render the cell. See {@link #setRenderer}.
7160      */
7161     getRenderer : function(col){
7162         if(!this.config[col].renderer){
7163             return Roo.grid.ColumnModel.defaultRenderer;
7164         }
7165         return this.config[col].renderer;
7166     },
7167
7168     /**
7169      * Sets the rendering (formatting) function for a column.
7170      * @param {Number} col The column index
7171      * @param {Function} fn The function to use to process the cell's raw data
7172      * to return HTML markup for the grid view. The render function is called with
7173      * the following parameters:<ul>
7174      * <li>Data value.</li>
7175      * <li>Cell metadata. An object in which you may set the following attributes:<ul>
7176      * <li>css A CSS style string to apply to the table cell.</li>
7177      * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
7178      * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
7179      * <li>Row index</li>
7180      * <li>Column index</li>
7181      * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
7182      */
7183     setRenderer : function(col, fn){
7184         this.config[col].renderer = fn;
7185     },
7186
7187     /**
7188      * Returns the width for the specified column.
7189      * @param {Number} col The column index
7190      * @return {Number}
7191      */
7192     getColumnWidth : function(col){
7193         return this.config[col].width * 1 || this.defaultWidth;
7194     },
7195
7196     /**
7197      * Sets the width for a column.
7198      * @param {Number} col The column index
7199      * @param {Number} width The new width
7200      */
7201     setColumnWidth : function(col, width, suppressEvent){
7202         this.config[col].width = width;
7203         this.totalWidth = null;
7204         if(!suppressEvent){
7205              this.fireEvent("widthchange", this, col, width);
7206         }
7207     },
7208
7209     /**
7210      * Returns the total width of all columns.
7211      * @param {Boolean} includeHidden True to include hidden column widths
7212      * @return {Number}
7213      */
7214     getTotalWidth : function(includeHidden){
7215         if(!this.totalWidth){
7216             this.totalWidth = 0;
7217             for(var i = 0, len = this.config.length; i < len; i++){
7218                 if(includeHidden || !this.isHidden(i)){
7219                     this.totalWidth += this.getColumnWidth(i);
7220                 }
7221             }
7222         }
7223         return this.totalWidth;
7224     },
7225
7226     /**
7227      * Returns the header for the specified column.
7228      * @param {Number} col The column index
7229      * @return {String}
7230      */
7231     getColumnHeader : function(col){
7232         return this.config[col].header;
7233     },
7234
7235     /**
7236      * Sets the header for a column.
7237      * @param {Number} col The column index
7238      * @param {String} header The new header
7239      */
7240     setColumnHeader : function(col, header){
7241         this.config[col].header = header;
7242         this.fireEvent("headerchange", this, col, header);
7243     },
7244
7245     /**
7246      * Returns the tooltip for the specified column.
7247      * @param {Number} col The column index
7248      * @return {String}
7249      */
7250     getColumnTooltip : function(col){
7251             return this.config[col].tooltip;
7252     },
7253     /**
7254      * Sets the tooltip for a column.
7255      * @param {Number} col The column index
7256      * @param {String} tooltip The new tooltip
7257      */
7258     setColumnTooltip : function(col, tooltip){
7259             this.config[col].tooltip = tooltip;
7260     },
7261
7262     /**
7263      * Returns the dataIndex for the specified column.
7264      * @param {Number} col The column index
7265      * @return {Number}
7266      */
7267     getDataIndex : function(col){
7268         return this.config[col].dataIndex;
7269     },
7270
7271     /**
7272      * Sets the dataIndex for a column.
7273      * @param {Number} col The column index
7274      * @param {Number} dataIndex The new dataIndex
7275      */
7276     setDataIndex : function(col, dataIndex){
7277         this.config[col].dataIndex = dataIndex;
7278     },
7279
7280     
7281     
7282     /**
7283      * Returns true if the cell is editable.
7284      * @param {Number} colIndex The column index
7285      * @param {Number} rowIndex The row index - this is nto actually used..?
7286      * @return {Boolean}
7287      */
7288     isCellEditable : function(colIndex, rowIndex){
7289         return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
7290     },
7291
7292     /**
7293      * Returns the editor defined for the cell/column.
7294      * return false or null to disable editing.
7295      * @param {Number} colIndex The column index
7296      * @param {Number} rowIndex The row index
7297      * @return {Object}
7298      */
7299     getCellEditor : function(colIndex, rowIndex){
7300         return this.config[colIndex].editor;
7301     },
7302
7303     /**
7304      * Sets if a column is editable.
7305      * @param {Number} col The column index
7306      * @param {Boolean} editable True if the column is editable
7307      */
7308     setEditable : function(col, editable){
7309         this.config[col].editable = editable;
7310     },
7311
7312
7313     /**
7314      * Returns true if the column is hidden.
7315      * @param {Number} colIndex The column index
7316      * @return {Boolean}
7317      */
7318     isHidden : function(colIndex){
7319         return this.config[colIndex].hidden;
7320     },
7321
7322
7323     /**
7324      * Returns true if the column width cannot be changed
7325      */
7326     isFixed : function(colIndex){
7327         return this.config[colIndex].fixed;
7328     },
7329
7330     /**
7331      * Returns true if the column can be resized
7332      * @return {Boolean}
7333      */
7334     isResizable : function(colIndex){
7335         return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
7336     },
7337     /**
7338      * Sets if a column is hidden.
7339      * @param {Number} colIndex The column index
7340      * @param {Boolean} hidden True if the column is hidden
7341      */
7342     setHidden : function(colIndex, hidden){
7343         this.config[colIndex].hidden = hidden;
7344         this.totalWidth = null;
7345         this.fireEvent("hiddenchange", this, colIndex, hidden);
7346     },
7347
7348     /**
7349      * Sets the editor for a column.
7350      * @param {Number} col The column index
7351      * @param {Object} editor The editor object
7352      */
7353     setEditor : function(col, editor){
7354         this.config[col].editor = editor;
7355     }
7356 });
7357
7358 Roo.grid.ColumnModel.defaultRenderer = function(value)
7359 {
7360     if(typeof value == "object") {
7361         return value;
7362     }
7363         if(typeof value == "string" && value.length < 1){
7364             return "&#160;";
7365         }
7366     
7367         return String.format("{0}", value);
7368 };
7369
7370 // Alias for backwards compatibility
7371 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
7372 /*
7373  * Based on:
7374  * Ext JS Library 1.1.1
7375  * Copyright(c) 2006-2007, Ext JS, LLC.
7376  *
7377  * Originally Released Under LGPL - original licence link has changed is not relivant.
7378  *
7379  * Fork - LGPL
7380  * <script type="text/javascript">
7381  */
7382  
7383 /**
7384  * @class Roo.LoadMask
7385  * A simple utility class for generically masking elements while loading data.  If the element being masked has
7386  * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
7387  * process and the mask element will be cached for reuse.  For all other elements, this mask will replace the
7388  * element's UpdateManager load indicator and will be destroyed after the initial load.
7389  * @constructor
7390  * Create a new LoadMask
7391  * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
7392  * @param {Object} config The config object
7393  */
7394 Roo.LoadMask = function(el, config){
7395     this.el = Roo.get(el);
7396     Roo.apply(this, config);
7397     if(this.store){
7398         this.store.on('beforeload', this.onBeforeLoad, this);
7399         this.store.on('load', this.onLoad, this);
7400         this.store.on('loadexception', this.onLoadException, this);
7401         this.removeMask = false;
7402     }else{
7403         var um = this.el.getUpdateManager();
7404         um.showLoadIndicator = false; // disable the default indicator
7405         um.on('beforeupdate', this.onBeforeLoad, this);
7406         um.on('update', this.onLoad, this);
7407         um.on('failure', this.onLoad, this);
7408         this.removeMask = true;
7409     }
7410 };
7411
7412 Roo.LoadMask.prototype = {
7413     /**
7414      * @cfg {Boolean} removeMask
7415      * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
7416      * False to persist the mask element reference for multiple uses (e.g., for paged data widgets).  Defaults to false.
7417      */
7418     /**
7419      * @cfg {String} msg
7420      * The text to display in a centered loading message box (defaults to 'Loading...')
7421      */
7422     msg : 'Loading...',
7423     /**
7424      * @cfg {String} msgCls
7425      * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
7426      */
7427     msgCls : 'x-mask-loading',
7428
7429     /**
7430      * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
7431      * @type Boolean
7432      */
7433     disabled: false,
7434
7435     /**
7436      * Disables the mask to prevent it from being displayed
7437      */
7438     disable : function(){
7439        this.disabled = true;
7440     },
7441
7442     /**
7443      * Enables the mask so that it can be displayed
7444      */
7445     enable : function(){
7446         this.disabled = false;
7447     },
7448     
7449     onLoadException : function()
7450     {
7451         Roo.log(arguments);
7452         
7453         if (typeof(arguments[3]) != 'undefined') {
7454             Roo.MessageBox.alert("Error loading",arguments[3]);
7455         } 
7456         /*
7457         try {
7458             if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
7459                 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
7460             }   
7461         } catch(e) {
7462             
7463         }
7464         */
7465     
7466         (function() { this.el.unmask(this.removeMask); }).defer(50, this);
7467     },
7468     // private
7469     onLoad : function()
7470     {
7471         (function() { this.el.unmask(this.removeMask); }).defer(50, this);
7472     },
7473
7474     // private
7475     onBeforeLoad : function(){
7476         if(!this.disabled){
7477             (function() { this.el.mask(this.msg, this.msgCls); }).defer(50, this);
7478         }
7479     },
7480
7481     // private
7482     destroy : function(){
7483         if(this.store){
7484             this.store.un('beforeload', this.onBeforeLoad, this);
7485             this.store.un('load', this.onLoad, this);
7486             this.store.un('loadexception', this.onLoadException, this);
7487         }else{
7488             var um = this.el.getUpdateManager();
7489             um.un('beforeupdate', this.onBeforeLoad, this);
7490             um.un('update', this.onLoad, this);
7491             um.un('failure', this.onLoad, this);
7492         }
7493     }
7494 };/*
7495  * - LGPL
7496  *
7497  * table
7498  * 
7499  */
7500
7501 /**
7502  * @class Roo.bootstrap.Table
7503  * @extends Roo.bootstrap.Component
7504  * Bootstrap Table class
7505  * @cfg {String} cls table class
7506  * @cfg {String} align (left|center|right) Specifies the alignment of a table according to surrounding text
7507  * @cfg {String} bgcolor Specifies the background color for a table
7508  * @cfg {Number} border Specifies whether the table cells should have borders or not
7509  * @cfg {Number} cellpadding Specifies the space between the cell wall and the cell content
7510  * @cfg {Number} cellspacing Specifies the space between cells
7511  * @cfg {String} frame Specifies which parts of the outside borders that should be visible
7512  * @cfg {String} rules Specifies which parts of the inside borders that should be visible
7513  * @cfg {String} sortable Specifies that the table should be sortable
7514  * @cfg {String} summary Specifies a summary of the content of a table
7515  * @cfg {Number} width Specifies the width of a table
7516  * @cfg {String} layout table layout (auto | fixed | initial | inherit)
7517  * 
7518  * @cfg {boolean} striped Should the rows be alternative striped
7519  * @cfg {boolean} bordered Add borders to the table
7520  * @cfg {boolean} hover Add hover highlighting
7521  * @cfg {boolean} condensed Format condensed
7522  * @cfg {boolean} responsive Format condensed
7523  * @cfg {Boolean} loadMask (true|false) default false
7524  * @cfg {Boolean} footerShow (true|false) generate tfoot, default true
7525  * @cfg {Boolean} headerShow (true|false) generate thead, default true
7526  * @cfg {Boolean} rowSelection (true|false) default false
7527  * @cfg {Boolean} cellSelection (true|false) default false
7528  * @cfg {Boolean} scrollBody (true|false) default false - body scrolled / fixed header
7529  * @cfg {Roo.bootstrap.PagingToolbar} footer  a paging toolbar
7530  * @cfg {Boolean} lazyLoad  auto load data while scrolling to the end (default false)
7531  * @cfg {Boolean} auto_hide_footer  auto hide footer if only one page (default false)
7532  
7533  * 
7534  * @constructor
7535  * Create a new Table
7536  * @param {Object} config The config object
7537  */
7538
7539 Roo.bootstrap.Table = function(config){
7540     Roo.bootstrap.Table.superclass.constructor.call(this, config);
7541     
7542   
7543     
7544     // BC...
7545     this.rowSelection = (typeof(config.rowSelection) != 'undefined') ? config.rowSelection : this.rowSelection;
7546     this.cellSelection = (typeof(config.cellSelection) != 'undefined') ? config.cellSelection : this.cellSelection;
7547     this.headerShow = (typeof(config.thead) != 'undefined') ? config.thead : this.headerShow;
7548     this.footerShow = (typeof(config.tfoot) != 'undefined') ? config.tfoot : this.footerShow;
7549     
7550     this.sm = this.sm || {xtype: 'RowSelectionModel'};
7551     if (this.sm) {
7552         this.sm.grid = this;
7553         this.selModel = Roo.factory(this.sm, Roo.bootstrap.Table);
7554         this.sm = this.selModel;
7555         this.sm.xmodule = this.xmodule || false;
7556     }
7557     
7558     if (this.cm && typeof(this.cm.config) == 'undefined') {
7559         this.colModel = new Roo.grid.ColumnModel(this.cm);
7560         this.cm = this.colModel;
7561         this.cm.xmodule = this.xmodule || false;
7562     }
7563     if (this.store) {
7564         this.store= Roo.factory(this.store, Roo.data);
7565         this.ds = this.store;
7566         this.ds.xmodule = this.xmodule || false;
7567          
7568     }
7569     if (this.footer && this.store) {
7570         this.footer.dataSource = this.ds;
7571         this.footer = Roo.factory(this.footer);
7572     }
7573     
7574     /** @private */
7575     this.addEvents({
7576         /**
7577          * @event cellclick
7578          * Fires when a cell is clicked
7579          * @param {Roo.bootstrap.Table} this
7580          * @param {Roo.Element} el
7581          * @param {Number} rowIndex
7582          * @param {Number} columnIndex
7583          * @param {Roo.EventObject} e
7584          */
7585         "cellclick" : true,
7586         /**
7587          * @event celldblclick
7588          * Fires when a cell is double clicked
7589          * @param {Roo.bootstrap.Table} this
7590          * @param {Roo.Element} el
7591          * @param {Number} rowIndex
7592          * @param {Number} columnIndex
7593          * @param {Roo.EventObject} e
7594          */
7595         "celldblclick" : true,
7596         /**
7597          * @event rowclick
7598          * Fires when a row is clicked
7599          * @param {Roo.bootstrap.Table} this
7600          * @param {Roo.Element} el
7601          * @param {Number} rowIndex
7602          * @param {Roo.EventObject} e
7603          */
7604         "rowclick" : true,
7605         /**
7606          * @event rowdblclick
7607          * Fires when a row is double clicked
7608          * @param {Roo.bootstrap.Table} this
7609          * @param {Roo.Element} el
7610          * @param {Number} rowIndex
7611          * @param {Roo.EventObject} e
7612          */
7613         "rowdblclick" : true,
7614         /**
7615          * @event mouseover
7616          * Fires when a mouseover occur
7617          * @param {Roo.bootstrap.Table} this
7618          * @param {Roo.Element} el
7619          * @param {Number} rowIndex
7620          * @param {Number} columnIndex
7621          * @param {Roo.EventObject} e
7622          */
7623         "mouseover" : true,
7624         /**
7625          * @event mouseout
7626          * Fires when a mouseout occur
7627          * @param {Roo.bootstrap.Table} this
7628          * @param {Roo.Element} el
7629          * @param {Number} rowIndex
7630          * @param {Number} columnIndex
7631          * @param {Roo.EventObject} e
7632          */
7633         "mouseout" : true,
7634         /**
7635          * @event rowclass
7636          * Fires when a row is rendered, so you can change add a style to it.
7637          * @param {Roo.bootstrap.Table} this
7638          * @param {Object} rowcfg   contains record  rowIndex colIndex and rowClass - set rowClass to add a style.
7639          */
7640         'rowclass' : true,
7641           /**
7642          * @event rowsrendered
7643          * Fires when all the  rows have been rendered
7644          * @param {Roo.bootstrap.Table} this
7645          */
7646         'rowsrendered' : true,
7647         /**
7648          * @event contextmenu
7649          * The raw contextmenu event for the entire grid.
7650          * @param {Roo.EventObject} e
7651          */
7652         "contextmenu" : true,
7653         /**
7654          * @event rowcontextmenu
7655          * Fires when a row is right clicked
7656          * @param {Roo.bootstrap.Table} this
7657          * @param {Number} rowIndex
7658          * @param {Roo.EventObject} e
7659          */
7660         "rowcontextmenu" : true,
7661         /**
7662          * @event cellcontextmenu
7663          * Fires when a cell is right clicked
7664          * @param {Roo.bootstrap.Table} this
7665          * @param {Number} rowIndex
7666          * @param {Number} cellIndex
7667          * @param {Roo.EventObject} e
7668          */
7669          "cellcontextmenu" : true,
7670          /**
7671          * @event headercontextmenu
7672          * Fires when a header is right clicked
7673          * @param {Roo.bootstrap.Table} this
7674          * @param {Number} columnIndex
7675          * @param {Roo.EventObject} e
7676          */
7677         "headercontextmenu" : true
7678     });
7679 };
7680
7681 Roo.extend(Roo.bootstrap.Table, Roo.bootstrap.Component,  {
7682     
7683     cls: false,
7684     align: false,
7685     bgcolor: false,
7686     border: false,
7687     cellpadding: false,
7688     cellspacing: false,
7689     frame: false,
7690     rules: false,
7691     sortable: false,
7692     summary: false,
7693     width: false,
7694     striped : false,
7695     scrollBody : false,
7696     bordered: false,
7697     hover:  false,
7698     condensed : false,
7699     responsive : false,
7700     sm : false,
7701     cm : false,
7702     store : false,
7703     loadMask : false,
7704     footerShow : true,
7705     headerShow : true,
7706   
7707     rowSelection : false,
7708     cellSelection : false,
7709     layout : false,
7710     
7711     // Roo.Element - the tbody
7712     mainBody: false,
7713     // Roo.Element - thead element
7714     mainHead: false,
7715     
7716     container: false, // used by gridpanel...
7717     
7718     lazyLoad : false,
7719     
7720     CSS : Roo.util.CSS,
7721     
7722     auto_hide_footer : false,
7723     
7724     getAutoCreate : function()
7725     {
7726         var cfg = Roo.apply({}, Roo.bootstrap.Table.superclass.getAutoCreate.call(this));
7727         
7728         cfg = {
7729             tag: 'table',
7730             cls : 'table',
7731             cn : []
7732         };
7733         if (this.scrollBody) {
7734             cfg.cls += ' table-body-fixed';
7735         }    
7736         if (this.striped) {
7737             cfg.cls += ' table-striped';
7738         }
7739         
7740         if (this.hover) {
7741             cfg.cls += ' table-hover';
7742         }
7743         if (this.bordered) {
7744             cfg.cls += ' table-bordered';
7745         }
7746         if (this.condensed) {
7747             cfg.cls += ' table-condensed';
7748         }
7749         if (this.responsive) {
7750             cfg.cls += ' table-responsive';
7751         }
7752         
7753         if (this.cls) {
7754             cfg.cls+=  ' ' +this.cls;
7755         }
7756         
7757         // this lot should be simplifed...
7758         var _t = this;
7759         var cp = [
7760             'align',
7761             'bgcolor',
7762             'border',
7763             'cellpadding',
7764             'cellspacing',
7765             'frame',
7766             'rules',
7767             'sortable',
7768             'summary',
7769             'width'
7770         ].forEach(function(k) {
7771             if (_t[k]) {
7772                 cfg[k] = _t[k];
7773             }
7774         });
7775         
7776         
7777         if (this.layout) {
7778             cfg.style = (typeof(cfg.style) == 'undefined') ? ('table-layout:' + this.layout + ';') : (cfg.style + ('table-layout:' + this.layout + ';'));
7779         }
7780         
7781         if(this.store || this.cm){
7782             if(this.headerShow){
7783                 cfg.cn.push(this.renderHeader());
7784             }
7785             
7786             cfg.cn.push(this.renderBody());
7787             
7788             if(this.footerShow){
7789                 cfg.cn.push(this.renderFooter());
7790             }
7791             // where does this come from?
7792             //cfg.cls+=  ' TableGrid';
7793         }
7794         
7795         return { cn : [ cfg ] };
7796     },
7797     
7798     initEvents : function()
7799     {   
7800         if(!this.store || !this.cm){
7801             return;
7802         }
7803         if (this.selModel) {
7804             this.selModel.initEvents();
7805         }
7806         
7807         
7808         //Roo.log('initEvents with ds!!!!');
7809         
7810         this.mainBody = this.el.select('tbody', true).first();
7811         this.mainHead = this.el.select('thead', true).first();
7812         this.mainFoot = this.el.select('tfoot', true).first();
7813         
7814         
7815         
7816         var _this = this;
7817         
7818         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
7819             e.on('click', _this.sort, _this);
7820         });
7821         
7822         this.mainBody.on("click", this.onClick, this);
7823         this.mainBody.on("dblclick", this.onDblClick, this);
7824         
7825         // why is this done????? = it breaks dialogs??
7826         //this.parent().el.setStyle('position', 'relative');
7827         
7828         
7829         if (this.footer) {
7830             this.footer.parentId = this.id;
7831             this.footer.onRender(this.el.select('tfoot tr td').first(), null);
7832             
7833             if(this.lazyLoad){
7834                 this.el.select('tfoot tr td').first().addClass('hide');
7835             }
7836         } 
7837         
7838         if(this.loadMask) {
7839             this.maskEl = new Roo.LoadMask(this.el, { store : this.ds, msgCls: 'roo-el-mask-msg' });
7840         }
7841         
7842         this.store.on('load', this.onLoad, this);
7843         this.store.on('beforeload', this.onBeforeLoad, this);
7844         this.store.on('update', this.onUpdate, this);
7845         this.store.on('add', this.onAdd, this);
7846         this.store.on("clear", this.clear, this);
7847         
7848         this.el.on("contextmenu", this.onContextMenu, this);
7849         
7850         this.mainBody.on('scroll', this.onBodyScroll, this);
7851         
7852         this.cm.on("headerchange", this.onHeaderChange, this);
7853         
7854         this.cm.on("hiddenchange", this.onHiddenChange, this, arguments);
7855         
7856     },
7857     
7858     onContextMenu : function(e, t)
7859     {
7860         this.processEvent("contextmenu", e);
7861     },
7862     
7863     processEvent : function(name, e)
7864     {
7865         if (name != 'touchstart' ) {
7866             this.fireEvent(name, e);    
7867         }
7868         
7869         var t = e.getTarget();
7870         
7871         var cell = Roo.get(t);
7872         
7873         if(!cell){
7874             return;
7875         }
7876         
7877         if(cell.findParent('tfoot', false, true)){
7878             return;
7879         }
7880         
7881         if(cell.findParent('thead', false, true)){
7882             
7883             if(e.getTarget().nodeName.toLowerCase() != 'th'){
7884                 cell = Roo.get(t).findParent('th', false, true);
7885                 if (!cell) {
7886                     Roo.log("failed to find th in thead?");
7887                     Roo.log(e.getTarget());
7888                     return;
7889                 }
7890             }
7891             
7892             var cellIndex = cell.dom.cellIndex;
7893             
7894             var ename = name == 'touchstart' ? 'click' : name;
7895             this.fireEvent("header" + ename, this, cellIndex, e);
7896             
7897             return;
7898         }
7899         
7900         if(e.getTarget().nodeName.toLowerCase() != 'td'){
7901             cell = Roo.get(t).findParent('td', false, true);
7902             if (!cell) {
7903                 Roo.log("failed to find th in tbody?");
7904                 Roo.log(e.getTarget());
7905                 return;
7906             }
7907         }
7908         
7909         var row = cell.findParent('tr', false, true);
7910         var cellIndex = cell.dom.cellIndex;
7911         var rowIndex = row.dom.rowIndex - 1;
7912         
7913         if(row !== false){
7914             
7915             this.fireEvent("row" + name, this, rowIndex, e);
7916             
7917             if(cell !== false){
7918             
7919                 this.fireEvent("cell" + name, this, rowIndex, cellIndex, e);
7920             }
7921         }
7922         
7923     },
7924     
7925     onMouseover : function(e, el)
7926     {
7927         var cell = Roo.get(el);
7928         
7929         if(!cell){
7930             return;
7931         }
7932         
7933         if(e.getTarget().nodeName.toLowerCase() != 'td'){
7934             cell = cell.findParent('td', false, true);
7935         }
7936         
7937         var row = cell.findParent('tr', false, true);
7938         var cellIndex = cell.dom.cellIndex;
7939         var rowIndex = row.dom.rowIndex - 1; // start from 0
7940         
7941         this.fireEvent('mouseover', this, cell, rowIndex, cellIndex, e);
7942         
7943     },
7944     
7945     onMouseout : function(e, el)
7946     {
7947         var cell = Roo.get(el);
7948         
7949         if(!cell){
7950             return;
7951         }
7952         
7953         if(e.getTarget().nodeName.toLowerCase() != 'td'){
7954             cell = cell.findParent('td', false, true);
7955         }
7956         
7957         var row = cell.findParent('tr', false, true);
7958         var cellIndex = cell.dom.cellIndex;
7959         var rowIndex = row.dom.rowIndex - 1; // start from 0
7960         
7961         this.fireEvent('mouseout', this, cell, rowIndex, cellIndex, e);
7962         
7963     },
7964     
7965     onClick : function(e, el)
7966     {
7967         var cell = Roo.get(el);
7968         
7969         if(!cell || (!this.cellSelection && !this.rowSelection)){
7970             return;
7971         }
7972         
7973         if(e.getTarget().nodeName.toLowerCase() != 'td'){
7974             cell = cell.findParent('td', false, true);
7975         }
7976         
7977         if(!cell || typeof(cell) == 'undefined'){
7978             return;
7979         }
7980         
7981         var row = cell.findParent('tr', false, true);
7982         
7983         if(!row || typeof(row) == 'undefined'){
7984             return;
7985         }
7986         
7987         var cellIndex = cell.dom.cellIndex;
7988         var rowIndex = this.getRowIndex(row);
7989         
7990         // why??? - should these not be based on SelectionModel?
7991         if(this.cellSelection){
7992             this.fireEvent('cellclick', this, cell, rowIndex, cellIndex, e);
7993         }
7994         
7995         if(this.rowSelection){
7996             this.fireEvent('rowclick', this, row, rowIndex, e);
7997         }
7998         
7999         
8000     },
8001         
8002     onDblClick : function(e,el)
8003     {
8004         var cell = Roo.get(el);
8005         
8006         if(!cell || (!this.cellSelection && !this.rowSelection)){
8007             return;
8008         }
8009         
8010         if(e.getTarget().nodeName.toLowerCase() != 'td'){
8011             cell = cell.findParent('td', false, true);
8012         }
8013         
8014         if(!cell || typeof(cell) == 'undefined'){
8015             return;
8016         }
8017         
8018         var row = cell.findParent('tr', false, true);
8019         
8020         if(!row || typeof(row) == 'undefined'){
8021             return;
8022         }
8023         
8024         var cellIndex = cell.dom.cellIndex;
8025         var rowIndex = this.getRowIndex(row);
8026         
8027         if(this.cellSelection){
8028             this.fireEvent('celldblclick', this, cell, rowIndex, cellIndex, e);
8029         }
8030         
8031         if(this.rowSelection){
8032             this.fireEvent('rowdblclick', this, row, rowIndex, e);
8033         }
8034     },
8035     
8036     sort : function(e,el)
8037     {
8038         var col = Roo.get(el);
8039         
8040         if(!col.hasClass('sortable')){
8041             return;
8042         }
8043         
8044         var sort = col.attr('sort');
8045         var dir = 'ASC';
8046         
8047         if(col.select('i', true).first().hasClass('glyphicon-arrow-up')){
8048             dir = 'DESC';
8049         }
8050         
8051         this.store.sortInfo = {field : sort, direction : dir};
8052         
8053         if (this.footer) {
8054             Roo.log("calling footer first");
8055             this.footer.onClick('first');
8056         } else {
8057         
8058             this.store.load({ params : { start : 0 } });
8059         }
8060     },
8061     
8062     renderHeader : function()
8063     {
8064         var header = {
8065             tag: 'thead',
8066             cn : []
8067         };
8068         
8069         var cm = this.cm;
8070         this.totalWidth = 0;
8071         
8072         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
8073             
8074             var config = cm.config[i];
8075             
8076             var c = {
8077                 tag: 'th',
8078                 cls : 'x-hcol-' + i,
8079                 style : '',
8080                 html: cm.getColumnHeader(i)
8081             };
8082             
8083             var hh = '';
8084             
8085             if(typeof(config.sortable) != 'undefined' && config.sortable){
8086                 c.cls = 'sortable';
8087                 c.html = '<i class="glyphicon"></i>' + c.html;
8088             }
8089             
8090             // could use BS4 hidden-..-down 
8091             
8092             if(typeof(config.lgHeader) != 'undefined'){
8093                 hh += '<span class="hidden-xs hidden-sm hidden-md ">' + config.lgHeader + '</span>';
8094             }
8095             
8096             if(typeof(config.mdHeader) != 'undefined'){
8097                 hh += '<span class="hidden-xs hidden-sm hidden-lg">' + config.mdHeader + '</span>';
8098             }
8099             
8100             if(typeof(config.smHeader) != 'undefined'){
8101                 hh += '<span class="hidden-xs hidden-md hidden-lg">' + config.smHeader + '</span>';
8102             }
8103             
8104             if(typeof(config.xsHeader) != 'undefined'){
8105                 hh += '<span class="hidden-sm hidden-md hidden-lg">' + config.xsHeader + '</span>';
8106             }
8107             
8108             if(hh.length){
8109                 c.html = hh;
8110             }
8111             
8112             if(typeof(config.tooltip) != 'undefined'){
8113                 c.tooltip = config.tooltip;
8114             }
8115             
8116             if(typeof(config.colspan) != 'undefined'){
8117                 c.colspan = config.colspan;
8118             }
8119             
8120             if(typeof(config.hidden) != 'undefined' && config.hidden){
8121                 c.style += ' display:none;';
8122             }
8123             
8124             if(typeof(config.dataIndex) != 'undefined'){
8125                 c.sort = config.dataIndex;
8126             }
8127             
8128            
8129             
8130             if(typeof(config.align) != 'undefined' && config.align.length){
8131                 c.style += ' text-align:' + config.align + ';';
8132             }
8133             
8134             if(typeof(config.width) != 'undefined'){
8135                 c.style += ' width:' + config.width + 'px;';
8136                 this.totalWidth += config.width;
8137             } else {
8138                 this.totalWidth += 100; // assume minimum of 100 per column?
8139             }
8140             
8141             if(typeof(config.cls) != 'undefined'){
8142                 c.cls = (typeof(c.cls) == 'undefined') ? config.cls : (c.cls + ' ' + config.cls);
8143             }
8144             
8145             ['xs','sm','md','lg'].map(function(size){
8146                 
8147                 if(typeof(config[size]) == 'undefined'){
8148                     return;
8149                 }
8150                  
8151                 if (!config[size]) { // 0 = hidden
8152                     // BS 4 '0' is treated as hide that column and below.
8153                     c.cls += ' hidden-' + size + ' hidden' + size + '-down';
8154                     return;
8155                 }
8156                 
8157                 c.cls += ' col-' + size + '-' + config[size] + (
8158                     size == 'xs' ? (' col-' + config[size] ) : '' // bs4 col-{num} replaces col-xs
8159                 );
8160                 
8161                 
8162             });
8163             
8164             header.cn.push(c)
8165         }
8166         
8167         return header;
8168     },
8169     
8170     renderBody : function()
8171     {
8172         var body = {
8173             tag: 'tbody',
8174             cn : [
8175                 {
8176                     tag: 'tr',
8177                     cn : [
8178                         {
8179                             tag : 'td',
8180                             colspan :  this.cm.getColumnCount()
8181                         }
8182                     ]
8183                 }
8184             ]
8185         };
8186         
8187         return body;
8188     },
8189     
8190     renderFooter : function()
8191     {
8192         var footer = {
8193             tag: 'tfoot',
8194             cn : [
8195                 {
8196                     tag: 'tr',
8197                     cn : [
8198                         {
8199                             tag : 'td',
8200                             colspan :  this.cm.getColumnCount()
8201                         }
8202                     ]
8203                 }
8204             ]
8205         };
8206         
8207         return footer;
8208     },
8209     
8210     
8211     
8212     onLoad : function()
8213     {
8214 //        Roo.log('ds onload');
8215         this.clear();
8216         
8217         var _this = this;
8218         var cm = this.cm;
8219         var ds = this.store;
8220         
8221         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
8222             e.select('i', true).removeClass(['glyphicon-arrow-up', 'glyphicon-arrow-down']);
8223             if (_this.store.sortInfo) {
8224                     
8225                 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'ASC'){
8226                     e.select('i', true).addClass(['glyphicon-arrow-up']);
8227                 }
8228                 
8229                 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'DESC'){
8230                     e.select('i', true).addClass(['glyphicon-arrow-down']);
8231                 }
8232             }
8233         });
8234         
8235         var tbody =  this.mainBody;
8236               
8237         if(ds.getCount() > 0){
8238             ds.data.each(function(d,rowIndex){
8239                 var row =  this.renderRow(cm, ds, rowIndex);
8240                 
8241                 tbody.createChild(row);
8242                 
8243                 var _this = this;
8244                 
8245                 if(row.cellObjects.length){
8246                     Roo.each(row.cellObjects, function(r){
8247                         _this.renderCellObject(r);
8248                     })
8249                 }
8250                 
8251             }, this);
8252         }
8253         
8254         var tfoot = this.el.select('tfoot', true).first();
8255         
8256         if(this.footerShow && this.auto_hide_footer && this.mainFoot){
8257             
8258             this.mainFoot.setVisibilityMode(Roo.Element.DISPLAY).hide();
8259             
8260             var total = this.ds.getTotalCount();
8261             
8262             if(this.footer.pageSize < total){
8263                 this.mainFoot.show();
8264             }
8265         }
8266         
8267         Roo.each(this.el.select('tbody td', true).elements, function(e){
8268             e.on('mouseover', _this.onMouseover, _this);
8269         });
8270         
8271         Roo.each(this.el.select('tbody td', true).elements, function(e){
8272             e.on('mouseout', _this.onMouseout, _this);
8273         });
8274         this.fireEvent('rowsrendered', this);
8275         
8276         this.autoSize();
8277     },
8278     
8279     
8280     onUpdate : function(ds,record)
8281     {
8282         this.refreshRow(record);
8283         this.autoSize();
8284     },
8285     
8286     onRemove : function(ds, record, index, isUpdate){
8287         if(isUpdate !== true){
8288             this.fireEvent("beforerowremoved", this, index, record);
8289         }
8290         var bt = this.mainBody.dom;
8291         
8292         var rows = this.el.select('tbody > tr', true).elements;
8293         
8294         if(typeof(rows[index]) != 'undefined'){
8295             bt.removeChild(rows[index].dom);
8296         }
8297         
8298 //        if(bt.rows[index]){
8299 //            bt.removeChild(bt.rows[index]);
8300 //        }
8301         
8302         if(isUpdate !== true){
8303             //this.stripeRows(index);
8304             //this.syncRowHeights(index, index);
8305             //this.layout();
8306             this.fireEvent("rowremoved", this, index, record);
8307         }
8308     },
8309     
8310     onAdd : function(ds, records, rowIndex)
8311     {
8312         //Roo.log('on Add called');
8313         // - note this does not handle multiple adding very well..
8314         var bt = this.mainBody.dom;
8315         for (var i =0 ; i < records.length;i++) {
8316             //Roo.log('call insert row Add called on ' + rowIndex + ':' + i);
8317             //Roo.log(records[i]);
8318             //Roo.log(this.store.getAt(rowIndex+i));
8319             this.insertRow(this.store, rowIndex + i, false);
8320             return;
8321         }
8322         
8323     },
8324     
8325     
8326     refreshRow : function(record){
8327         var ds = this.store, index;
8328         if(typeof record == 'number'){
8329             index = record;
8330             record = ds.getAt(index);
8331         }else{
8332             index = ds.indexOf(record);
8333             if (index < 0) {
8334                 return; // should not happen - but seems to 
8335             }
8336         }
8337         this.insertRow(ds, index, true);
8338         this.autoSize();
8339         this.onRemove(ds, record, index+1, true);
8340         this.autoSize();
8341         //this.syncRowHeights(index, index);
8342         //this.layout();
8343         this.fireEvent("rowupdated", this, index, record);
8344     },
8345     
8346     insertRow : function(dm, rowIndex, isUpdate){
8347         
8348         if(!isUpdate){
8349             this.fireEvent("beforerowsinserted", this, rowIndex);
8350         }
8351             //var s = this.getScrollState();
8352         var row = this.renderRow(this.cm, this.store, rowIndex);
8353         // insert before rowIndex..
8354         var e = this.mainBody.createChild(row,this.getRowDom(rowIndex));
8355         
8356         var _this = this;
8357                 
8358         if(row.cellObjects.length){
8359             Roo.each(row.cellObjects, function(r){
8360                 _this.renderCellObject(r);
8361             })
8362         }
8363             
8364         if(!isUpdate){
8365             this.fireEvent("rowsinserted", this, rowIndex);
8366             //this.syncRowHeights(firstRow, lastRow);
8367             //this.stripeRows(firstRow);
8368             //this.layout();
8369         }
8370         
8371     },
8372     
8373     
8374     getRowDom : function(rowIndex)
8375     {
8376         var rows = this.el.select('tbody > tr', true).elements;
8377         
8378         return (typeof(rows[rowIndex]) == 'undefined') ? false : rows[rowIndex];
8379         
8380     },
8381     // returns the object tree for a tr..
8382   
8383     
8384     renderRow : function(cm, ds, rowIndex) 
8385     {
8386         var d = ds.getAt(rowIndex);
8387         
8388         var row = {
8389             tag : 'tr',
8390             cls : 'x-row-' + rowIndex,
8391             cn : []
8392         };
8393             
8394         var cellObjects = [];
8395         
8396         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
8397             var config = cm.config[i];
8398             
8399             var renderer = cm.getRenderer(i);
8400             var value = '';
8401             var id = false;
8402             
8403             if(typeof(renderer) !== 'undefined'){
8404                 value = renderer(d.data[cm.getDataIndex(i)], false, d);
8405             }
8406             // if object are returned, then they are expected to be Roo.bootstrap.Component instances
8407             // and are rendered into the cells after the row is rendered - using the id for the element.
8408             
8409             if(typeof(value) === 'object'){
8410                 id = Roo.id();
8411                 cellObjects.push({
8412                     container : id,
8413                     cfg : value 
8414                 })
8415             }
8416             
8417             var rowcfg = {
8418                 record: d,
8419                 rowIndex : rowIndex,
8420                 colIndex : i,
8421                 rowClass : ''
8422             };
8423
8424             this.fireEvent('rowclass', this, rowcfg);
8425             
8426             var td = {
8427                 tag: 'td',
8428                 cls : rowcfg.rowClass + ' x-col-' + i,
8429                 style: '',
8430                 html: (typeof(value) === 'object') ? '' : value
8431             };
8432             
8433             if (id) {
8434                 td.id = id;
8435             }
8436             
8437             if(typeof(config.colspan) != 'undefined'){
8438                 td.colspan = config.colspan;
8439             }
8440             
8441             if(typeof(config.hidden) != 'undefined' && config.hidden){
8442                 td.style += ' display:none;';
8443             }
8444             
8445             if(typeof(config.align) != 'undefined' && config.align.length){
8446                 td.style += ' text-align:' + config.align + ';';
8447             }
8448             if(typeof(config.valign) != 'undefined' && config.valign.length){
8449                 td.style += ' vertical-align:' + config.valign + ';';
8450             }
8451             
8452             if(typeof(config.width) != 'undefined'){
8453                 td.style += ' width:' +  config.width + 'px;';
8454             }
8455             
8456             if(typeof(config.cursor) != 'undefined'){
8457                 td.style += ' cursor:' +  config.cursor + ';';
8458             }
8459             
8460             if(typeof(config.cls) != 'undefined'){
8461                 td.cls = (typeof(td.cls) == 'undefined') ? config.cls : (td.cls + ' ' + config.cls);
8462             }
8463             
8464             ['xs','sm','md','lg'].map(function(size){
8465                 
8466                 if(typeof(config[size]) == 'undefined'){
8467                     return;
8468                 }
8469                 
8470                 
8471                   
8472                 if (!config[size]) { // 0 = hidden
8473                     // BS 4 '0' is treated as hide that column and below.
8474                     td.cls += ' hidden-' + size + ' hidden' + size + '-down';
8475                     return;
8476                 }
8477                 
8478                 td.cls += ' col-' + size + '-' + config[size] + (
8479                     size == 'xs' ? (' col-' +   config[size] ) : '' // bs4 col-{num} replaces col-xs
8480                 );
8481                  
8482
8483             });
8484             
8485             row.cn.push(td);
8486            
8487         }
8488         
8489         row.cellObjects = cellObjects;
8490         
8491         return row;
8492           
8493     },
8494     
8495     
8496     
8497     onBeforeLoad : function()
8498     {
8499         
8500     },
8501      /**
8502      * Remove all rows
8503      */
8504     clear : function()
8505     {
8506         this.el.select('tbody', true).first().dom.innerHTML = '';
8507     },
8508     /**
8509      * Show or hide a row.
8510      * @param {Number} rowIndex to show or hide
8511      * @param {Boolean} state hide
8512      */
8513     setRowVisibility : function(rowIndex, state)
8514     {
8515         var bt = this.mainBody.dom;
8516         
8517         var rows = this.el.select('tbody > tr', true).elements;
8518         
8519         if(typeof(rows[rowIndex]) == 'undefined'){
8520             return;
8521         }
8522         rows[rowIndex].dom.style.display = state ? '' : 'none';
8523     },
8524     
8525     
8526     getSelectionModel : function(){
8527         if(!this.selModel){
8528             this.selModel = new Roo.bootstrap.Table.RowSelectionModel({grid: this});
8529         }
8530         return this.selModel;
8531     },
8532     /*
8533      * Render the Roo.bootstrap object from renderder
8534      */
8535     renderCellObject : function(r)
8536     {
8537         var _this = this;
8538         
8539         r.cfg.parentId = (typeof(r.container) == 'string') ? r.container : r.container.id;
8540         
8541         var t = r.cfg.render(r.container);
8542         
8543         if(r.cfg.cn){
8544             Roo.each(r.cfg.cn, function(c){
8545                 var child = {
8546                     container: t.getChildContainer(),
8547                     cfg: c
8548                 };
8549                 _this.renderCellObject(child);
8550             })
8551         }
8552     },
8553     
8554     getRowIndex : function(row)
8555     {
8556         var rowIndex = -1;
8557         
8558         Roo.each(this.el.select('tbody > tr', true).elements, function(el, index){
8559             if(el != row){
8560                 return;
8561             }
8562             
8563             rowIndex = index;
8564         });
8565         
8566         return rowIndex;
8567     },
8568      /**
8569      * Returns the grid's underlying element = used by panel.Grid
8570      * @return {Element} The element
8571      */
8572     getGridEl : function(){
8573         return this.el;
8574     },
8575      /**
8576      * Forces a resize - used by panel.Grid
8577      * @return {Element} The element
8578      */
8579     autoSize : function()
8580     {
8581         //var ctr = Roo.get(this.container.dom.parentElement);
8582         var ctr = Roo.get(this.el.dom);
8583         
8584         var thd = this.getGridEl().select('thead',true).first();
8585         var tbd = this.getGridEl().select('tbody', true).first();
8586         var tfd = this.getGridEl().select('tfoot', true).first();
8587         
8588         var cw = ctr.getWidth();
8589         
8590         if (tbd) {
8591             
8592             tbd.setWidth(ctr.getWidth());
8593             // if the body has a max height - and then scrolls - we should perhaps set up the height here
8594             // this needs fixing for various usage - currently only hydra job advers I think..
8595             //tdb.setHeight(
8596             //        ctr.getHeight() - ((thd ? thd.getHeight() : 0) + (tfd ? tfd.getHeight() : 0))
8597             //); 
8598             var barsize = (tbd.dom.offsetWidth - tbd.dom.clientWidth);
8599             cw -= barsize;
8600         }
8601         cw = Math.max(cw, this.totalWidth);
8602         this.getGridEl().select('tr',true).setWidth(cw);
8603         // resize 'expandable coloumn?
8604         
8605         return; // we doe not have a view in this design..
8606         
8607     },
8608     onBodyScroll: function()
8609     {
8610         //Roo.log("body scrolled');" + this.mainBody.dom.scrollLeft);
8611         if(this.mainHead){
8612             this.mainHead.setStyle({
8613                 'position' : 'relative',
8614                 'left': (-1* this.mainBody.dom.scrollLeft) + 'px'
8615             });
8616         }
8617         
8618         if(this.lazyLoad){
8619             
8620             var scrollHeight = this.mainBody.dom.scrollHeight;
8621             
8622             var scrollTop = Math.ceil(this.mainBody.getScroll().top);
8623             
8624             var height = this.mainBody.getHeight();
8625             
8626             if(scrollHeight - height == scrollTop) {
8627                 
8628                 var total = this.ds.getTotalCount();
8629                 
8630                 if(this.footer.cursor + this.footer.pageSize < total){
8631                     
8632                     this.footer.ds.load({
8633                         params : {
8634                             start : this.footer.cursor + this.footer.pageSize,
8635                             limit : this.footer.pageSize
8636                         },
8637                         add : true
8638                     });
8639                 }
8640             }
8641             
8642         }
8643     },
8644     
8645     onHeaderChange : function()
8646     {
8647         var header = this.renderHeader();
8648         var table = this.el.select('table', true).first();
8649         
8650         this.mainHead.remove();
8651         this.mainHead = table.createChild(header, this.mainBody, false);
8652     },
8653     
8654     onHiddenChange : function(colModel, colIndex, hidden)
8655     {
8656         var thSelector = '#' + this.id + ' .x-hcol-' + colIndex;
8657         var tdSelector = '#' + this.id + ' .x-col-' + colIndex;
8658         
8659         this.CSS.updateRule(thSelector, "display", "");
8660         this.CSS.updateRule(tdSelector, "display", "");
8661         
8662         if(hidden){
8663             this.CSS.updateRule(thSelector, "display", "none");
8664             this.CSS.updateRule(tdSelector, "display", "none");
8665         }
8666         
8667         this.onHeaderChange();
8668         this.onLoad();
8669     },
8670     
8671     setColumnWidth: function(col_index, width)
8672     {
8673         // width = "md-2 xs-2..."
8674         if(!this.colModel.config[col_index]) {
8675             return;
8676         }
8677         
8678         var w = width.split(" ");
8679         
8680         var rows = this.el.dom.getElementsByClassName("x-col-"+col_index);
8681         
8682         var h_row = this.el.dom.getElementsByClassName("x-hcol-"+col_index);
8683         
8684         
8685         for(var j = 0; j < w.length; j++) {
8686             
8687             if(!w[j]) {
8688                 continue;
8689             }
8690             
8691             var size_cls = w[j].split("-");
8692             
8693             if(!Number.isInteger(size_cls[1] * 1)) {
8694                 continue;
8695             }
8696             
8697             if(!this.colModel.config[col_index][size_cls[0]]) {
8698                 continue;
8699             }
8700             
8701             if(!h_row[0].classList.contains("col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]])) {
8702                 continue;
8703             }
8704             
8705             h_row[0].classList.replace(
8706                 "col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]],
8707                 "col-"+size_cls[0]+"-"+size_cls[1]
8708             );
8709             
8710             for(var i = 0; i < rows.length; i++) {
8711                 
8712                 var size_cls = w[j].split("-");
8713                 
8714                 if(!Number.isInteger(size_cls[1] * 1)) {
8715                     continue;
8716                 }
8717                 
8718                 if(!this.colModel.config[col_index][size_cls[0]]) {
8719                     continue;
8720                 }
8721                 
8722                 if(!rows[i].classList.contains("col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]])) {
8723                     continue;
8724                 }
8725                 
8726                 rows[i].classList.replace(
8727                     "col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]],
8728                     "col-"+size_cls[0]+"-"+size_cls[1]
8729                 );
8730             }
8731             
8732             this.colModel.config[col_index][size_cls[0]] = size_cls[1];
8733         }
8734     }
8735 });
8736
8737  
8738
8739  /*
8740  * - LGPL
8741  *
8742  * table cell
8743  * 
8744  */
8745
8746 /**
8747  * @class Roo.bootstrap.TableCell
8748  * @extends Roo.bootstrap.Component
8749  * Bootstrap TableCell class
8750  * @cfg {String} html cell contain text
8751  * @cfg {String} cls cell class
8752  * @cfg {String} tag cell tag (td|th) default td
8753  * @cfg {String} abbr Specifies an abbreviated version of the content in a cell
8754  * @cfg {String} align Aligns the content in a cell
8755  * @cfg {String} axis Categorizes cells
8756  * @cfg {String} bgcolor Specifies the background color of a cell
8757  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
8758  * @cfg {Number} colspan Specifies the number of columns a cell should span
8759  * @cfg {String} headers Specifies one or more header cells a cell is related to
8760  * @cfg {Number} height Sets the height of a cell
8761  * @cfg {String} nowrap Specifies that the content inside a cell should not wrap
8762  * @cfg {Number} rowspan Sets the number of rows a cell should span
8763  * @cfg {String} scope Defines a way to associate header cells and data cells in a table
8764  * @cfg {String} valign Vertical aligns the content in a cell
8765  * @cfg {Number} width Specifies the width of a cell
8766  * 
8767  * @constructor
8768  * Create a new TableCell
8769  * @param {Object} config The config object
8770  */
8771
8772 Roo.bootstrap.TableCell = function(config){
8773     Roo.bootstrap.TableCell.superclass.constructor.call(this, config);
8774 };
8775
8776 Roo.extend(Roo.bootstrap.TableCell, Roo.bootstrap.Component,  {
8777     
8778     html: false,
8779     cls: false,
8780     tag: false,
8781     abbr: false,
8782     align: false,
8783     axis: false,
8784     bgcolor: false,
8785     charoff: false,
8786     colspan: false,
8787     headers: false,
8788     height: false,
8789     nowrap: false,
8790     rowspan: false,
8791     scope: false,
8792     valign: false,
8793     width: false,
8794     
8795     
8796     getAutoCreate : function(){
8797         var cfg = Roo.apply({}, Roo.bootstrap.TableCell.superclass.getAutoCreate.call(this));
8798         
8799         cfg = {
8800             tag: 'td'
8801         };
8802         
8803         if(this.tag){
8804             cfg.tag = this.tag;
8805         }
8806         
8807         if (this.html) {
8808             cfg.html=this.html
8809         }
8810         if (this.cls) {
8811             cfg.cls=this.cls
8812         }
8813         if (this.abbr) {
8814             cfg.abbr=this.abbr
8815         }
8816         if (this.align) {
8817             cfg.align=this.align
8818         }
8819         if (this.axis) {
8820             cfg.axis=this.axis
8821         }
8822         if (this.bgcolor) {
8823             cfg.bgcolor=this.bgcolor
8824         }
8825         if (this.charoff) {
8826             cfg.charoff=this.charoff
8827         }
8828         if (this.colspan) {
8829             cfg.colspan=this.colspan
8830         }
8831         if (this.headers) {
8832             cfg.headers=this.headers
8833         }
8834         if (this.height) {
8835             cfg.height=this.height
8836         }
8837         if (this.nowrap) {
8838             cfg.nowrap=this.nowrap
8839         }
8840         if (this.rowspan) {
8841             cfg.rowspan=this.rowspan
8842         }
8843         if (this.scope) {
8844             cfg.scope=this.scope
8845         }
8846         if (this.valign) {
8847             cfg.valign=this.valign
8848         }
8849         if (this.width) {
8850             cfg.width=this.width
8851         }
8852         
8853         
8854         return cfg;
8855     }
8856    
8857 });
8858
8859  
8860
8861  /*
8862  * - LGPL
8863  *
8864  * table row
8865  * 
8866  */
8867
8868 /**
8869  * @class Roo.bootstrap.TableRow
8870  * @extends Roo.bootstrap.Component
8871  * Bootstrap TableRow class
8872  * @cfg {String} cls row class
8873  * @cfg {String} align Aligns the content in a table row
8874  * @cfg {String} bgcolor Specifies a background color for a table row
8875  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
8876  * @cfg {String} valign Vertical aligns the content in a table row
8877  * 
8878  * @constructor
8879  * Create a new TableRow
8880  * @param {Object} config The config object
8881  */
8882
8883 Roo.bootstrap.TableRow = function(config){
8884     Roo.bootstrap.TableRow.superclass.constructor.call(this, config);
8885 };
8886
8887 Roo.extend(Roo.bootstrap.TableRow, Roo.bootstrap.Component,  {
8888     
8889     cls: false,
8890     align: false,
8891     bgcolor: false,
8892     charoff: false,
8893     valign: false,
8894     
8895     getAutoCreate : function(){
8896         var cfg = Roo.apply({}, Roo.bootstrap.TableRow.superclass.getAutoCreate.call(this));
8897         
8898         cfg = {
8899             tag: 'tr'
8900         };
8901             
8902         if(this.cls){
8903             cfg.cls = this.cls;
8904         }
8905         if(this.align){
8906             cfg.align = this.align;
8907         }
8908         if(this.bgcolor){
8909             cfg.bgcolor = this.bgcolor;
8910         }
8911         if(this.charoff){
8912             cfg.charoff = this.charoff;
8913         }
8914         if(this.valign){
8915             cfg.valign = this.valign;
8916         }
8917         
8918         return cfg;
8919     }
8920    
8921 });
8922
8923  
8924
8925  /*
8926  * - LGPL
8927  *
8928  * table body
8929  * 
8930  */
8931
8932 /**
8933  * @class Roo.bootstrap.TableBody
8934  * @extends Roo.bootstrap.Component
8935  * Bootstrap TableBody class
8936  * @cfg {String} cls element class
8937  * @cfg {String} tag element tag (thead|tbody|tfoot) default tbody
8938  * @cfg {String} align Aligns the content inside the element
8939  * @cfg {Number} charoff Sets the number of characters the content inside the element will be aligned from the character specified by the char attribute
8940  * @cfg {String} valign Vertical aligns the content inside the <tbody> element
8941  * 
8942  * @constructor
8943  * Create a new TableBody
8944  * @param {Object} config The config object
8945  */
8946
8947 Roo.bootstrap.TableBody = function(config){
8948     Roo.bootstrap.TableBody.superclass.constructor.call(this, config);
8949 };
8950
8951 Roo.extend(Roo.bootstrap.TableBody, Roo.bootstrap.Component,  {
8952     
8953     cls: false,
8954     tag: false,
8955     align: false,
8956     charoff: false,
8957     valign: false,
8958     
8959     getAutoCreate : function(){
8960         var cfg = Roo.apply({}, Roo.bootstrap.TableBody.superclass.getAutoCreate.call(this));
8961         
8962         cfg = {
8963             tag: 'tbody'
8964         };
8965             
8966         if (this.cls) {
8967             cfg.cls=this.cls
8968         }
8969         if(this.tag){
8970             cfg.tag = this.tag;
8971         }
8972         
8973         if(this.align){
8974             cfg.align = this.align;
8975         }
8976         if(this.charoff){
8977             cfg.charoff = this.charoff;
8978         }
8979         if(this.valign){
8980             cfg.valign = this.valign;
8981         }
8982         
8983         return cfg;
8984     }
8985     
8986     
8987 //    initEvents : function()
8988 //    {
8989 //        
8990 //        if(!this.store){
8991 //            return;
8992 //        }
8993 //        
8994 //        this.store = Roo.factory(this.store, Roo.data);
8995 //        this.store.on('load', this.onLoad, this);
8996 //        
8997 //        this.store.load();
8998 //        
8999 //    },
9000 //    
9001 //    onLoad: function () 
9002 //    {   
9003 //        this.fireEvent('load', this);
9004 //    }
9005 //    
9006 //   
9007 });
9008
9009  
9010
9011  /*
9012  * Based on:
9013  * Ext JS Library 1.1.1
9014  * Copyright(c) 2006-2007, Ext JS, LLC.
9015  *
9016  * Originally Released Under LGPL - original licence link has changed is not relivant.
9017  *
9018  * Fork - LGPL
9019  * <script type="text/javascript">
9020  */
9021
9022 // as we use this in bootstrap.
9023 Roo.namespace('Roo.form');
9024  /**
9025  * @class Roo.form.Action
9026  * Internal Class used to handle form actions
9027  * @constructor
9028  * @param {Roo.form.BasicForm} el The form element or its id
9029  * @param {Object} config Configuration options
9030  */
9031
9032  
9033  
9034 // define the action interface
9035 Roo.form.Action = function(form, options){
9036     this.form = form;
9037     this.options = options || {};
9038 };
9039 /**
9040  * Client Validation Failed
9041  * @const 
9042  */
9043 Roo.form.Action.CLIENT_INVALID = 'client';
9044 /**
9045  * Server Validation Failed
9046  * @const 
9047  */
9048 Roo.form.Action.SERVER_INVALID = 'server';
9049  /**
9050  * Connect to Server Failed
9051  * @const 
9052  */
9053 Roo.form.Action.CONNECT_FAILURE = 'connect';
9054 /**
9055  * Reading Data from Server Failed
9056  * @const 
9057  */
9058 Roo.form.Action.LOAD_FAILURE = 'load';
9059
9060 Roo.form.Action.prototype = {
9061     type : 'default',
9062     failureType : undefined,
9063     response : undefined,
9064     result : undefined,
9065
9066     // interface method
9067     run : function(options){
9068
9069     },
9070
9071     // interface method
9072     success : function(response){
9073
9074     },
9075
9076     // interface method
9077     handleResponse : function(response){
9078
9079     },
9080
9081     // default connection failure
9082     failure : function(response){
9083         
9084         this.response = response;
9085         this.failureType = Roo.form.Action.CONNECT_FAILURE;
9086         this.form.afterAction(this, false);
9087     },
9088
9089     processResponse : function(response){
9090         this.response = response;
9091         if(!response.responseText){
9092             return true;
9093         }
9094         this.result = this.handleResponse(response);
9095         return this.result;
9096     },
9097
9098     // utility functions used internally
9099     getUrl : function(appendParams){
9100         var url = this.options.url || this.form.url || this.form.el.dom.action;
9101         if(appendParams){
9102             var p = this.getParams();
9103             if(p){
9104                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
9105             }
9106         }
9107         return url;
9108     },
9109
9110     getMethod : function(){
9111         return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
9112     },
9113
9114     getParams : function(){
9115         var bp = this.form.baseParams;
9116         var p = this.options.params;
9117         if(p){
9118             if(typeof p == "object"){
9119                 p = Roo.urlEncode(Roo.applyIf(p, bp));
9120             }else if(typeof p == 'string' && bp){
9121                 p += '&' + Roo.urlEncode(bp);
9122             }
9123         }else if(bp){
9124             p = Roo.urlEncode(bp);
9125         }
9126         return p;
9127     },
9128
9129     createCallback : function(){
9130         return {
9131             success: this.success,
9132             failure: this.failure,
9133             scope: this,
9134             timeout: (this.form.timeout*1000),
9135             upload: this.form.fileUpload ? this.success : undefined
9136         };
9137     }
9138 };
9139
9140 Roo.form.Action.Submit = function(form, options){
9141     Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
9142 };
9143
9144 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
9145     type : 'submit',
9146
9147     haveProgress : false,
9148     uploadComplete : false,
9149     
9150     // uploadProgress indicator.
9151     uploadProgress : function()
9152     {
9153         if (!this.form.progressUrl) {
9154             return;
9155         }
9156         
9157         if (!this.haveProgress) {
9158             Roo.MessageBox.progress("Uploading", "Uploading");
9159         }
9160         if (this.uploadComplete) {
9161            Roo.MessageBox.hide();
9162            return;
9163         }
9164         
9165         this.haveProgress = true;
9166    
9167         var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
9168         
9169         var c = new Roo.data.Connection();
9170         c.request({
9171             url : this.form.progressUrl,
9172             params: {
9173                 id : uid
9174             },
9175             method: 'GET',
9176             success : function(req){
9177                //console.log(data);
9178                 var rdata = false;
9179                 var edata;
9180                 try  {
9181                    rdata = Roo.decode(req.responseText)
9182                 } catch (e) {
9183                     Roo.log("Invalid data from server..");
9184                     Roo.log(edata);
9185                     return;
9186                 }
9187                 if (!rdata || !rdata.success) {
9188                     Roo.log(rdata);
9189                     Roo.MessageBox.alert(Roo.encode(rdata));
9190                     return;
9191                 }
9192                 var data = rdata.data;
9193                 
9194                 if (this.uploadComplete) {
9195                    Roo.MessageBox.hide();
9196                    return;
9197                 }
9198                    
9199                 if (data){
9200                     Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
9201                        Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
9202                     );
9203                 }
9204                 this.uploadProgress.defer(2000,this);
9205             },
9206        
9207             failure: function(data) {
9208                 Roo.log('progress url failed ');
9209                 Roo.log(data);
9210             },
9211             scope : this
9212         });
9213            
9214     },
9215     
9216     
9217     run : function()
9218     {
9219         // run get Values on the form, so it syncs any secondary forms.
9220         this.form.getValues();
9221         
9222         var o = this.options;
9223         var method = this.getMethod();
9224         var isPost = method == 'POST';
9225         if(o.clientValidation === false || this.form.isValid()){
9226             
9227             if (this.form.progressUrl) {
9228                 this.form.findField('UPLOAD_IDENTIFIER').setValue(
9229                     (new Date() * 1) + '' + Math.random());
9230                     
9231             } 
9232             
9233             
9234             Roo.Ajax.request(Roo.apply(this.createCallback(), {
9235                 form:this.form.el.dom,
9236                 url:this.getUrl(!isPost),
9237                 method: method,
9238                 params:isPost ? this.getParams() : null,
9239                 isUpload: this.form.fileUpload,
9240                 formData : this.form.formData
9241             }));
9242             
9243             this.uploadProgress();
9244
9245         }else if (o.clientValidation !== false){ // client validation failed
9246             this.failureType = Roo.form.Action.CLIENT_INVALID;
9247             this.form.afterAction(this, false);
9248         }
9249     },
9250
9251     success : function(response)
9252     {
9253         this.uploadComplete= true;
9254         if (this.haveProgress) {
9255             Roo.MessageBox.hide();
9256         }
9257         
9258         
9259         var result = this.processResponse(response);
9260         if(result === true || result.success){
9261             this.form.afterAction(this, true);
9262             return;
9263         }
9264         if(result.errors){
9265             this.form.markInvalid(result.errors);
9266             this.failureType = Roo.form.Action.SERVER_INVALID;
9267         }
9268         this.form.afterAction(this, false);
9269     },
9270     failure : function(response)
9271     {
9272         this.uploadComplete= true;
9273         if (this.haveProgress) {
9274             Roo.MessageBox.hide();
9275         }
9276         
9277         this.response = response;
9278         this.failureType = Roo.form.Action.CONNECT_FAILURE;
9279         this.form.afterAction(this, false);
9280     },
9281     
9282     handleResponse : function(response){
9283         if(this.form.errorReader){
9284             var rs = this.form.errorReader.read(response);
9285             var errors = [];
9286             if(rs.records){
9287                 for(var i = 0, len = rs.records.length; i < len; i++) {
9288                     var r = rs.records[i];
9289                     errors[i] = r.data;
9290                 }
9291             }
9292             if(errors.length < 1){
9293                 errors = null;
9294             }
9295             return {
9296                 success : rs.success,
9297                 errors : errors
9298             };
9299         }
9300         var ret = false;
9301         try {
9302             ret = Roo.decode(response.responseText);
9303         } catch (e) {
9304             ret = {
9305                 success: false,
9306                 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
9307                 errors : []
9308             };
9309         }
9310         return ret;
9311         
9312     }
9313 });
9314
9315
9316 Roo.form.Action.Load = function(form, options){
9317     Roo.form.Action.Load.superclass.constructor.call(this, form, options);
9318     this.reader = this.form.reader;
9319 };
9320
9321 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
9322     type : 'load',
9323
9324     run : function(){
9325         
9326         Roo.Ajax.request(Roo.apply(
9327                 this.createCallback(), {
9328                     method:this.getMethod(),
9329                     url:this.getUrl(false),
9330                     params:this.getParams()
9331         }));
9332     },
9333
9334     success : function(response){
9335         
9336         var result = this.processResponse(response);
9337         if(result === true || !result.success || !result.data){
9338             this.failureType = Roo.form.Action.LOAD_FAILURE;
9339             this.form.afterAction(this, false);
9340             return;
9341         }
9342         this.form.clearInvalid();
9343         this.form.setValues(result.data);
9344         this.form.afterAction(this, true);
9345     },
9346
9347     handleResponse : function(response){
9348         if(this.form.reader){
9349             var rs = this.form.reader.read(response);
9350             var data = rs.records && rs.records[0] ? rs.records[0].data : null;
9351             return {
9352                 success : rs.success,
9353                 data : data
9354             };
9355         }
9356         return Roo.decode(response.responseText);
9357     }
9358 });
9359
9360 Roo.form.Action.ACTION_TYPES = {
9361     'load' : Roo.form.Action.Load,
9362     'submit' : Roo.form.Action.Submit
9363 };/*
9364  * - LGPL
9365  *
9366  * form
9367  *
9368  */
9369
9370 /**
9371  * @class Roo.bootstrap.Form
9372  * @extends Roo.bootstrap.Component
9373  * Bootstrap Form class
9374  * @cfg {String} method  GET | POST (default POST)
9375  * @cfg {String} labelAlign top | left (default top)
9376  * @cfg {String} align left  | right - for navbars
9377  * @cfg {Boolean} loadMask load mask when submit (default true)
9378
9379  *
9380  * @constructor
9381  * Create a new Form
9382  * @param {Object} config The config object
9383  */
9384
9385
9386 Roo.bootstrap.Form = function(config){
9387     
9388     Roo.bootstrap.Form.superclass.constructor.call(this, config);
9389     
9390     Roo.bootstrap.Form.popover.apply();
9391     
9392     this.addEvents({
9393         /**
9394          * @event clientvalidation
9395          * If the monitorValid config option is true, this event fires repetitively to notify of valid state
9396          * @param {Form} this
9397          * @param {Boolean} valid true if the form has passed client-side validation
9398          */
9399         clientvalidation: true,
9400         /**
9401          * @event beforeaction
9402          * Fires before any action is performed. Return false to cancel the action.
9403          * @param {Form} this
9404          * @param {Action} action The action to be performed
9405          */
9406         beforeaction: true,
9407         /**
9408          * @event actionfailed
9409          * Fires when an action fails.
9410          * @param {Form} this
9411          * @param {Action} action The action that failed
9412          */
9413         actionfailed : true,
9414         /**
9415          * @event actioncomplete
9416          * Fires when an action is completed.
9417          * @param {Form} this
9418          * @param {Action} action The action that completed
9419          */
9420         actioncomplete : true
9421     });
9422 };
9423
9424 Roo.extend(Roo.bootstrap.Form, Roo.bootstrap.Component,  {
9425
9426      /**
9427      * @cfg {String} method
9428      * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
9429      */
9430     method : 'POST',
9431     /**
9432      * @cfg {String} url
9433      * The URL to use for form actions if one isn't supplied in the action options.
9434      */
9435     /**
9436      * @cfg {Boolean} fileUpload
9437      * Set to true if this form is a file upload.
9438      */
9439
9440     /**
9441      * @cfg {Object} baseParams
9442      * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
9443      */
9444
9445     /**
9446      * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
9447      */
9448     timeout: 30,
9449     /**
9450      * @cfg {Sting} align (left|right) for navbar forms
9451      */
9452     align : 'left',
9453
9454     // private
9455     activeAction : null,
9456
9457     /**
9458      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
9459      * element by passing it or its id or mask the form itself by passing in true.
9460      * @type Mixed
9461      */
9462     waitMsgTarget : false,
9463
9464     loadMask : true,
9465     
9466     /**
9467      * @cfg {Boolean} errorMask (true|false) default false
9468      */
9469     errorMask : false,
9470     
9471     /**
9472      * @cfg {Number} maskOffset Default 100
9473      */
9474     maskOffset : 100,
9475     
9476     /**
9477      * @cfg {Boolean} maskBody
9478      */
9479     maskBody : false,
9480
9481     getAutoCreate : function(){
9482
9483         var cfg = {
9484             tag: 'form',
9485             method : this.method || 'POST',
9486             id : this.id || Roo.id(),
9487             cls : ''
9488         };
9489         if (this.parent().xtype.match(/^Nav/)) {
9490             cfg.cls = 'navbar-form form-inline navbar-' + this.align;
9491
9492         }
9493
9494         if (this.labelAlign == 'left' ) {
9495             cfg.cls += ' form-horizontal';
9496         }
9497
9498
9499         return cfg;
9500     },
9501     initEvents : function()
9502     {
9503         this.el.on('submit', this.onSubmit, this);
9504         // this was added as random key presses on the form where triggering form submit.
9505         this.el.on('keypress', function(e) {
9506             if (e.getCharCode() != 13) {
9507                 return true;
9508             }
9509             // we might need to allow it for textareas.. and some other items.
9510             // check e.getTarget().
9511
9512             if(e.getTarget().nodeName.toLowerCase() === 'textarea'){
9513                 return true;
9514             }
9515
9516             Roo.log("keypress blocked");
9517
9518             e.preventDefault();
9519             return false;
9520         });
9521         
9522     },
9523     // private
9524     onSubmit : function(e){
9525         e.stopEvent();
9526     },
9527
9528      /**
9529      * Returns true if client-side validation on the form is successful.
9530      * @return Boolean
9531      */
9532     isValid : function(){
9533         var items = this.getItems();
9534         var valid = true;
9535         var target = false;
9536         
9537         items.each(function(f){
9538             
9539             if(f.validate()){
9540                 return;
9541             }
9542             
9543             Roo.log('invalid field: ' + f.name);
9544             
9545             valid = false;
9546
9547             if(!target && f.el.isVisible(true)){
9548                 target = f;
9549             }
9550            
9551         });
9552         
9553         if(this.errorMask && !valid){
9554             Roo.bootstrap.Form.popover.mask(this, target);
9555         }
9556         
9557         return valid;
9558     },
9559     
9560     /**
9561      * Returns true if any fields in this form have changed since their original load.
9562      * @return Boolean
9563      */
9564     isDirty : function(){
9565         var dirty = false;
9566         var items = this.getItems();
9567         items.each(function(f){
9568            if(f.isDirty()){
9569                dirty = true;
9570                return false;
9571            }
9572            return true;
9573         });
9574         return dirty;
9575     },
9576      /**
9577      * Performs a predefined action (submit or load) or custom actions you define on this form.
9578      * @param {String} actionName The name of the action type
9579      * @param {Object} options (optional) The options to pass to the action.  All of the config options listed
9580      * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
9581      * accept other config options):
9582      * <pre>
9583 Property          Type             Description
9584 ----------------  ---------------  ----------------------------------------------------------------------------------
9585 url               String           The url for the action (defaults to the form's url)
9586 method            String           The form method to use (defaults to the form's method, or POST if not defined)
9587 params            String/Object    The params to pass (defaults to the form's baseParams, or none if not defined)
9588 clientValidation  Boolean          Applies to submit only.  Pass true to call form.isValid() prior to posting to
9589                                    validate the form on the client (defaults to false)
9590      * </pre>
9591      * @return {BasicForm} this
9592      */
9593     doAction : function(action, options){
9594         if(typeof action == 'string'){
9595             action = new Roo.form.Action.ACTION_TYPES[action](this, options);
9596         }
9597         if(this.fireEvent('beforeaction', this, action) !== false){
9598             this.beforeAction(action);
9599             action.run.defer(100, action);
9600         }
9601         return this;
9602     },
9603
9604     // private
9605     beforeAction : function(action){
9606         var o = action.options;
9607         
9608         if(this.loadMask){
9609             
9610             if(this.maskBody){
9611                 Roo.get(document.body).mask(o.waitMsg || "Sending", 'x-mask-loading')
9612             } else {
9613                 this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
9614             }
9615         }
9616         // not really supported yet.. ??
9617
9618         //if(this.waitMsgTarget === true){
9619         //  this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
9620         //}else if(this.waitMsgTarget){
9621         //    this.waitMsgTarget = Roo.get(this.waitMsgTarget);
9622         //    this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
9623         //}else {
9624         //    Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
9625        // }
9626
9627     },
9628
9629     // private
9630     afterAction : function(action, success){
9631         this.activeAction = null;
9632         var o = action.options;
9633
9634         if(this.loadMask){
9635             
9636             if(this.maskBody){
9637                 Roo.get(document.body).unmask();
9638             } else {
9639                 this.el.unmask();
9640             }
9641         }
9642         
9643         //if(this.waitMsgTarget === true){
9644 //            this.el.unmask();
9645         //}else if(this.waitMsgTarget){
9646         //    this.waitMsgTarget.unmask();
9647         //}else{
9648         //    Roo.MessageBox.updateProgress(1);
9649         //    Roo.MessageBox.hide();
9650        // }
9651         //
9652         if(success){
9653             if(o.reset){
9654                 this.reset();
9655             }
9656             Roo.callback(o.success, o.scope, [this, action]);
9657             this.fireEvent('actioncomplete', this, action);
9658
9659         }else{
9660
9661             // failure condition..
9662             // we have a scenario where updates need confirming.
9663             // eg. if a locking scenario exists..
9664             // we look for { errors : { needs_confirm : true }} in the response.
9665             if (
9666                 (typeof(action.result) != 'undefined')  &&
9667                 (typeof(action.result.errors) != 'undefined')  &&
9668                 (typeof(action.result.errors.needs_confirm) != 'undefined')
9669            ){
9670                 var _t = this;
9671                 Roo.log("not supported yet");
9672                  /*
9673
9674                 Roo.MessageBox.confirm(
9675                     "Change requires confirmation",
9676                     action.result.errorMsg,
9677                     function(r) {
9678                         if (r != 'yes') {
9679                             return;
9680                         }
9681                         _t.doAction('submit', { params :  { _submit_confirmed : 1 } }  );
9682                     }
9683
9684                 );
9685                 */
9686
9687
9688                 return;
9689             }
9690
9691             Roo.callback(o.failure, o.scope, [this, action]);
9692             // show an error message if no failed handler is set..
9693             if (!this.hasListener('actionfailed')) {
9694                 Roo.log("need to add dialog support");
9695                 /*
9696                 Roo.MessageBox.alert("Error",
9697                     (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
9698                         action.result.errorMsg :
9699                         "Saving Failed, please check your entries or try again"
9700                 );
9701                 */
9702             }
9703
9704             this.fireEvent('actionfailed', this, action);
9705         }
9706
9707     },
9708     /**
9709      * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
9710      * @param {String} id The value to search for
9711      * @return Field
9712      */
9713     findField : function(id){
9714         var items = this.getItems();
9715         var field = items.get(id);
9716         if(!field){
9717              items.each(function(f){
9718                 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
9719                     field = f;
9720                     return false;
9721                 }
9722                 return true;
9723             });
9724         }
9725         return field || null;
9726     },
9727      /**
9728      * Mark fields in this form invalid in bulk.
9729      * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
9730      * @return {BasicForm} this
9731      */
9732     markInvalid : function(errors){
9733         if(errors instanceof Array){
9734             for(var i = 0, len = errors.length; i < len; i++){
9735                 var fieldError = errors[i];
9736                 var f = this.findField(fieldError.id);
9737                 if(f){
9738                     f.markInvalid(fieldError.msg);
9739                 }
9740             }
9741         }else{
9742             var field, id;
9743             for(id in errors){
9744                 if(typeof errors[id] != 'function' && (field = this.findField(id))){
9745                     field.markInvalid(errors[id]);
9746                 }
9747             }
9748         }
9749         //Roo.each(this.childForms || [], function (f) {
9750         //    f.markInvalid(errors);
9751         //});
9752
9753         return this;
9754     },
9755
9756     /**
9757      * Set values for fields in this form in bulk.
9758      * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
9759      * @return {BasicForm} this
9760      */
9761     setValues : function(values){
9762         if(values instanceof Array){ // array of objects
9763             for(var i = 0, len = values.length; i < len; i++){
9764                 var v = values[i];
9765                 var f = this.findField(v.id);
9766                 if(f){
9767                     f.setValue(v.value);
9768                     if(this.trackResetOnLoad){
9769                         f.originalValue = f.getValue();
9770                     }
9771                 }
9772             }
9773         }else{ // object hash
9774             var field, id;
9775             for(id in values){
9776                 if(typeof values[id] != 'function' && (field = this.findField(id))){
9777
9778                     if (field.setFromData &&
9779                         field.valueField &&
9780                         field.displayField &&
9781                         // combos' with local stores can
9782                         // be queried via setValue()
9783                         // to set their value..
9784                         (field.store && !field.store.isLocal)
9785                         ) {
9786                         // it's a combo
9787                         var sd = { };
9788                         sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
9789                         sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
9790                         field.setFromData(sd);
9791
9792                     } else if(field.setFromData && (field.store && !field.store.isLocal)) {
9793                         
9794                         field.setFromData(values);
9795                         
9796                     } else {
9797                         field.setValue(values[id]);
9798                     }
9799
9800
9801                     if(this.trackResetOnLoad){
9802                         field.originalValue = field.getValue();
9803                     }
9804                 }
9805             }
9806         }
9807
9808         //Roo.each(this.childForms || [], function (f) {
9809         //    f.setValues(values);
9810         //});
9811
9812         return this;
9813     },
9814
9815     /**
9816      * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
9817      * they are returned as an array.
9818      * @param {Boolean} asString
9819      * @return {Object}
9820      */
9821     getValues : function(asString){
9822         //if (this.childForms) {
9823             // copy values from the child forms
9824         //    Roo.each(this.childForms, function (f) {
9825         //        this.setValues(f.getValues());
9826         //    }, this);
9827         //}
9828
9829
9830
9831         var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
9832         if(asString === true){
9833             return fs;
9834         }
9835         return Roo.urlDecode(fs);
9836     },
9837
9838     /**
9839      * Returns the fields in this form as an object with key/value pairs.
9840      * This differs from getValues as it calls getValue on each child item, rather than using dom data.
9841      * @return {Object}
9842      */
9843     getFieldValues : function(with_hidden)
9844     {
9845         var items = this.getItems();
9846         var ret = {};
9847         items.each(function(f){
9848             
9849             if (!f.getName()) {
9850                 return;
9851             }
9852             
9853             var v = f.getValue();
9854             
9855             if (f.inputType =='radio') {
9856                 if (typeof(ret[f.getName()]) == 'undefined') {
9857                     ret[f.getName()] = ''; // empty..
9858                 }
9859
9860                 if (!f.el.dom.checked) {
9861                     return;
9862
9863                 }
9864                 v = f.el.dom.value;
9865
9866             }
9867             
9868             if(f.xtype == 'MoneyField'){
9869                 ret[f.currencyName] = f.getCurrency();
9870             }
9871
9872             // not sure if this supported any more..
9873             if ((typeof(v) == 'object') && f.getRawValue) {
9874                 v = f.getRawValue() ; // dates..
9875             }
9876             // combo boxes where name != hiddenName...
9877             if (f.name !== false && f.name != '' && f.name != f.getName()) {
9878                 ret[f.name] = f.getRawValue();
9879             }
9880             ret[f.getName()] = v;
9881         });
9882
9883         return ret;
9884     },
9885
9886     /**
9887      * Clears all invalid messages in this form.
9888      * @return {BasicForm} this
9889      */
9890     clearInvalid : function(){
9891         var items = this.getItems();
9892
9893         items.each(function(f){
9894            f.clearInvalid();
9895         });
9896
9897         return this;
9898     },
9899
9900     /**
9901      * Resets this form.
9902      * @return {BasicForm} this
9903      */
9904     reset : function(){
9905         var items = this.getItems();
9906         items.each(function(f){
9907             f.reset();
9908         });
9909
9910         Roo.each(this.childForms || [], function (f) {
9911             f.reset();
9912         });
9913
9914
9915         return this;
9916     },
9917     
9918     getItems : function()
9919     {
9920         var r=new Roo.util.MixedCollection(false, function(o){
9921             return o.id || (o.id = Roo.id());
9922         });
9923         var iter = function(el) {
9924             if (el.inputEl) {
9925                 r.add(el);
9926             }
9927             if (!el.items) {
9928                 return;
9929             }
9930             Roo.each(el.items,function(e) {
9931                 iter(e);
9932             });
9933         };
9934
9935         iter(this);
9936         return r;
9937     },
9938     
9939     hideFields : function(items)
9940     {
9941         Roo.each(items, function(i){
9942             
9943             var f = this.findField(i);
9944             
9945             if(!f){
9946                 return;
9947             }
9948             
9949             f.hide();
9950             
9951         }, this);
9952     },
9953     
9954     showFields : function(items)
9955     {
9956         Roo.each(items, function(i){
9957             
9958             var f = this.findField(i);
9959             
9960             if(!f){
9961                 return;
9962             }
9963             
9964             f.show();
9965             
9966         }, this);
9967     }
9968
9969 });
9970
9971 Roo.apply(Roo.bootstrap.Form, {
9972     
9973     popover : {
9974         
9975         padding : 5,
9976         
9977         isApplied : false,
9978         
9979         isMasked : false,
9980         
9981         form : false,
9982         
9983         target : false,
9984         
9985         toolTip : false,
9986         
9987         intervalID : false,
9988         
9989         maskEl : false,
9990         
9991         apply : function()
9992         {
9993             if(this.isApplied){
9994                 return;
9995             }
9996             
9997             this.maskEl = {
9998                 top : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-top-mask" }, true),
9999                 left : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-left-mask" }, true),
10000                 bottom : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-bottom-mask" }, true),
10001                 right : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-right-mask" }, true)
10002             };
10003             
10004             this.maskEl.top.enableDisplayMode("block");
10005             this.maskEl.left.enableDisplayMode("block");
10006             this.maskEl.bottom.enableDisplayMode("block");
10007             this.maskEl.right.enableDisplayMode("block");
10008             
10009             this.toolTip = new Roo.bootstrap.Tooltip({
10010                 cls : 'roo-form-error-popover',
10011                 alignment : {
10012                     'left' : ['r-l', [-2,0], 'right'],
10013                     'right' : ['l-r', [2,0], 'left'],
10014                     'bottom' : ['tl-bl', [0,2], 'top'],
10015                     'top' : [ 'bl-tl', [0,-2], 'bottom']
10016                 }
10017             });
10018             
10019             this.toolTip.render(Roo.get(document.body));
10020
10021             this.toolTip.el.enableDisplayMode("block");
10022             
10023             Roo.get(document.body).on('click', function(){
10024                 this.unmask();
10025             }, this);
10026             
10027             Roo.get(document.body).on('touchstart', function(){
10028                 this.unmask();
10029             }, this);
10030             
10031             this.isApplied = true
10032         },
10033         
10034         mask : function(form, target)
10035         {
10036             this.form = form;
10037             
10038             this.target = target;
10039             
10040             if(!this.form.errorMask || !target.el){
10041                 return;
10042             }
10043             
10044             var scrollable = this.target.el.findScrollableParent() || this.target.el.findParent('div.modal', 100, true) || Roo.get(document.body);
10045             
10046             Roo.log(scrollable);
10047             
10048             var ot = this.target.el.calcOffsetsTo(scrollable);
10049             
10050             var scrollTo = ot[1] - this.form.maskOffset;
10051             
10052             scrollTo = Math.min(scrollTo, scrollable.dom.scrollHeight);
10053             
10054             scrollable.scrollTo('top', scrollTo);
10055             
10056             var box = this.target.el.getBox();
10057             Roo.log(box);
10058             var zIndex = Roo.bootstrap.Modal.zIndex++;
10059
10060             
10061             this.maskEl.top.setStyle('position', 'absolute');
10062             this.maskEl.top.setStyle('z-index', zIndex);
10063             this.maskEl.top.setSize(Roo.lib.Dom.getDocumentWidth(), box.y - this.padding);
10064             this.maskEl.top.setLeft(0);
10065             this.maskEl.top.setTop(0);
10066             this.maskEl.top.show();
10067             
10068             this.maskEl.left.setStyle('position', 'absolute');
10069             this.maskEl.left.setStyle('z-index', zIndex);
10070             this.maskEl.left.setSize(box.x - this.padding, box.height + this.padding * 2);
10071             this.maskEl.left.setLeft(0);
10072             this.maskEl.left.setTop(box.y - this.padding);
10073             this.maskEl.left.show();
10074
10075             this.maskEl.bottom.setStyle('position', 'absolute');
10076             this.maskEl.bottom.setStyle('z-index', zIndex);
10077             this.maskEl.bottom.setSize(Roo.lib.Dom.getDocumentWidth(), Roo.lib.Dom.getDocumentHeight() - box.bottom - this.padding);
10078             this.maskEl.bottom.setLeft(0);
10079             this.maskEl.bottom.setTop(box.bottom + this.padding);
10080             this.maskEl.bottom.show();
10081
10082             this.maskEl.right.setStyle('position', 'absolute');
10083             this.maskEl.right.setStyle('z-index', zIndex);
10084             this.maskEl.right.setSize(Roo.lib.Dom.getDocumentWidth() - box.right - this.padding, box.height + this.padding * 2);
10085             this.maskEl.right.setLeft(box.right + this.padding);
10086             this.maskEl.right.setTop(box.y - this.padding);
10087             this.maskEl.right.show();
10088
10089             this.toolTip.bindEl = this.target.el;
10090
10091             this.toolTip.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
10092
10093             var tip = this.target.blankText;
10094
10095             if(this.target.getValue() !== '' ) {
10096                 
10097                 if (this.target.invalidText.length) {
10098                     tip = this.target.invalidText;
10099                 } else if (this.target.regexText.length){
10100                     tip = this.target.regexText;
10101                 }
10102             }
10103
10104             this.toolTip.show(tip);
10105
10106             this.intervalID = window.setInterval(function() {
10107                 Roo.bootstrap.Form.popover.unmask();
10108             }, 10000);
10109
10110             window.onwheel = function(){ return false;};
10111             
10112             (function(){ this.isMasked = true; }).defer(500, this);
10113             
10114         },
10115         
10116         unmask : function()
10117         {
10118             if(!this.isApplied || !this.isMasked || !this.form || !this.target || !this.form.errorMask){
10119                 return;
10120             }
10121             
10122             this.maskEl.top.setStyle('position', 'absolute');
10123             this.maskEl.top.setSize(0, 0).setXY([0, 0]);
10124             this.maskEl.top.hide();
10125
10126             this.maskEl.left.setStyle('position', 'absolute');
10127             this.maskEl.left.setSize(0, 0).setXY([0, 0]);
10128             this.maskEl.left.hide();
10129
10130             this.maskEl.bottom.setStyle('position', 'absolute');
10131             this.maskEl.bottom.setSize(0, 0).setXY([0, 0]);
10132             this.maskEl.bottom.hide();
10133
10134             this.maskEl.right.setStyle('position', 'absolute');
10135             this.maskEl.right.setSize(0, 0).setXY([0, 0]);
10136             this.maskEl.right.hide();
10137             
10138             this.toolTip.hide();
10139             
10140             this.toolTip.el.hide();
10141             
10142             window.onwheel = function(){ return true;};
10143             
10144             if(this.intervalID){
10145                 window.clearInterval(this.intervalID);
10146                 this.intervalID = false;
10147             }
10148             
10149             this.isMasked = false;
10150             
10151         }
10152         
10153     }
10154     
10155 });
10156
10157 /*
10158  * Based on:
10159  * Ext JS Library 1.1.1
10160  * Copyright(c) 2006-2007, Ext JS, LLC.
10161  *
10162  * Originally Released Under LGPL - original licence link has changed is not relivant.
10163  *
10164  * Fork - LGPL
10165  * <script type="text/javascript">
10166  */
10167 /**
10168  * @class Roo.form.VTypes
10169  * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
10170  * @singleton
10171  */
10172 Roo.form.VTypes = function(){
10173     // closure these in so they are only created once.
10174     var alpha = /^[a-zA-Z_]+$/;
10175     var alphanum = /^[a-zA-Z0-9_]+$/;
10176     var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,24}$/;
10177     var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
10178
10179     // All these messages and functions are configurable
10180     return {
10181         /**
10182          * The function used to validate email addresses
10183          * @param {String} value The email address
10184          */
10185         'email' : function(v){
10186             return email.test(v);
10187         },
10188         /**
10189          * The error text to display when the email validation function returns false
10190          * @type String
10191          */
10192         'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
10193         /**
10194          * The keystroke filter mask to be applied on email input
10195          * @type RegExp
10196          */
10197         'emailMask' : /[a-z0-9_\.\-@]/i,
10198
10199         /**
10200          * The function used to validate URLs
10201          * @param {String} value The URL
10202          */
10203         'url' : function(v){
10204             return url.test(v);
10205         },
10206         /**
10207          * The error text to display when the url validation function returns false
10208          * @type String
10209          */
10210         'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
10211         
10212         /**
10213          * The function used to validate alpha values
10214          * @param {String} value The value
10215          */
10216         'alpha' : function(v){
10217             return alpha.test(v);
10218         },
10219         /**
10220          * The error text to display when the alpha validation function returns false
10221          * @type String
10222          */
10223         'alphaText' : 'This field should only contain letters and _',
10224         /**
10225          * The keystroke filter mask to be applied on alpha input
10226          * @type RegExp
10227          */
10228         'alphaMask' : /[a-z_]/i,
10229
10230         /**
10231          * The function used to validate alphanumeric values
10232          * @param {String} value The value
10233          */
10234         'alphanum' : function(v){
10235             return alphanum.test(v);
10236         },
10237         /**
10238          * The error text to display when the alphanumeric validation function returns false
10239          * @type String
10240          */
10241         'alphanumText' : 'This field should only contain letters, numbers and _',
10242         /**
10243          * The keystroke filter mask to be applied on alphanumeric input
10244          * @type RegExp
10245          */
10246         'alphanumMask' : /[a-z0-9_]/i
10247     };
10248 }();/*
10249  * - LGPL
10250  *
10251  * Input
10252  * 
10253  */
10254
10255 /**
10256  * @class Roo.bootstrap.Input
10257  * @extends Roo.bootstrap.Component
10258  * Bootstrap Input class
10259  * @cfg {Boolean} disabled is it disabled
10260  * @cfg {String} (button|checkbox|email|file|hidden|image|number|password|radio|range|reset|search|submit|text) inputType 
10261  * @cfg {String} name name of the input
10262  * @cfg {string} fieldLabel - the label associated
10263  * @cfg {string} placeholder - placeholder to put in text.
10264  * @cfg {string}  before - input group add on before
10265  * @cfg {string} after - input group add on after
10266  * @cfg {string} size - (lg|sm) or leave empty..
10267  * @cfg {Number} xs colspan out of 12 for mobile-sized screens
10268  * @cfg {Number} sm colspan out of 12 for tablet-sized screens
10269  * @cfg {Number} md colspan out of 12 for computer-sized screens
10270  * @cfg {Number} lg colspan out of 12 for large computer-sized screens
10271  * @cfg {string} value default value of the input
10272  * @cfg {Number} labelWidth set the width of label 
10273  * @cfg {Number} labellg set the width of label (1-12)
10274  * @cfg {Number} labelmd set the width of label (1-12)
10275  * @cfg {Number} labelsm set the width of label (1-12)
10276  * @cfg {Number} labelxs set the width of label (1-12)
10277  * @cfg {String} labelAlign (top|left)
10278  * @cfg {Boolean} readOnly Specifies that the field should be read-only
10279  * @cfg {String} autocomplete - default is new-password see: https://developers.google.com/web/fundamentals/input/form/label-and-name-inputs?hl=en
10280  * @cfg {String} indicatorpos (left|right) default left
10281  * @cfg {String} capture (user|camera) use for file input only. (default empty)
10282  * @cfg {String} accept (image|video|audio) use for file input only. (default empty)
10283  * @cfg {Boolean} preventMark Do not show tick or cross if error/success
10284
10285  * @cfg {String} align (left|center|right) Default left
10286  * @cfg {Boolean} forceFeedback (true|false) Default false
10287  * 
10288  * @constructor
10289  * Create a new Input
10290  * @param {Object} config The config object
10291  */
10292
10293 Roo.bootstrap.Input = function(config){
10294     
10295     Roo.bootstrap.Input.superclass.constructor.call(this, config);
10296     
10297     this.addEvents({
10298         /**
10299          * @event focus
10300          * Fires when this field receives input focus.
10301          * @param {Roo.form.Field} this
10302          */
10303         focus : true,
10304         /**
10305          * @event blur
10306          * Fires when this field loses input focus.
10307          * @param {Roo.form.Field} this
10308          */
10309         blur : true,
10310         /**
10311          * @event specialkey
10312          * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
10313          * {@link Roo.EventObject#getKey} to determine which key was pressed.
10314          * @param {Roo.form.Field} this
10315          * @param {Roo.EventObject} e The event object
10316          */
10317         specialkey : true,
10318         /**
10319          * @event change
10320          * Fires just before the field blurs if the field value has changed.
10321          * @param {Roo.form.Field} this
10322          * @param {Mixed} newValue The new value
10323          * @param {Mixed} oldValue The original value
10324          */
10325         change : true,
10326         /**
10327          * @event invalid
10328          * Fires after the field has been marked as invalid.
10329          * @param {Roo.form.Field} this
10330          * @param {String} msg The validation message
10331          */
10332         invalid : true,
10333         /**
10334          * @event valid
10335          * Fires after the field has been validated with no errors.
10336          * @param {Roo.form.Field} this
10337          */
10338         valid : true,
10339          /**
10340          * @event keyup
10341          * Fires after the key up
10342          * @param {Roo.form.Field} this
10343          * @param {Roo.EventObject}  e The event Object
10344          */
10345         keyup : true
10346     });
10347 };
10348
10349 Roo.extend(Roo.bootstrap.Input, Roo.bootstrap.Component,  {
10350      /**
10351      * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
10352       automatic validation (defaults to "keyup").
10353      */
10354     validationEvent : "keyup",
10355      /**
10356      * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
10357      */
10358     validateOnBlur : true,
10359     /**
10360      * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
10361      */
10362     validationDelay : 250,
10363      /**
10364      * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
10365      */
10366     focusClass : "x-form-focus",  // not needed???
10367     
10368        
10369     /**
10370      * @cfg {String} invalidClass DEPRICATED - code uses BS4 - is-valid / is-invalid
10371      */
10372     invalidClass : "has-warning",
10373     
10374     /**
10375      * @cfg {String} validClass DEPRICATED - code uses BS4 - is-valid / is-invalid
10376      */
10377     validClass : "has-success",
10378     
10379     /**
10380      * @cfg {Boolean} hasFeedback (true|false) default true
10381      */
10382     hasFeedback : true,
10383     
10384     /**
10385      * @cfg {String} invalidFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
10386      */
10387     invalidFeedbackClass : "glyphicon-warning-sign",
10388     
10389     /**
10390      * @cfg {String} validFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
10391      */
10392     validFeedbackClass : "glyphicon-ok",
10393     
10394     /**
10395      * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
10396      */
10397     selectOnFocus : false,
10398     
10399      /**
10400      * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
10401      */
10402     maskRe : null,
10403        /**
10404      * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
10405      */
10406     vtype : null,
10407     
10408       /**
10409      * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
10410      */
10411     disableKeyFilter : false,
10412     
10413        /**
10414      * @cfg {Boolean} disabled True to disable the field (defaults to false).
10415      */
10416     disabled : false,
10417      /**
10418      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
10419      */
10420     allowBlank : true,
10421     /**
10422      * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
10423      */
10424     blankText : "Please complete this mandatory field",
10425     
10426      /**
10427      * @cfg {Number} minLength Minimum input field length required (defaults to 0)
10428      */
10429     minLength : 0,
10430     /**
10431      * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
10432      */
10433     maxLength : Number.MAX_VALUE,
10434     /**
10435      * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
10436      */
10437     minLengthText : "The minimum length for this field is {0}",
10438     /**
10439      * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
10440      */
10441     maxLengthText : "The maximum length for this field is {0}",
10442   
10443     
10444     /**
10445      * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
10446      * If available, this function will be called only after the basic validators all return true, and will be passed the
10447      * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
10448      */
10449     validator : null,
10450     /**
10451      * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
10452      * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
10453      * current field value.  If the test fails, the field will be marked invalid using {@link #regexText}.
10454      */
10455     regex : null,
10456     /**
10457      * @cfg {String} regexText -- Depricated - use Invalid Text
10458      */
10459     regexText : "",
10460     
10461     /**
10462      * @cfg {String} invalidText The error text to display if {@link #validator} test fails during validation (defaults to "")
10463      */
10464     invalidText : "",
10465     
10466     
10467     
10468     autocomplete: false,
10469     
10470     
10471     fieldLabel : '',
10472     inputType : 'text',
10473     
10474     name : false,
10475     placeholder: false,
10476     before : false,
10477     after : false,
10478     size : false,
10479     hasFocus : false,
10480     preventMark: false,
10481     isFormField : true,
10482     value : '',
10483     labelWidth : 2,
10484     labelAlign : false,
10485     readOnly : false,
10486     align : false,
10487     formatedValue : false,
10488     forceFeedback : false,
10489     
10490     indicatorpos : 'left',
10491     
10492     labellg : 0,
10493     labelmd : 0,
10494     labelsm : 0,
10495     labelxs : 0,
10496     
10497     capture : '',
10498     accept : '',
10499     
10500     parentLabelAlign : function()
10501     {
10502         var parent = this;
10503         while (parent.parent()) {
10504             parent = parent.parent();
10505             if (typeof(parent.labelAlign) !='undefined') {
10506                 return parent.labelAlign;
10507             }
10508         }
10509         return 'left';
10510         
10511     },
10512     
10513     getAutoCreate : function()
10514     {
10515         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
10516         
10517         var id = Roo.id();
10518         
10519         var cfg = {};
10520         
10521         if(this.inputType != 'hidden'){
10522             cfg.cls = 'form-group' //input-group
10523         }
10524         
10525         var input =  {
10526             tag: 'input',
10527             id : id,
10528             type : this.inputType,
10529             value : this.value,
10530             cls : 'form-control',
10531             placeholder : this.placeholder || '',
10532             autocomplete : this.autocomplete || 'new-password'
10533         };
10534         if (this.inputType == 'file') {
10535             input.style = 'overflow:hidden'; // why not in CSS?
10536         }
10537         
10538         if(this.capture.length){
10539             input.capture = this.capture;
10540         }
10541         
10542         if(this.accept.length){
10543             input.accept = this.accept + "/*";
10544         }
10545         
10546         if(this.align){
10547             input.style = (typeof(input.style) == 'undefined') ? ('text-align:' + this.align) : (input.style + 'text-align:' + this.align);
10548         }
10549         
10550         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
10551             input.maxLength = this.maxLength;
10552         }
10553         
10554         if (this.disabled) {
10555             input.disabled=true;
10556         }
10557         
10558         if (this.readOnly) {
10559             input.readonly=true;
10560         }
10561         
10562         if (this.name) {
10563             input.name = this.name;
10564         }
10565         
10566         if (this.size) {
10567             input.cls += ' input-' + this.size;
10568         }
10569         
10570         var settings=this;
10571         ['xs','sm','md','lg'].map(function(size){
10572             if (settings[size]) {
10573                 cfg.cls += ' col-' + size + '-' + settings[size];
10574             }
10575         });
10576         
10577         var inputblock = input;
10578         
10579         var feedback = {
10580             tag: 'span',
10581             cls: 'glyphicon form-control-feedback'
10582         };
10583             
10584         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
10585             
10586             inputblock = {
10587                 cls : 'has-feedback',
10588                 cn :  [
10589                     input,
10590                     feedback
10591                 ] 
10592             };  
10593         }
10594         
10595         if (this.before || this.after) {
10596             
10597             inputblock = {
10598                 cls : 'input-group',
10599                 cn :  [] 
10600             };
10601             
10602             if (this.before && typeof(this.before) == 'string') {
10603                 
10604                 inputblock.cn.push({
10605                     tag :'span',
10606                     cls : 'roo-input-before input-group-addon input-group-prepend input-group-text',
10607                     html : this.before
10608                 });
10609             }
10610             if (this.before && typeof(this.before) == 'object') {
10611                 this.before = Roo.factory(this.before);
10612                 
10613                 inputblock.cn.push({
10614                     tag :'span',
10615                     cls : 'roo-input-before input-group-prepend   input-group-' +
10616                         (this.before.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
10617                 });
10618             }
10619             
10620             inputblock.cn.push(input);
10621             
10622             if (this.after && typeof(this.after) == 'string') {
10623                 inputblock.cn.push({
10624                     tag :'span',
10625                     cls : 'roo-input-after input-group-append input-group-text input-group-addon',
10626                     html : this.after
10627                 });
10628             }
10629             if (this.after && typeof(this.after) == 'object') {
10630                 this.after = Roo.factory(this.after);
10631                 
10632                 inputblock.cn.push({
10633                     tag :'span',
10634                     cls : 'roo-input-after input-group-append  input-group-' +
10635                         (this.after.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
10636                 });
10637             }
10638             
10639             if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
10640                 inputblock.cls += ' has-feedback';
10641                 inputblock.cn.push(feedback);
10642             }
10643         };
10644         var indicator = {
10645             tag : 'i',
10646             cls : 'roo-required-indicator ' + (this.indicatorpos == 'right'  ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
10647             tooltip : 'This field is required'
10648         };
10649         if (this.allowBlank ) {
10650             indicator.style = this.allowBlank ? ' display:none' : '';
10651         }
10652         if (align ==='left' && this.fieldLabel.length) {
10653             
10654             cfg.cls += ' roo-form-group-label-left'  + (Roo.bootstrap.version == 4 ? ' row' : '');
10655             
10656             cfg.cn = [
10657                 indicator,
10658                 {
10659                     tag: 'label',
10660                     'for' :  id,
10661                     cls : 'control-label col-form-label',
10662                     html : this.fieldLabel
10663
10664                 },
10665                 {
10666                     cls : "", 
10667                     cn: [
10668                         inputblock
10669                     ]
10670                 }
10671             ];
10672             
10673             var labelCfg = cfg.cn[1];
10674             var contentCfg = cfg.cn[2];
10675             
10676             if(this.indicatorpos == 'right'){
10677                 cfg.cn = [
10678                     {
10679                         tag: 'label',
10680                         'for' :  id,
10681                         cls : 'control-label col-form-label',
10682                         cn : [
10683                             {
10684                                 tag : 'span',
10685                                 html : this.fieldLabel
10686                             },
10687                             indicator
10688                         ]
10689                     },
10690                     {
10691                         cls : "",
10692                         cn: [
10693                             inputblock
10694                         ]
10695                     }
10696
10697                 ];
10698                 
10699                 labelCfg = cfg.cn[0];
10700                 contentCfg = cfg.cn[1];
10701             
10702             }
10703             
10704             if(this.labelWidth > 12){
10705                 labelCfg.style = "width: " + this.labelWidth + 'px';
10706             }
10707             
10708             if(this.labelWidth < 13 && this.labelmd == 0){
10709                 this.labelmd = this.labelWidth;
10710             }
10711             
10712             if(this.labellg > 0){
10713                 labelCfg.cls += ' col-lg-' + this.labellg;
10714                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
10715             }
10716             
10717             if(this.labelmd > 0){
10718                 labelCfg.cls += ' col-md-' + this.labelmd;
10719                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
10720             }
10721             
10722             if(this.labelsm > 0){
10723                 labelCfg.cls += ' col-sm-' + this.labelsm;
10724                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
10725             }
10726             
10727             if(this.labelxs > 0){
10728                 labelCfg.cls += ' col-xs-' + this.labelxs;
10729                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
10730             }
10731             
10732             
10733         } else if ( this.fieldLabel.length) {
10734                 
10735             
10736             
10737             cfg.cn = [
10738                 {
10739                     tag : 'i',
10740                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
10741                     tooltip : 'This field is required',
10742                     style : this.allowBlank ? ' display:none' : '' 
10743                 },
10744                 {
10745                     tag: 'label',
10746                    //cls : 'input-group-addon',
10747                     html : this.fieldLabel
10748
10749                 },
10750
10751                inputblock
10752
10753            ];
10754            
10755            if(this.indicatorpos == 'right'){
10756        
10757                 cfg.cn = [
10758                     {
10759                         tag: 'label',
10760                        //cls : 'input-group-addon',
10761                         html : this.fieldLabel
10762
10763                     },
10764                     {
10765                         tag : 'i',
10766                         cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
10767                         tooltip : 'This field is required',
10768                         style : this.allowBlank ? ' display:none' : '' 
10769                     },
10770
10771                    inputblock
10772
10773                ];
10774
10775             }
10776
10777         } else {
10778             
10779             cfg.cn = [
10780
10781                     inputblock
10782
10783             ];
10784                 
10785                 
10786         };
10787         
10788         if (this.parentType === 'Navbar' &&  this.parent().bar) {
10789            cfg.cls += ' navbar-form';
10790         }
10791         
10792         if (this.parentType === 'NavGroup' && !(Roo.bootstrap.version == 4 && this.parent().form)) {
10793             // on BS4 we do this only if not form 
10794             cfg.cls += ' navbar-form';
10795             cfg.tag = 'li';
10796         }
10797         
10798         return cfg;
10799         
10800     },
10801     /**
10802      * return the real input element.
10803      */
10804     inputEl: function ()
10805     {
10806         return this.el.select('input.form-control',true).first();
10807     },
10808     
10809     tooltipEl : function()
10810     {
10811         return this.inputEl();
10812     },
10813     
10814     indicatorEl : function()
10815     {
10816         if (Roo.bootstrap.version == 4) {
10817             return false; // not enabled in v4 yet.
10818         }
10819         
10820         var indicator = this.el.select('i.roo-required-indicator',true).first();
10821         
10822         if(!indicator){
10823             return false;
10824         }
10825         
10826         return indicator;
10827         
10828     },
10829     
10830     setDisabled : function(v)
10831     {
10832         var i  = this.inputEl().dom;
10833         if (!v) {
10834             i.removeAttribute('disabled');
10835             return;
10836             
10837         }
10838         i.setAttribute('disabled','true');
10839     },
10840     initEvents : function()
10841     {
10842           
10843         this.inputEl().on("keydown" , this.fireKey,  this);
10844         this.inputEl().on("focus", this.onFocus,  this);
10845         this.inputEl().on("blur", this.onBlur,  this);
10846         
10847         this.inputEl().relayEvent('keyup', this);
10848         
10849         this.indicator = this.indicatorEl();
10850         
10851         if(this.indicator){
10852             this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible'); // changed from invisible??? - 
10853         }
10854  
10855         // reference to original value for reset
10856         this.originalValue = this.getValue();
10857         //Roo.form.TextField.superclass.initEvents.call(this);
10858         if(this.validationEvent == 'keyup'){
10859             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
10860             this.inputEl().on('keyup', this.filterValidation, this);
10861         }
10862         else if(this.validationEvent !== false){
10863             this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
10864         }
10865         
10866         if(this.selectOnFocus){
10867             this.on("focus", this.preFocus, this);
10868             
10869         }
10870         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
10871             this.inputEl().on("keypress", this.filterKeys, this);
10872         } else {
10873             this.inputEl().relayEvent('keypress', this);
10874         }
10875        /* if(this.grow){
10876             this.el.on("keyup", this.onKeyUp,  this, {buffer:50});
10877             this.el.on("click", this.autoSize,  this);
10878         }
10879         */
10880         if(this.inputEl().is('input[type=password]') && Roo.isSafari){
10881             this.inputEl().on('keydown', this.SafariOnKeyDown, this);
10882         }
10883         
10884         if (typeof(this.before) == 'object') {
10885             this.before.render(this.el.select('.roo-input-before',true).first());
10886         }
10887         if (typeof(this.after) == 'object') {
10888             this.after.render(this.el.select('.roo-input-after',true).first());
10889         }
10890         
10891         this.inputEl().on('change', this.onChange, this);
10892         
10893     },
10894     filterValidation : function(e){
10895         if(!e.isNavKeyPress()){
10896             this.validationTask.delay(this.validationDelay);
10897         }
10898     },
10899      /**
10900      * Validates the field value
10901      * @return {Boolean} True if the value is valid, else false
10902      */
10903     validate : function(){
10904         //if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
10905         if(this.disabled || this.validateValue(this.getRawValue())){
10906             this.markValid();
10907             return true;
10908         }
10909         
10910         this.markInvalid();
10911         return false;
10912     },
10913     
10914     
10915     /**
10916      * Validates a value according to the field's validation rules and marks the field as invalid
10917      * if the validation fails
10918      * @param {Mixed} value The value to validate
10919      * @return {Boolean} True if the value is valid, else false
10920      */
10921     validateValue : function(value)
10922     {
10923         if(this.getVisibilityEl().hasClass('hidden')){
10924             return true;
10925         }
10926         
10927         if(value.length < 1)  { // if it's blank
10928             if(this.allowBlank){
10929                 return true;
10930             }
10931             return false;
10932         }
10933         
10934         if(value.length < this.minLength){
10935             return false;
10936         }
10937         if(value.length > this.maxLength){
10938             return false;
10939         }
10940         if(this.vtype){
10941             var vt = Roo.form.VTypes;
10942             if(!vt[this.vtype](value, this)){
10943                 return false;
10944             }
10945         }
10946         if(typeof this.validator == "function"){
10947             var msg = this.validator(value);
10948             if(msg !== true){
10949                 return false;
10950             }
10951             if (typeof(msg) == 'string') {
10952                 this.invalidText = msg;
10953             }
10954         }
10955         
10956         if(this.regex && !this.regex.test(value)){
10957             return false;
10958         }
10959         
10960         return true;
10961     },
10962     
10963      // private
10964     fireKey : function(e){
10965         //Roo.log('field ' + e.getKey());
10966         if(e.isNavKeyPress()){
10967             this.fireEvent("specialkey", this, e);
10968         }
10969     },
10970     focus : function (selectText){
10971         if(this.rendered){
10972             this.inputEl().focus();
10973             if(selectText === true){
10974                 this.inputEl().dom.select();
10975             }
10976         }
10977         return this;
10978     } ,
10979     
10980     onFocus : function(){
10981         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
10982            // this.el.addClass(this.focusClass);
10983         }
10984         if(!this.hasFocus){
10985             this.hasFocus = true;
10986             this.startValue = this.getValue();
10987             this.fireEvent("focus", this);
10988         }
10989     },
10990     
10991     beforeBlur : Roo.emptyFn,
10992
10993     
10994     // private
10995     onBlur : function(){
10996         this.beforeBlur();
10997         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
10998             //this.el.removeClass(this.focusClass);
10999         }
11000         this.hasFocus = false;
11001         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
11002             this.validate();
11003         }
11004         var v = this.getValue();
11005         if(String(v) !== String(this.startValue)){
11006             this.fireEvent('change', this, v, this.startValue);
11007         }
11008         this.fireEvent("blur", this);
11009     },
11010     
11011     onChange : function(e)
11012     {
11013         var v = this.getValue();
11014         if(String(v) !== String(this.startValue)){
11015             this.fireEvent('change', this, v, this.startValue);
11016         }
11017         
11018     },
11019     
11020     /**
11021      * Resets the current field value to the originally loaded value and clears any validation messages
11022      */
11023     reset : function(){
11024         this.setValue(this.originalValue);
11025         this.validate();
11026     },
11027      /**
11028      * Returns the name of the field
11029      * @return {Mixed} name The name field
11030      */
11031     getName: function(){
11032         return this.name;
11033     },
11034      /**
11035      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
11036      * @return {Mixed} value The field value
11037      */
11038     getValue : function(){
11039         
11040         var v = this.inputEl().getValue();
11041         
11042         return v;
11043     },
11044     /**
11045      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
11046      * @return {Mixed} value The field value
11047      */
11048     getRawValue : function(){
11049         var v = this.inputEl().getValue();
11050         
11051         return v;
11052     },
11053     
11054     /**
11055      * Sets the underlying DOM field's value directly, bypassing validation.  To set the value with validation see {@link #setValue}.
11056      * @param {Mixed} value The value to set
11057      */
11058     setRawValue : function(v){
11059         return this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
11060     },
11061     
11062     selectText : function(start, end){
11063         var v = this.getRawValue();
11064         if(v.length > 0){
11065             start = start === undefined ? 0 : start;
11066             end = end === undefined ? v.length : end;
11067             var d = this.inputEl().dom;
11068             if(d.setSelectionRange){
11069                 d.setSelectionRange(start, end);
11070             }else if(d.createTextRange){
11071                 var range = d.createTextRange();
11072                 range.moveStart("character", start);
11073                 range.moveEnd("character", v.length-end);
11074                 range.select();
11075             }
11076         }
11077     },
11078     
11079     /**
11080      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
11081      * @param {Mixed} value The value to set
11082      */
11083     setValue : function(v){
11084         this.value = v;
11085         if(this.rendered){
11086             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
11087             this.validate();
11088         }
11089     },
11090     
11091     /*
11092     processValue : function(value){
11093         if(this.stripCharsRe){
11094             var newValue = value.replace(this.stripCharsRe, '');
11095             if(newValue !== value){
11096                 this.setRawValue(newValue);
11097                 return newValue;
11098             }
11099         }
11100         return value;
11101     },
11102   */
11103     preFocus : function(){
11104         
11105         if(this.selectOnFocus){
11106             this.inputEl().dom.select();
11107         }
11108     },
11109     filterKeys : function(e){
11110         var k = e.getKey();
11111         if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
11112             return;
11113         }
11114         var c = e.getCharCode(), cc = String.fromCharCode(c);
11115         if(Roo.isIE && (e.isSpecialKey() || !cc)){
11116             return;
11117         }
11118         if(!this.maskRe.test(cc)){
11119             e.stopEvent();
11120         }
11121     },
11122      /**
11123      * Clear any invalid styles/messages for this field
11124      */
11125     clearInvalid : function(){
11126         
11127         if(!this.el || this.preventMark){ // not rendered
11128             return;
11129         }
11130         
11131         
11132         this.el.removeClass([this.invalidClass, 'is-invalid']);
11133         
11134         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
11135             
11136             var feedback = this.el.select('.form-control-feedback', true).first();
11137             
11138             if(feedback){
11139                 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
11140             }
11141             
11142         }
11143         
11144         if(this.indicator){
11145             this.indicator.removeClass('visible');
11146             this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
11147         }
11148         
11149         this.fireEvent('valid', this);
11150     },
11151     
11152      /**
11153      * Mark this field as valid
11154      */
11155     markValid : function()
11156     {
11157         if(!this.el  || this.preventMark){ // not rendered...
11158             return;
11159         }
11160         
11161         this.el.removeClass([this.invalidClass, this.validClass]);
11162         this.inputEl().removeClass(['is-valid', 'is-invalid']);
11163
11164         var feedback = this.el.select('.form-control-feedback', true).first();
11165             
11166         if(feedback){
11167             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
11168         }
11169         
11170         if(this.indicator){
11171             this.indicator.removeClass('visible');
11172             this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
11173         }
11174         
11175         if(this.disabled){
11176             return;
11177         }
11178         
11179            
11180         if(this.allowBlank && !this.getRawValue().length){
11181             return;
11182         }
11183         if (Roo.bootstrap.version == 3) {
11184             this.el.addClass(this.validClass);
11185         } else {
11186             this.inputEl().addClass('is-valid');
11187         }
11188
11189         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
11190             
11191             var feedback = this.el.select('.form-control-feedback', true).first();
11192             
11193             if(feedback){
11194                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
11195                 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
11196             }
11197             
11198         }
11199         
11200         this.fireEvent('valid', this);
11201     },
11202     
11203      /**
11204      * Mark this field as invalid
11205      * @param {String} msg The validation message
11206      */
11207     markInvalid : function(msg)
11208     {
11209         if(!this.el  || this.preventMark){ // not rendered
11210             return;
11211         }
11212         
11213         this.el.removeClass([this.invalidClass, this.validClass]);
11214         this.inputEl().removeClass(['is-valid', 'is-invalid']);
11215         
11216         var feedback = this.el.select('.form-control-feedback', true).first();
11217             
11218         if(feedback){
11219             this.el.select('.form-control-feedback', true).first().removeClass(
11220                     [this.invalidFeedbackClass, this.validFeedbackClass]);
11221         }
11222
11223         if(this.disabled){
11224             return;
11225         }
11226         
11227         if(this.allowBlank && !this.getRawValue().length){
11228             return;
11229         }
11230         
11231         if(this.indicator){
11232             this.indicator.removeClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
11233             this.indicator.addClass('visible');
11234         }
11235         if (Roo.bootstrap.version == 3) {
11236             this.el.addClass(this.invalidClass);
11237         } else {
11238             this.inputEl().addClass('is-invalid');
11239         }
11240         
11241         
11242         
11243         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
11244             
11245             var feedback = this.el.select('.form-control-feedback', true).first();
11246             
11247             if(feedback){
11248                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
11249                 
11250                 if(this.getValue().length || this.forceFeedback){
11251                     this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
11252                 }
11253                 
11254             }
11255             
11256         }
11257         
11258         this.fireEvent('invalid', this, msg);
11259     },
11260     // private
11261     SafariOnKeyDown : function(event)
11262     {
11263         // this is a workaround for a password hang bug on chrome/ webkit.
11264         if (this.inputEl().dom.type != 'password') {
11265             return;
11266         }
11267         
11268         var isSelectAll = false;
11269         
11270         if(this.inputEl().dom.selectionEnd > 0){
11271             isSelectAll = (this.inputEl().dom.selectionEnd - this.inputEl().dom.selectionStart - this.getValue().length == 0) ? true : false;
11272         }
11273         if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
11274             event.preventDefault();
11275             this.setValue('');
11276             return;
11277         }
11278         
11279         if(isSelectAll  && event.getCharCode() > 31 && !event.ctrlKey) { // not backspace and delete key (or ctrl-v)
11280             
11281             event.preventDefault();
11282             // this is very hacky as keydown always get's upper case.
11283             //
11284             var cc = String.fromCharCode(event.getCharCode());
11285             this.setValue( event.shiftKey ?  cc : cc.toLowerCase());
11286             
11287         }
11288     },
11289     adjustWidth : function(tag, w){
11290         tag = tag.toLowerCase();
11291         if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
11292             if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
11293                 if(tag == 'input'){
11294                     return w + 2;
11295                 }
11296                 if(tag == 'textarea'){
11297                     return w-2;
11298                 }
11299             }else if(Roo.isOpera){
11300                 if(tag == 'input'){
11301                     return w + 2;
11302                 }
11303                 if(tag == 'textarea'){
11304                     return w-2;
11305                 }
11306             }
11307         }
11308         return w;
11309     },
11310     
11311     setFieldLabel : function(v)
11312     {
11313         if(!this.rendered){
11314             return;
11315         }
11316         
11317         if(this.indicatorEl()){
11318             var ar = this.el.select('label > span',true);
11319             
11320             if (ar.elements.length) {
11321                 this.el.select('label > span',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
11322                 this.fieldLabel = v;
11323                 return;
11324             }
11325             
11326             var br = this.el.select('label',true);
11327             
11328             if(br.elements.length) {
11329                 this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
11330                 this.fieldLabel = v;
11331                 return;
11332             }
11333             
11334             Roo.log('Cannot Found any of label > span || label in input');
11335             return;
11336         }
11337         
11338         this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
11339         this.fieldLabel = v;
11340         
11341         
11342     }
11343 });
11344
11345  
11346 /*
11347  * - LGPL
11348  *
11349  * Input
11350  * 
11351  */
11352
11353 /**
11354  * @class Roo.bootstrap.TextArea
11355  * @extends Roo.bootstrap.Input
11356  * Bootstrap TextArea class
11357  * @cfg {Number} cols Specifies the visible width of a text area
11358  * @cfg {Number} rows Specifies the visible number of lines in a text area
11359  * @cfg {string} wrap (soft|hard)Specifies how the text in a text area is to be wrapped when submitted in a form
11360  * @cfg {string} resize (none|both|horizontal|vertical|inherit|initial)
11361  * @cfg {string} html text
11362  * 
11363  * @constructor
11364  * Create a new TextArea
11365  * @param {Object} config The config object
11366  */
11367
11368 Roo.bootstrap.TextArea = function(config){
11369     Roo.bootstrap.TextArea.superclass.constructor.call(this, config);
11370    
11371 };
11372
11373 Roo.extend(Roo.bootstrap.TextArea, Roo.bootstrap.Input,  {
11374      
11375     cols : false,
11376     rows : 5,
11377     readOnly : false,
11378     warp : 'soft',
11379     resize : false,
11380     value: false,
11381     html: false,
11382     
11383     getAutoCreate : function(){
11384         
11385         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
11386         
11387         var id = Roo.id();
11388         
11389         var cfg = {};
11390         
11391         if(this.inputType != 'hidden'){
11392             cfg.cls = 'form-group' //input-group
11393         }
11394         
11395         var input =  {
11396             tag: 'textarea',
11397             id : id,
11398             warp : this.warp,
11399             rows : this.rows,
11400             value : this.value || '',
11401             html: this.html || '',
11402             cls : 'form-control',
11403             placeholder : this.placeholder || '' 
11404             
11405         };
11406         
11407         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
11408             input.maxLength = this.maxLength;
11409         }
11410         
11411         if(this.resize){
11412             input.style = (typeof(input.style) == 'undefined') ? 'resize:' + this.resize : input.style + 'resize:' + this.resize;
11413         }
11414         
11415         if(this.cols){
11416             input.cols = this.cols;
11417         }
11418         
11419         if (this.readOnly) {
11420             input.readonly = true;
11421         }
11422         
11423         if (this.name) {
11424             input.name = this.name;
11425         }
11426         
11427         if (this.size) {
11428             input.cls = (typeof(input.cls) == 'undefined') ? 'input-' + this.size : input.cls + ' input-' + this.size;
11429         }
11430         
11431         var settings=this;
11432         ['xs','sm','md','lg'].map(function(size){
11433             if (settings[size]) {
11434                 cfg.cls += ' col-' + size + '-' + settings[size];
11435             }
11436         });
11437         
11438         var inputblock = input;
11439         
11440         if(this.hasFeedback && !this.allowBlank){
11441             
11442             var feedback = {
11443                 tag: 'span',
11444                 cls: 'glyphicon form-control-feedback'
11445             };
11446
11447             inputblock = {
11448                 cls : 'has-feedback',
11449                 cn :  [
11450                     input,
11451                     feedback
11452                 ] 
11453             };  
11454         }
11455         
11456         
11457         if (this.before || this.after) {
11458             
11459             inputblock = {
11460                 cls : 'input-group',
11461                 cn :  [] 
11462             };
11463             if (this.before) {
11464                 inputblock.cn.push({
11465                     tag :'span',
11466                     cls : 'input-group-addon',
11467                     html : this.before
11468                 });
11469             }
11470             
11471             inputblock.cn.push(input);
11472             
11473             if(this.hasFeedback && !this.allowBlank){
11474                 inputblock.cls += ' has-feedback';
11475                 inputblock.cn.push(feedback);
11476             }
11477             
11478             if (this.after) {
11479                 inputblock.cn.push({
11480                     tag :'span',
11481                     cls : 'input-group-addon',
11482                     html : this.after
11483                 });
11484             }
11485             
11486         }
11487         
11488         if (align ==='left' && this.fieldLabel.length) {
11489             cfg.cn = [
11490                 {
11491                     tag: 'label',
11492                     'for' :  id,
11493                     cls : 'control-label',
11494                     html : this.fieldLabel
11495                 },
11496                 {
11497                     cls : "",
11498                     cn: [
11499                         inputblock
11500                     ]
11501                 }
11502
11503             ];
11504             
11505             if(this.labelWidth > 12){
11506                 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
11507             }
11508
11509             if(this.labelWidth < 13 && this.labelmd == 0){
11510                 this.labelmd = this.labelWidth;
11511             }
11512
11513             if(this.labellg > 0){
11514                 cfg.cn[0].cls += ' col-lg-' + this.labellg;
11515                 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
11516             }
11517
11518             if(this.labelmd > 0){
11519                 cfg.cn[0].cls += ' col-md-' + this.labelmd;
11520                 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
11521             }
11522
11523             if(this.labelsm > 0){
11524                 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
11525                 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
11526             }
11527
11528             if(this.labelxs > 0){
11529                 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
11530                 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
11531             }
11532             
11533         } else if ( this.fieldLabel.length) {
11534             cfg.cn = [
11535
11536                {
11537                    tag: 'label',
11538                    //cls : 'input-group-addon',
11539                    html : this.fieldLabel
11540
11541                },
11542
11543                inputblock
11544
11545            ];
11546
11547         } else {
11548
11549             cfg.cn = [
11550
11551                 inputblock
11552
11553             ];
11554                 
11555         }
11556         
11557         if (this.disabled) {
11558             input.disabled=true;
11559         }
11560         
11561         return cfg;
11562         
11563     },
11564     /**
11565      * return the real textarea element.
11566      */
11567     inputEl: function ()
11568     {
11569         return this.el.select('textarea.form-control',true).first();
11570     },
11571     
11572     /**
11573      * Clear any invalid styles/messages for this field
11574      */
11575     clearInvalid : function()
11576     {
11577         
11578         if(!this.el || this.preventMark){ // not rendered
11579             return;
11580         }
11581         
11582         var label = this.el.select('label', true).first();
11583         var icon = this.el.select('i.fa-star', true).first();
11584         
11585         if(label && icon){
11586             icon.remove();
11587         }
11588         this.el.removeClass( this.validClass);
11589         this.inputEl().removeClass('is-invalid');
11590          
11591         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
11592             
11593             var feedback = this.el.select('.form-control-feedback', true).first();
11594             
11595             if(feedback){
11596                 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
11597             }
11598             
11599         }
11600         
11601         this.fireEvent('valid', this);
11602     },
11603     
11604      /**
11605      * Mark this field as valid
11606      */
11607     markValid : function()
11608     {
11609         if(!this.el  || this.preventMark){ // not rendered
11610             return;
11611         }
11612         
11613         this.el.removeClass([this.invalidClass, this.validClass]);
11614         this.inputEl().removeClass(['is-valid', 'is-invalid']);
11615         
11616         var feedback = this.el.select('.form-control-feedback', true).first();
11617             
11618         if(feedback){
11619             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
11620         }
11621
11622         if(this.disabled || this.allowBlank){
11623             return;
11624         }
11625         
11626         var label = this.el.select('label', true).first();
11627         var icon = this.el.select('i.fa-star', true).first();
11628         
11629         if(label && icon){
11630             icon.remove();
11631         }
11632         if (Roo.bootstrap.version == 3) {
11633             this.el.addClass(this.validClass);
11634         } else {
11635             this.inputEl().addClass('is-valid');
11636         }
11637         
11638         
11639         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
11640             
11641             var feedback = this.el.select('.form-control-feedback', true).first();
11642             
11643             if(feedback){
11644                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
11645                 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
11646             }
11647             
11648         }
11649         
11650         this.fireEvent('valid', this);
11651     },
11652     
11653      /**
11654      * Mark this field as invalid
11655      * @param {String} msg The validation message
11656      */
11657     markInvalid : function(msg)
11658     {
11659         if(!this.el  || this.preventMark){ // not rendered
11660             return;
11661         }
11662         
11663         this.el.removeClass([this.invalidClass, this.validClass]);
11664         this.inputEl().removeClass(['is-valid', 'is-invalid']);
11665         
11666         var feedback = this.el.select('.form-control-feedback', true).first();
11667             
11668         if(feedback){
11669             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
11670         }
11671
11672         if(this.disabled || this.allowBlank){
11673             return;
11674         }
11675         
11676         var label = this.el.select('label', true).first();
11677         var icon = this.el.select('i.fa-star', true).first();
11678         
11679         if(!this.getValue().length && label && !icon){
11680             this.el.createChild({
11681                 tag : 'i',
11682                 cls : 'text-danger fa fa-lg fa-star',
11683                 tooltip : 'This field is required',
11684                 style : 'margin-right:5px;'
11685             }, label, true);
11686         }
11687         
11688         if (Roo.bootstrap.version == 3) {
11689             this.el.addClass(this.invalidClass);
11690         } else {
11691             this.inputEl().addClass('is-invalid');
11692         }
11693         
11694         // fixme ... this may be depricated need to test..
11695         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
11696             
11697             var feedback = this.el.select('.form-control-feedback', true).first();
11698             
11699             if(feedback){
11700                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
11701                 
11702                 if(this.getValue().length || this.forceFeedback){
11703                     this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
11704                 }
11705                 
11706             }
11707             
11708         }
11709         
11710         this.fireEvent('invalid', this, msg);
11711     }
11712 });
11713
11714  
11715 /*
11716  * - LGPL
11717  *
11718  * trigger field - base class for combo..
11719  * 
11720  */
11721  
11722 /**
11723  * @class Roo.bootstrap.TriggerField
11724  * @extends Roo.bootstrap.Input
11725  * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
11726  * The trigger has no default action, so you must assign a function to implement the trigger click handler by
11727  * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
11728  * for which you can provide a custom implementation.  For example:
11729  * <pre><code>
11730 var trigger = new Roo.bootstrap.TriggerField();
11731 trigger.onTriggerClick = myTriggerFn;
11732 trigger.applyTo('my-field');
11733 </code></pre>
11734  *
11735  * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
11736  * {@link Roo.bootstrap.DateField} and {@link Roo.bootstrap.ComboBox} are perfect examples of this.
11737  * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
11738  * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
11739  * @cfg {String} caret (search|calendar) BS3 only - carat fa name
11740
11741  * @constructor
11742  * Create a new TriggerField.
11743  * @param {Object} config Configuration options (valid {@Roo.bootstrap.Input} config options will also be applied
11744  * to the base TextField)
11745  */
11746 Roo.bootstrap.TriggerField = function(config){
11747     this.mimicing = false;
11748     Roo.bootstrap.TriggerField.superclass.constructor.call(this, config);
11749 };
11750
11751 Roo.extend(Roo.bootstrap.TriggerField, Roo.bootstrap.Input,  {
11752     /**
11753      * @cfg {String} triggerClass A CSS class to apply to the trigger
11754      */
11755      /**
11756      * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
11757      */
11758     hideTrigger:false,
11759
11760     /**
11761      * @cfg {Boolean} removable (true|false) special filter default false
11762      */
11763     removable : false,
11764     
11765     /** @cfg {Boolean} grow @hide */
11766     /** @cfg {Number} growMin @hide */
11767     /** @cfg {Number} growMax @hide */
11768
11769     /**
11770      * @hide 
11771      * @method
11772      */
11773     autoSize: Roo.emptyFn,
11774     // private
11775     monitorTab : true,
11776     // private
11777     deferHeight : true,
11778
11779     
11780     actionMode : 'wrap',
11781     
11782     caret : false,
11783     
11784     
11785     getAutoCreate : function(){
11786        
11787         var align = this.labelAlign || this.parentLabelAlign();
11788         
11789         var id = Roo.id();
11790         
11791         var cfg = {
11792             cls: 'form-group' //input-group
11793         };
11794         
11795         
11796         var input =  {
11797             tag: 'input',
11798             id : id,
11799             type : this.inputType,
11800             cls : 'form-control',
11801             autocomplete: 'new-password',
11802             placeholder : this.placeholder || '' 
11803             
11804         };
11805         if (this.name) {
11806             input.name = this.name;
11807         }
11808         if (this.size) {
11809             input.cls += ' input-' + this.size;
11810         }
11811         
11812         if (this.disabled) {
11813             input.disabled=true;
11814         }
11815         
11816         var inputblock = input;
11817         
11818         if(this.hasFeedback && !this.allowBlank){
11819             
11820             var feedback = {
11821                 tag: 'span',
11822                 cls: 'glyphicon form-control-feedback'
11823             };
11824             
11825             if(this.removable && !this.editable  ){
11826                 inputblock = {
11827                     cls : 'has-feedback',
11828                     cn :  [
11829                         inputblock,
11830                         {
11831                             tag: 'button',
11832                             html : 'x',
11833                             cls : 'roo-combo-removable-btn close'
11834                         },
11835                         feedback
11836                     ] 
11837                 };
11838             } else {
11839                 inputblock = {
11840                     cls : 'has-feedback',
11841                     cn :  [
11842                         inputblock,
11843                         feedback
11844                     ] 
11845                 };
11846             }
11847
11848         } else {
11849             if(this.removable && !this.editable ){
11850                 inputblock = {
11851                     cls : 'roo-removable',
11852                     cn :  [
11853                         inputblock,
11854                         {
11855                             tag: 'button',
11856                             html : 'x',
11857                             cls : 'roo-combo-removable-btn close'
11858                         }
11859                     ] 
11860                 };
11861             }
11862         }
11863         
11864         if (this.before || this.after) {
11865             
11866             inputblock = {
11867                 cls : 'input-group',
11868                 cn :  [] 
11869             };
11870             if (this.before) {
11871                 inputblock.cn.push({
11872                     tag :'span',
11873                     cls : 'input-group-addon input-group-prepend input-group-text',
11874                     html : this.before
11875                 });
11876             }
11877             
11878             inputblock.cn.push(input);
11879             
11880             if(this.hasFeedback && !this.allowBlank){
11881                 inputblock.cls += ' has-feedback';
11882                 inputblock.cn.push(feedback);
11883             }
11884             
11885             if (this.after) {
11886                 inputblock.cn.push({
11887                     tag :'span',
11888                     cls : 'input-group-addon input-group-append input-group-text',
11889                     html : this.after
11890                 });
11891             }
11892             
11893         };
11894         
11895       
11896         
11897         var ibwrap = inputblock;
11898         
11899         if(this.multiple){
11900             ibwrap = {
11901                 tag: 'ul',
11902                 cls: 'roo-select2-choices',
11903                 cn:[
11904                     {
11905                         tag: 'li',
11906                         cls: 'roo-select2-search-field',
11907                         cn: [
11908
11909                             inputblock
11910                         ]
11911                     }
11912                 ]
11913             };
11914                 
11915         }
11916         
11917         var combobox = {
11918             cls: 'roo-select2-container input-group',
11919             cn: [
11920                  {
11921                     tag: 'input',
11922                     type : 'hidden',
11923                     cls: 'form-hidden-field'
11924                 },
11925                 ibwrap
11926             ]
11927         };
11928         
11929         if(!this.multiple && this.showToggleBtn){
11930             
11931             var caret = {
11932                         tag: 'span',
11933                         cls: 'caret'
11934              };
11935             if (this.caret != false) {
11936                 caret = {
11937                      tag: 'i',
11938                      cls: 'fa fa-' + this.caret
11939                 };
11940                 
11941             }
11942             
11943             combobox.cn.push({
11944                 tag :'span',
11945                 cls : 'input-group-addon input-group-append input-group-text btn dropdown-toggle',
11946                 cn : [
11947                     Roo.bootstrap.version == 3 ? caret : '',
11948                     {
11949                         tag: 'span',
11950                         cls: 'combobox-clear',
11951                         cn  : [
11952                             {
11953                                 tag : 'i',
11954                                 cls: 'icon-remove'
11955                             }
11956                         ]
11957                     }
11958                 ]
11959
11960             })
11961         }
11962         
11963         if(this.multiple){
11964             combobox.cls += ' roo-select2-container-multi';
11965         }
11966          var indicator = {
11967             tag : 'i',
11968             cls : 'roo-required-indicator ' + (this.indicatorpos == 'right'  ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
11969             tooltip : 'This field is required'
11970         };
11971         if (Roo.bootstrap.version == 4) {
11972             indicator = {
11973                 tag : 'i',
11974                 style : 'display:none'
11975             };
11976         }
11977         
11978         
11979         if (align ==='left' && this.fieldLabel.length) {
11980             
11981             cfg.cls += ' roo-form-group-label-left'  + (Roo.bootstrap.version == 4 ? ' row' : '');
11982
11983             cfg.cn = [
11984                 indicator,
11985                 {
11986                     tag: 'label',
11987                     'for' :  id,
11988                     cls : 'control-label',
11989                     html : this.fieldLabel
11990
11991                 },
11992                 {
11993                     cls : "", 
11994                     cn: [
11995                         combobox
11996                     ]
11997                 }
11998
11999             ];
12000             
12001             var labelCfg = cfg.cn[1];
12002             var contentCfg = cfg.cn[2];
12003             
12004             if(this.indicatorpos == 'right'){
12005                 cfg.cn = [
12006                     {
12007                         tag: 'label',
12008                         'for' :  id,
12009                         cls : 'control-label',
12010                         cn : [
12011                             {
12012                                 tag : 'span',
12013                                 html : this.fieldLabel
12014                             },
12015                             indicator
12016                         ]
12017                     },
12018                     {
12019                         cls : "", 
12020                         cn: [
12021                             combobox
12022                         ]
12023                     }
12024
12025                 ];
12026                 
12027                 labelCfg = cfg.cn[0];
12028                 contentCfg = cfg.cn[1];
12029             }
12030             
12031             if(this.labelWidth > 12){
12032                 labelCfg.style = "width: " + this.labelWidth + 'px';
12033             }
12034             
12035             if(this.labelWidth < 13 && this.labelmd == 0){
12036                 this.labelmd = this.labelWidth;
12037             }
12038             
12039             if(this.labellg > 0){
12040                 labelCfg.cls += ' col-lg-' + this.labellg;
12041                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
12042             }
12043             
12044             if(this.labelmd > 0){
12045                 labelCfg.cls += ' col-md-' + this.labelmd;
12046                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
12047             }
12048             
12049             if(this.labelsm > 0){
12050                 labelCfg.cls += ' col-sm-' + this.labelsm;
12051                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
12052             }
12053             
12054             if(this.labelxs > 0){
12055                 labelCfg.cls += ' col-xs-' + this.labelxs;
12056                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
12057             }
12058             
12059         } else if ( this.fieldLabel.length) {
12060 //                Roo.log(" label");
12061             cfg.cn = [
12062                 indicator,
12063                {
12064                    tag: 'label',
12065                    //cls : 'input-group-addon',
12066                    html : this.fieldLabel
12067
12068                },
12069
12070                combobox
12071
12072             ];
12073             
12074             if(this.indicatorpos == 'right'){
12075                 
12076                 cfg.cn = [
12077                     {
12078                        tag: 'label',
12079                        cn : [
12080                            {
12081                                tag : 'span',
12082                                html : this.fieldLabel
12083                            },
12084                            indicator
12085                        ]
12086
12087                     },
12088                     combobox
12089
12090                 ];
12091
12092             }
12093
12094         } else {
12095             
12096 //                Roo.log(" no label && no align");
12097                 cfg = combobox
12098                      
12099                 
12100         }
12101         
12102         var settings=this;
12103         ['xs','sm','md','lg'].map(function(size){
12104             if (settings[size]) {
12105                 cfg.cls += ' col-' + size + '-' + settings[size];
12106             }
12107         });
12108         
12109         return cfg;
12110         
12111     },
12112     
12113     
12114     
12115     // private
12116     onResize : function(w, h){
12117 //        Roo.bootstrap.TriggerField.superclass.onResize.apply(this, arguments);
12118 //        if(typeof w == 'number'){
12119 //            var x = w - this.trigger.getWidth();
12120 //            this.inputEl().setWidth(this.adjustWidth('input', x));
12121 //            this.trigger.setStyle('left', x+'px');
12122 //        }
12123     },
12124
12125     // private
12126     adjustSize : Roo.BoxComponent.prototype.adjustSize,
12127
12128     // private
12129     getResizeEl : function(){
12130         return this.inputEl();
12131     },
12132
12133     // private
12134     getPositionEl : function(){
12135         return this.inputEl();
12136     },
12137
12138     // private
12139     alignErrorIcon : function(){
12140         this.errorIcon.alignTo(this.inputEl(), 'tl-tr', [2, 0]);
12141     },
12142
12143     // private
12144     initEvents : function(){
12145         
12146         this.createList();
12147         
12148         Roo.bootstrap.TriggerField.superclass.initEvents.call(this);
12149         //this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
12150         if(!this.multiple && this.showToggleBtn){
12151             this.trigger = this.el.select('span.dropdown-toggle',true).first();
12152             if(this.hideTrigger){
12153                 this.trigger.setDisplayed(false);
12154             }
12155             this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
12156         }
12157         
12158         if(this.multiple){
12159             this.inputEl().on("click", this.onTriggerClick, this, {preventDefault:true});
12160         }
12161         
12162         if(this.removable && !this.editable && !this.tickable){
12163             var close = this.closeTriggerEl();
12164             
12165             if(close){
12166                 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
12167                 close.on('click', this.removeBtnClick, this, close);
12168             }
12169         }
12170         
12171         //this.trigger.addClassOnOver('x-form-trigger-over');
12172         //this.trigger.addClassOnClick('x-form-trigger-click');
12173         
12174         //if(!this.width){
12175         //    this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
12176         //}
12177     },
12178     
12179     closeTriggerEl : function()
12180     {
12181         var close = this.el.select('.roo-combo-removable-btn', true).first();
12182         return close ? close : false;
12183     },
12184     
12185     removeBtnClick : function(e, h, el)
12186     {
12187         e.preventDefault();
12188         
12189         if(this.fireEvent("remove", this) !== false){
12190             this.reset();
12191             this.fireEvent("afterremove", this)
12192         }
12193     },
12194     
12195     createList : function()
12196     {
12197         this.list = Roo.get(document.body).createChild({
12198             tag: Roo.bootstrap.version == 4 ? 'div' : 'ul',
12199             cls: 'typeahead typeahead-long dropdown-menu',
12200             style: 'display:none'
12201         });
12202         
12203         this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';;
12204         
12205     },
12206
12207     // private
12208     initTrigger : function(){
12209        
12210     },
12211
12212     // private
12213     onDestroy : function(){
12214         if(this.trigger){
12215             this.trigger.removeAllListeners();
12216           //  this.trigger.remove();
12217         }
12218         //if(this.wrap){
12219         //    this.wrap.remove();
12220         //}
12221         Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
12222     },
12223
12224     // private
12225     onFocus : function(){
12226         Roo.bootstrap.TriggerField.superclass.onFocus.call(this);
12227         /*
12228         if(!this.mimicing){
12229             this.wrap.addClass('x-trigger-wrap-focus');
12230             this.mimicing = true;
12231             Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
12232             if(this.monitorTab){
12233                 this.el.on("keydown", this.checkTab, this);
12234             }
12235         }
12236         */
12237     },
12238
12239     // private
12240     checkTab : function(e){
12241         if(e.getKey() == e.TAB){
12242             this.triggerBlur();
12243         }
12244     },
12245
12246     // private
12247     onBlur : function(){
12248         // do nothing
12249     },
12250
12251     // private
12252     mimicBlur : function(e, t){
12253         /*
12254         if(!this.wrap.contains(t) && this.validateBlur()){
12255             this.triggerBlur();
12256         }
12257         */
12258     },
12259
12260     // private
12261     triggerBlur : function(){
12262         this.mimicing = false;
12263         Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
12264         if(this.monitorTab){
12265             this.el.un("keydown", this.checkTab, this);
12266         }
12267         //this.wrap.removeClass('x-trigger-wrap-focus');
12268         Roo.bootstrap.TriggerField.superclass.onBlur.call(this);
12269     },
12270
12271     // private
12272     // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
12273     validateBlur : function(e, t){
12274         return true;
12275     },
12276
12277     // private
12278     onDisable : function(){
12279         this.inputEl().dom.disabled = true;
12280         //Roo.bootstrap.TriggerField.superclass.onDisable.call(this);
12281         //if(this.wrap){
12282         //    this.wrap.addClass('x-item-disabled');
12283         //}
12284     },
12285
12286     // private
12287     onEnable : function(){
12288         this.inputEl().dom.disabled = false;
12289         //Roo.bootstrap.TriggerField.superclass.onEnable.call(this);
12290         //if(this.wrap){
12291         //    this.el.removeClass('x-item-disabled');
12292         //}
12293     },
12294
12295     // private
12296     onShow : function(){
12297         var ae = this.getActionEl();
12298         
12299         if(ae){
12300             ae.dom.style.display = '';
12301             ae.dom.style.visibility = 'visible';
12302         }
12303     },
12304
12305     // private
12306     
12307     onHide : function(){
12308         var ae = this.getActionEl();
12309         ae.dom.style.display = 'none';
12310     },
12311
12312     /**
12313      * The function that should handle the trigger's click event.  This method does nothing by default until overridden
12314      * by an implementing function.
12315      * @method
12316      * @param {EventObject} e
12317      */
12318     onTriggerClick : Roo.emptyFn
12319 });
12320  
12321 /*
12322 * Licence: LGPL
12323 */
12324
12325 /**
12326  * @class Roo.bootstrap.CardUploader
12327  * @extends Roo.bootstrap.Button
12328  * Bootstrap Card Uploader class - it's a button which when you add files to it, adds cards below with preview and the name...
12329  * @cfg {Number} errorTimeout default 3000
12330  * @cfg {Array}  images  an array of ?? Img objects ??? when loading existing files..
12331  * @cfg {Array}  html The button text.
12332
12333  *
12334  * @constructor
12335  * Create a new CardUploader
12336  * @param {Object} config The config object
12337  */
12338
12339 Roo.bootstrap.CardUploader = function(config){
12340     
12341  
12342     
12343     Roo.bootstrap.CardUploader.superclass.constructor.call(this, config);
12344     
12345     
12346     this.fileCollection   = new Roo.util.MixedCollection(false,function(r) {
12347         return r.data.id
12348         });
12349     
12350     
12351 };
12352
12353 Roo.extend(Roo.bootstrap.CardUploader, Roo.bootstrap.Input,  {
12354     
12355      
12356     errorTimeout : 3000,
12357      
12358     images : false,
12359    
12360     fileCollection : false,
12361     allowBlank : true,
12362     
12363     getAutoCreate : function()
12364     {
12365         
12366         var cfg =  {
12367             cls :'form-group' ,
12368             cn : [
12369                
12370                 {
12371                     tag: 'label',
12372                    //cls : 'input-group-addon',
12373                     html : this.fieldLabel
12374
12375                 },
12376
12377                 {
12378                     tag: 'input',
12379                     type : 'hidden',
12380                     value : this.value,
12381                     cls : 'd-none  form-control'
12382                 },
12383                 
12384                 {
12385                     tag: 'input',
12386                     multiple : 'multiple',
12387                     type : 'file',
12388                     cls : 'd-none  roo-card-upload-selector'
12389                 },
12390                 
12391                 {
12392                     cls : 'roo-card-uploader-button-container w-100 mb-2'
12393                 },
12394                 {
12395                     cls : 'card-columns roo-card-uploader-container'
12396                 }
12397
12398             ]
12399         };
12400            
12401          
12402         return cfg;
12403     },
12404     
12405     getChildContainer : function() /// what children are added to.
12406     {
12407         return this.containerEl;
12408     },
12409    
12410     getButtonContainer : function() /// what children are added to.
12411     {
12412         return this.el.select(".roo-card-uploader-button-container").first();
12413     },
12414    
12415     initEvents : function()
12416     {
12417         
12418         Roo.bootstrap.Input.prototype.initEvents.call(this);
12419         
12420         var t = this;
12421         this.addxtype({
12422             xns: Roo.bootstrap,
12423
12424             xtype : 'Button',
12425             container_method : 'getButtonContainer' ,            
12426             html :  this.html, // fix changable?
12427             cls : 'w-100 ',
12428             listeners : {
12429                 'click' : function(btn, e) {
12430                     t.onClick(e);
12431                 }
12432             }
12433         });
12434         
12435         
12436         
12437         
12438         this.urlAPI = (window.createObjectURL && window) || 
12439                                 (window.URL && URL.revokeObjectURL && URL) || 
12440                                 (window.webkitURL && webkitURL);
12441                         
12442          
12443          
12444          
12445         this.selectorEl = this.el.select('.roo-card-upload-selector', true).first();
12446         
12447         this.selectorEl.on('change', this.onFileSelected, this);
12448         if (this.images) {
12449             var t = this;
12450             this.images.forEach(function(img) {
12451                 t.addCard(img)
12452             });
12453             this.images = false;
12454         }
12455         this.containerEl = this.el.select('.roo-card-uploader-container', true).first();
12456          
12457        
12458     },
12459     
12460    
12461     onClick : function(e)
12462     {
12463         e.preventDefault();
12464          
12465         this.selectorEl.dom.click();
12466          
12467     },
12468     
12469     onFileSelected : function(e)
12470     {
12471         e.preventDefault();
12472         
12473         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
12474             return;
12475         }
12476         
12477         Roo.each(this.selectorEl.dom.files, function(file){    
12478             this.addFile(file);
12479         }, this);
12480          
12481     },
12482     
12483       
12484     
12485       
12486     
12487     addFile : function(file)
12488     {
12489            
12490         if(typeof(file) === 'string'){
12491             throw "Add file by name?"; // should not happen
12492             return;
12493         }
12494         
12495         if(!file || !this.urlAPI){
12496             return;
12497         }
12498         
12499         // file;
12500         // file.type;
12501         
12502         var _this = this;
12503         
12504         
12505         var url = _this.urlAPI.createObjectURL( file);
12506            
12507         this.addCard({
12508             id : Roo.bootstrap.CardUploader.ID--,
12509             is_uploaded : false,
12510             src : url,
12511             title : file.name,
12512             mimetype : file.type,
12513             preview : false,
12514             is_deleted : 0
12515         })
12516         
12517     },
12518     
12519     addCard : function (data)
12520     {
12521         // hidden input element?
12522         // if the file is not an image...
12523         //then we need to use something other that and header_image
12524         var t = this;
12525         //   remove.....
12526         var footer = [
12527             {
12528                 xns : Roo.bootstrap,
12529                 xtype : 'CardFooter',
12530                 items: [
12531                     {
12532                         xns : Roo.bootstrap,
12533                         xtype : 'Element',
12534                         cls : 'd-flex',
12535                         items : [
12536                             
12537                             {
12538                                 xns : Roo.bootstrap,
12539                                 xtype : 'Button',
12540                                 html : String.format("<small>{0}</small>", data.title),
12541                                 cls : 'col-11 text-left',
12542                                 size: 'sm',
12543                                 weight: 'link',
12544                                 fa : 'download',
12545                                 listeners : {
12546                                     click : function() {
12547                                         this.downloadCard(data.id)
12548                                     }
12549                                 }
12550                             },
12551                           
12552                             {
12553                                 xns : Roo.bootstrap,
12554                                 xtype : 'Button',
12555                                 
12556                                 size : 'sm',
12557                                 weight: 'danger',
12558                                 cls : 'col-1',
12559                                 fa : 'times',
12560                                 listeners : {
12561                                     click : function() {
12562                                         t.removeCard(data.id)
12563                                     }
12564                                 }
12565                             }
12566                         ]
12567                     }
12568                     
12569                 ] 
12570             }
12571             
12572         ];
12573
12574         var cn = this.addxtype(
12575             {
12576                  
12577                 xns : Roo.bootstrap,
12578                 xtype : 'Card',
12579                 closeable : true,
12580                 header : !data.mimetype.match(/image/) && !data.preview ? "Document": false,
12581                 header_image : data.mimetype.match(/image/) ? data.src  : data.preview,
12582                 header_image_fit_square: true, // fixme  - we probably need to use the 'Img' element to do stuff like this.
12583                 data : data,
12584                 html : false,
12585                  
12586                 items : footer,
12587                 initEvents : function() {
12588                     Roo.bootstrap.Card.prototype.initEvents.call(this);
12589                     this.imgEl = this.el.select('.card-img-top').first();
12590                     if (this.imgEl) {
12591                         this.imgEl.on('click', function() { t.previewCard( data.id); }, this);
12592                         this.imgEl.set({ 'pointer' : 'cursor' });
12593                                   
12594                     }
12595                     
12596                   
12597                 }
12598                 
12599             }
12600         );
12601         // dont' really need ot update items.
12602         // this.items.push(cn);
12603         this.fileCollection.add(cn);
12604         this.updateInput();
12605         
12606     },
12607     removeCard : function(id)
12608     {
12609         
12610         var card  = this.fileCollection.get(id);
12611         card.data.is_deleted = 1;
12612         card.data.src = ''; /// delete the source - so it reduces size of not uploaded images etc.
12613         this.fileCollection.remove(card);
12614         //this.items = this.items.filter(function(e) { return e != card });
12615         // dont' really need ot update items.
12616         card.el.dom.parentNode.removeChild(card.el.dom);
12617         
12618     },
12619     reset: function()
12620     {
12621         this.fileCollection.each(function(card) {
12622             card.el.dom.parentNode.removeChild(card.el.dom);    
12623         });
12624         this.fileCollection.clear();
12625         this.updateInput();
12626     },
12627     
12628     updateInput : function()
12629     {
12630         var data = [];
12631         this.fileCollection.each(function(e) {
12632             data.push(e.data);
12633         });
12634         
12635         this.inputEl().dom.value = JSON.stringify(data);
12636     }
12637     
12638     
12639 });
12640
12641
12642 Roo.bootstrap.CardUploader.ID = -1;/*
12643  * Based on:
12644  * Ext JS Library 1.1.1
12645  * Copyright(c) 2006-2007, Ext JS, LLC.
12646  *
12647  * Originally Released Under LGPL - original licence link has changed is not relivant.
12648  *
12649  * Fork - LGPL
12650  * <script type="text/javascript">
12651  */
12652
12653
12654 /**
12655  * @class Roo.data.SortTypes
12656  * @singleton
12657  * Defines the default sorting (casting?) comparison functions used when sorting data.
12658  */
12659 Roo.data.SortTypes = {
12660     /**
12661      * Default sort that does nothing
12662      * @param {Mixed} s The value being converted
12663      * @return {Mixed} The comparison value
12664      */
12665     none : function(s){
12666         return s;
12667     },
12668     
12669     /**
12670      * The regular expression used to strip tags
12671      * @type {RegExp}
12672      * @property
12673      */
12674     stripTagsRE : /<\/?[^>]+>/gi,
12675     
12676     /**
12677      * Strips all HTML tags to sort on text only
12678      * @param {Mixed} s The value being converted
12679      * @return {String} The comparison value
12680      */
12681     asText : function(s){
12682         return String(s).replace(this.stripTagsRE, "");
12683     },
12684     
12685     /**
12686      * Strips all HTML tags to sort on text only - Case insensitive
12687      * @param {Mixed} s The value being converted
12688      * @return {String} The comparison value
12689      */
12690     asUCText : function(s){
12691         return String(s).toUpperCase().replace(this.stripTagsRE, "");
12692     },
12693     
12694     /**
12695      * Case insensitive string
12696      * @param {Mixed} s The value being converted
12697      * @return {String} The comparison value
12698      */
12699     asUCString : function(s) {
12700         return String(s).toUpperCase();
12701     },
12702     
12703     /**
12704      * Date sorting
12705      * @param {Mixed} s The value being converted
12706      * @return {Number} The comparison value
12707      */
12708     asDate : function(s) {
12709         if(!s){
12710             return 0;
12711         }
12712         if(s instanceof Date){
12713             return s.getTime();
12714         }
12715         return Date.parse(String(s));
12716     },
12717     
12718     /**
12719      * Float sorting
12720      * @param {Mixed} s The value being converted
12721      * @return {Float} The comparison value
12722      */
12723     asFloat : function(s) {
12724         var val = parseFloat(String(s).replace(/,/g, ""));
12725         if(isNaN(val)) {
12726             val = 0;
12727         }
12728         return val;
12729     },
12730     
12731     /**
12732      * Integer sorting
12733      * @param {Mixed} s The value being converted
12734      * @return {Number} The comparison value
12735      */
12736     asInt : function(s) {
12737         var val = parseInt(String(s).replace(/,/g, ""));
12738         if(isNaN(val)) {
12739             val = 0;
12740         }
12741         return val;
12742     }
12743 };/*
12744  * Based on:
12745  * Ext JS Library 1.1.1
12746  * Copyright(c) 2006-2007, Ext JS, LLC.
12747  *
12748  * Originally Released Under LGPL - original licence link has changed is not relivant.
12749  *
12750  * Fork - LGPL
12751  * <script type="text/javascript">
12752  */
12753
12754 /**
12755 * @class Roo.data.Record
12756  * Instances of this class encapsulate both record <em>definition</em> information, and record
12757  * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
12758  * to access Records cached in an {@link Roo.data.Store} object.<br>
12759  * <p>
12760  * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
12761  * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
12762  * objects.<br>
12763  * <p>
12764  * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
12765  * @constructor
12766  * This constructor should not be used to create Record objects. Instead, use the constructor generated by
12767  * {@link #create}. The parameters are the same.
12768  * @param {Array} data An associative Array of data values keyed by the field name.
12769  * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
12770  * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
12771  * not specified an integer id is generated.
12772  */
12773 Roo.data.Record = function(data, id){
12774     this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
12775     this.data = data;
12776 };
12777
12778 /**
12779  * Generate a constructor for a specific record layout.
12780  * @param {Array} o An Array of field definition objects which specify field names, and optionally,
12781  * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
12782  * Each field definition object may contain the following properties: <ul>
12783  * <li><b>name</b> : String<p style="margin-left:1em">The name by which the field is referenced within the Record. This is referenced by,
12784  * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
12785  * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
12786  * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
12787  * is being used, then this is a string containing the javascript expression to reference the data relative to 
12788  * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
12789  * to the data item relative to the record element. If the mapping expression is the same as the field name,
12790  * this may be omitted.</p></li>
12791  * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
12792  * <ul><li>auto (Default, implies no conversion)</li>
12793  * <li>string</li>
12794  * <li>int</li>
12795  * <li>float</li>
12796  * <li>boolean</li>
12797  * <li>date</li></ul></p></li>
12798  * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
12799  * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
12800  * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
12801  * by the Reader into an object that will be stored in the Record. It is passed the
12802  * following parameters:<ul>
12803  * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
12804  * </ul></p></li>
12805  * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
12806  * </ul>
12807  * <br>usage:<br><pre><code>
12808 var TopicRecord = Roo.data.Record.create(
12809     {name: 'title', mapping: 'topic_title'},
12810     {name: 'author', mapping: 'username'},
12811     {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
12812     {name: 'lastPost', mapping: 'post_time', type: 'date'},
12813     {name: 'lastPoster', mapping: 'user2'},
12814     {name: 'excerpt', mapping: 'post_text'}
12815 );
12816
12817 var myNewRecord = new TopicRecord({
12818     title: 'Do my job please',
12819     author: 'noobie',
12820     totalPosts: 1,
12821     lastPost: new Date(),
12822     lastPoster: 'Animal',
12823     excerpt: 'No way dude!'
12824 });
12825 myStore.add(myNewRecord);
12826 </code></pre>
12827  * @method create
12828  * @static
12829  */
12830 Roo.data.Record.create = function(o){
12831     var f = function(){
12832         f.superclass.constructor.apply(this, arguments);
12833     };
12834     Roo.extend(f, Roo.data.Record);
12835     var p = f.prototype;
12836     p.fields = new Roo.util.MixedCollection(false, function(field){
12837         return field.name;
12838     });
12839     for(var i = 0, len = o.length; i < len; i++){
12840         p.fields.add(new Roo.data.Field(o[i]));
12841     }
12842     f.getField = function(name){
12843         return p.fields.get(name);  
12844     };
12845     return f;
12846 };
12847
12848 Roo.data.Record.AUTO_ID = 1000;
12849 Roo.data.Record.EDIT = 'edit';
12850 Roo.data.Record.REJECT = 'reject';
12851 Roo.data.Record.COMMIT = 'commit';
12852
12853 Roo.data.Record.prototype = {
12854     /**
12855      * Readonly flag - true if this record has been modified.
12856      * @type Boolean
12857      */
12858     dirty : false,
12859     editing : false,
12860     error: null,
12861     modified: null,
12862
12863     // private
12864     join : function(store){
12865         this.store = store;
12866     },
12867
12868     /**
12869      * Set the named field to the specified value.
12870      * @param {String} name The name of the field to set.
12871      * @param {Object} value The value to set the field to.
12872      */
12873     set : function(name, value){
12874         if(this.data[name] == value){
12875             return;
12876         }
12877         this.dirty = true;
12878         if(!this.modified){
12879             this.modified = {};
12880         }
12881         if(typeof this.modified[name] == 'undefined'){
12882             this.modified[name] = this.data[name];
12883         }
12884         this.data[name] = value;
12885         if(!this.editing && this.store){
12886             this.store.afterEdit(this);
12887         }       
12888     },
12889
12890     /**
12891      * Get the value of the named field.
12892      * @param {String} name The name of the field to get the value of.
12893      * @return {Object} The value of the field.
12894      */
12895     get : function(name){
12896         return this.data[name]; 
12897     },
12898
12899     // private
12900     beginEdit : function(){
12901         this.editing = true;
12902         this.modified = {}; 
12903     },
12904
12905     // private
12906     cancelEdit : function(){
12907         this.editing = false;
12908         delete this.modified;
12909     },
12910
12911     // private
12912     endEdit : function(){
12913         this.editing = false;
12914         if(this.dirty && this.store){
12915             this.store.afterEdit(this);
12916         }
12917     },
12918
12919     /**
12920      * Usually called by the {@link Roo.data.Store} which owns the Record.
12921      * Rejects all changes made to the Record since either creation, or the last commit operation.
12922      * Modified fields are reverted to their original values.
12923      * <p>
12924      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
12925      * of reject operations.
12926      */
12927     reject : function(){
12928         var m = this.modified;
12929         for(var n in m){
12930             if(typeof m[n] != "function"){
12931                 this.data[n] = m[n];
12932             }
12933         }
12934         this.dirty = false;
12935         delete this.modified;
12936         this.editing = false;
12937         if(this.store){
12938             this.store.afterReject(this);
12939         }
12940     },
12941
12942     /**
12943      * Usually called by the {@link Roo.data.Store} which owns the Record.
12944      * Commits all changes made to the Record since either creation, or the last commit operation.
12945      * <p>
12946      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
12947      * of commit operations.
12948      */
12949     commit : function(){
12950         this.dirty = false;
12951         delete this.modified;
12952         this.editing = false;
12953         if(this.store){
12954             this.store.afterCommit(this);
12955         }
12956     },
12957
12958     // private
12959     hasError : function(){
12960         return this.error != null;
12961     },
12962
12963     // private
12964     clearError : function(){
12965         this.error = null;
12966     },
12967
12968     /**
12969      * Creates a copy of this record.
12970      * @param {String} id (optional) A new record id if you don't want to use this record's id
12971      * @return {Record}
12972      */
12973     copy : function(newId) {
12974         return new this.constructor(Roo.apply({}, this.data), newId || this.id);
12975     }
12976 };/*
12977  * Based on:
12978  * Ext JS Library 1.1.1
12979  * Copyright(c) 2006-2007, Ext JS, LLC.
12980  *
12981  * Originally Released Under LGPL - original licence link has changed is not relivant.
12982  *
12983  * Fork - LGPL
12984  * <script type="text/javascript">
12985  */
12986
12987
12988
12989 /**
12990  * @class Roo.data.Store
12991  * @extends Roo.util.Observable
12992  * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
12993  * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
12994  * <p>
12995  * A Store object uses an implementation of {@link Roo.data.DataProxy} to access a data object unless you call loadData() directly and pass in your data. The Store object
12996  * has no knowledge of the format of the data returned by the Proxy.<br>
12997  * <p>
12998  * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
12999  * instances from the data object. These records are cached and made available through accessor functions.
13000  * @constructor
13001  * Creates a new Store.
13002  * @param {Object} config A config object containing the objects needed for the Store to access data,
13003  * and read the data into Records.
13004  */
13005 Roo.data.Store = function(config){
13006     this.data = new Roo.util.MixedCollection(false);
13007     this.data.getKey = function(o){
13008         return o.id;
13009     };
13010     this.baseParams = {};
13011     // private
13012     this.paramNames = {
13013         "start" : "start",
13014         "limit" : "limit",
13015         "sort" : "sort",
13016         "dir" : "dir",
13017         "multisort" : "_multisort"
13018     };
13019
13020     if(config && config.data){
13021         this.inlineData = config.data;
13022         delete config.data;
13023     }
13024
13025     Roo.apply(this, config);
13026     
13027     if(this.reader){ // reader passed
13028         this.reader = Roo.factory(this.reader, Roo.data);
13029         this.reader.xmodule = this.xmodule || false;
13030         if(!this.recordType){
13031             this.recordType = this.reader.recordType;
13032         }
13033         if(this.reader.onMetaChange){
13034             this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
13035         }
13036     }
13037
13038     if(this.recordType){
13039         this.fields = this.recordType.prototype.fields;
13040     }
13041     this.modified = [];
13042
13043     this.addEvents({
13044         /**
13045          * @event datachanged
13046          * Fires when the data cache has changed, and a widget which is using this Store
13047          * as a Record cache should refresh its view.
13048          * @param {Store} this
13049          */
13050         datachanged : true,
13051         /**
13052          * @event metachange
13053          * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
13054          * @param {Store} this
13055          * @param {Object} meta The JSON metadata
13056          */
13057         metachange : true,
13058         /**
13059          * @event add
13060          * Fires when Records have been added to the Store
13061          * @param {Store} this
13062          * @param {Roo.data.Record[]} records The array of Records added
13063          * @param {Number} index The index at which the record(s) were added
13064          */
13065         add : true,
13066         /**
13067          * @event remove
13068          * Fires when a Record has been removed from the Store
13069          * @param {Store} this
13070          * @param {Roo.data.Record} record The Record that was removed
13071          * @param {Number} index The index at which the record was removed
13072          */
13073         remove : true,
13074         /**
13075          * @event update
13076          * Fires when a Record has been updated
13077          * @param {Store} this
13078          * @param {Roo.data.Record} record The Record that was updated
13079          * @param {String} operation The update operation being performed.  Value may be one of:
13080          * <pre><code>
13081  Roo.data.Record.EDIT
13082  Roo.data.Record.REJECT
13083  Roo.data.Record.COMMIT
13084          * </code></pre>
13085          */
13086         update : true,
13087         /**
13088          * @event clear
13089          * Fires when the data cache has been cleared.
13090          * @param {Store} this
13091          */
13092         clear : true,
13093         /**
13094          * @event beforeload
13095          * Fires before a request is made for a new data object.  If the beforeload handler returns false
13096          * the load action will be canceled.
13097          * @param {Store} this
13098          * @param {Object} options The loading options that were specified (see {@link #load} for details)
13099          */
13100         beforeload : true,
13101         /**
13102          * @event beforeloadadd
13103          * Fires after a new set of Records has been loaded.
13104          * @param {Store} this
13105          * @param {Roo.data.Record[]} records The Records that were loaded
13106          * @param {Object} options The loading options that were specified (see {@link #load} for details)
13107          */
13108         beforeloadadd : true,
13109         /**
13110          * @event load
13111          * Fires after a new set of Records has been loaded, before they are added to the store.
13112          * @param {Store} this
13113          * @param {Roo.data.Record[]} records The Records that were loaded
13114          * @param {Object} options The loading options that were specified (see {@link #load} for details)
13115          * @params {Object} return from reader
13116          */
13117         load : true,
13118         /**
13119          * @event loadexception
13120          * Fires if an exception occurs in the Proxy during loading.
13121          * Called with the signature of the Proxy's "loadexception" event.
13122          * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
13123          * 
13124          * @param {Proxy} 
13125          * @param {Object} return from JsonData.reader() - success, totalRecords, records
13126          * @param {Object} load options 
13127          * @param {Object} jsonData from your request (normally this contains the Exception)
13128          */
13129         loadexception : true
13130     });
13131     
13132     if(this.proxy){
13133         this.proxy = Roo.factory(this.proxy, Roo.data);
13134         this.proxy.xmodule = this.xmodule || false;
13135         this.relayEvents(this.proxy,  ["loadexception"]);
13136     }
13137     this.sortToggle = {};
13138     this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
13139
13140     Roo.data.Store.superclass.constructor.call(this);
13141
13142     if(this.inlineData){
13143         this.loadData(this.inlineData);
13144         delete this.inlineData;
13145     }
13146 };
13147
13148 Roo.extend(Roo.data.Store, Roo.util.Observable, {
13149      /**
13150     * @cfg {boolean} isLocal   flag if data is locally available (and can be always looked up
13151     * without a remote query - used by combo/forms at present.
13152     */
13153     
13154     /**
13155     * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
13156     */
13157     /**
13158     * @cfg {Array} data Inline data to be loaded when the store is initialized.
13159     */
13160     /**
13161     * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
13162     * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
13163     */
13164     /**
13165     * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
13166     * on any HTTP request
13167     */
13168     /**
13169     * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
13170     */
13171     /**
13172     * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
13173     */
13174     multiSort: false,
13175     /**
13176     * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
13177     * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
13178     */
13179     remoteSort : false,
13180
13181     /**
13182     * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
13183      * loaded or when a record is removed. (defaults to false).
13184     */
13185     pruneModifiedRecords : false,
13186
13187     // private
13188     lastOptions : null,
13189
13190     /**
13191      * Add Records to the Store and fires the add event.
13192      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
13193      */
13194     add : function(records){
13195         records = [].concat(records);
13196         for(var i = 0, len = records.length; i < len; i++){
13197             records[i].join(this);
13198         }
13199         var index = this.data.length;
13200         this.data.addAll(records);
13201         this.fireEvent("add", this, records, index);
13202     },
13203
13204     /**
13205      * Remove a Record from the Store and fires the remove event.
13206      * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
13207      */
13208     remove : function(record){
13209         var index = this.data.indexOf(record);
13210         this.data.removeAt(index);
13211  
13212         if(this.pruneModifiedRecords){
13213             this.modified.remove(record);
13214         }
13215         this.fireEvent("remove", this, record, index);
13216     },
13217
13218     /**
13219      * Remove all Records from the Store and fires the clear event.
13220      */
13221     removeAll : function(){
13222         this.data.clear();
13223         if(this.pruneModifiedRecords){
13224             this.modified = [];
13225         }
13226         this.fireEvent("clear", this);
13227     },
13228
13229     /**
13230      * Inserts Records to the Store at the given index and fires the add event.
13231      * @param {Number} index The start index at which to insert the passed Records.
13232      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
13233      */
13234     insert : function(index, records){
13235         records = [].concat(records);
13236         for(var i = 0, len = records.length; i < len; i++){
13237             this.data.insert(index, records[i]);
13238             records[i].join(this);
13239         }
13240         this.fireEvent("add", this, records, index);
13241     },
13242
13243     /**
13244      * Get the index within the cache of the passed Record.
13245      * @param {Roo.data.Record} record The Roo.data.Record object to to find.
13246      * @return {Number} The index of the passed Record. Returns -1 if not found.
13247      */
13248     indexOf : function(record){
13249         return this.data.indexOf(record);
13250     },
13251
13252     /**
13253      * Get the index within the cache of the Record with the passed id.
13254      * @param {String} id The id of the Record to find.
13255      * @return {Number} The index of the Record. Returns -1 if not found.
13256      */
13257     indexOfId : function(id){
13258         return this.data.indexOfKey(id);
13259     },
13260
13261     /**
13262      * Get the Record with the specified id.
13263      * @param {String} id The id of the Record to find.
13264      * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
13265      */
13266     getById : function(id){
13267         return this.data.key(id);
13268     },
13269
13270     /**
13271      * Get the Record at the specified index.
13272      * @param {Number} index The index of the Record to find.
13273      * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
13274      */
13275     getAt : function(index){
13276         return this.data.itemAt(index);
13277     },
13278
13279     /**
13280      * Returns a range of Records between specified indices.
13281      * @param {Number} startIndex (optional) The starting index (defaults to 0)
13282      * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
13283      * @return {Roo.data.Record[]} An array of Records
13284      */
13285     getRange : function(start, end){
13286         return this.data.getRange(start, end);
13287     },
13288
13289     // private
13290     storeOptions : function(o){
13291         o = Roo.apply({}, o);
13292         delete o.callback;
13293         delete o.scope;
13294         this.lastOptions = o;
13295     },
13296
13297     /**
13298      * Loads the Record cache from the configured Proxy using the configured Reader.
13299      * <p>
13300      * If using remote paging, then the first load call must specify the <em>start</em>
13301      * and <em>limit</em> properties in the options.params property to establish the initial
13302      * position within the dataset, and the number of Records to cache on each read from the Proxy.
13303      * <p>
13304      * <strong>It is important to note that for remote data sources, loading is asynchronous,
13305      * and this call will return before the new data has been loaded. Perform any post-processing
13306      * in a callback function, or in a "load" event handler.</strong>
13307      * <p>
13308      * @param {Object} options An object containing properties which control loading options:<ul>
13309      * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
13310      * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
13311      * passed the following arguments:<ul>
13312      * <li>r : Roo.data.Record[]</li>
13313      * <li>options: Options object from the load call</li>
13314      * <li>success: Boolean success indicator</li></ul></li>
13315      * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
13316      * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
13317      * </ul>
13318      */
13319     load : function(options){
13320         options = options || {};
13321         if(this.fireEvent("beforeload", this, options) !== false){
13322             this.storeOptions(options);
13323             var p = Roo.apply(options.params || {}, this.baseParams);
13324             // if meta was not loaded from remote source.. try requesting it.
13325             if (!this.reader.metaFromRemote) {
13326                 p._requestMeta = 1;
13327             }
13328             if(this.sortInfo && this.remoteSort){
13329                 var pn = this.paramNames;
13330                 p[pn["sort"]] = this.sortInfo.field;
13331                 p[pn["dir"]] = this.sortInfo.direction;
13332             }
13333             if (this.multiSort) {
13334                 var pn = this.paramNames;
13335                 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
13336             }
13337             
13338             this.proxy.load(p, this.reader, this.loadRecords, this, options);
13339         }
13340     },
13341
13342     /**
13343      * Reloads the Record cache from the configured Proxy using the configured Reader and
13344      * the options from the last load operation performed.
13345      * @param {Object} options (optional) An object containing properties which may override the options
13346      * used in the last load operation. See {@link #load} for details (defaults to null, in which case
13347      * the most recently used options are reused).
13348      */
13349     reload : function(options){
13350         this.load(Roo.applyIf(options||{}, this.lastOptions));
13351     },
13352
13353     // private
13354     // Called as a callback by the Reader during a load operation.
13355     loadRecords : function(o, options, success){
13356         if(!o || success === false){
13357             if(success !== false){
13358                 this.fireEvent("load", this, [], options, o);
13359             }
13360             if(options.callback){
13361                 options.callback.call(options.scope || this, [], options, false);
13362             }
13363             return;
13364         }
13365         // if data returned failure - throw an exception.
13366         if (o.success === false) {
13367             // show a message if no listener is registered.
13368             if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
13369                     Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
13370             }
13371             // loadmask wil be hooked into this..
13372             this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
13373             return;
13374         }
13375         var r = o.records, t = o.totalRecords || r.length;
13376         
13377         this.fireEvent("beforeloadadd", this, r, options, o);
13378         
13379         if(!options || options.add !== true){
13380             if(this.pruneModifiedRecords){
13381                 this.modified = [];
13382             }
13383             for(var i = 0, len = r.length; i < len; i++){
13384                 r[i].join(this);
13385             }
13386             if(this.snapshot){
13387                 this.data = this.snapshot;
13388                 delete this.snapshot;
13389             }
13390             this.data.clear();
13391             this.data.addAll(r);
13392             this.totalLength = t;
13393             this.applySort();
13394             this.fireEvent("datachanged", this);
13395         }else{
13396             this.totalLength = Math.max(t, this.data.length+r.length);
13397             this.add(r);
13398         }
13399         
13400         if(this.parent && !Roo.isIOS && !this.useNativeIOS && this.parent.emptyTitle.length) {
13401                 
13402             var e = new Roo.data.Record({});
13403
13404             e.set(this.parent.displayField, this.parent.emptyTitle);
13405             e.set(this.parent.valueField, '');
13406
13407             this.insert(0, e);
13408         }
13409             
13410         this.fireEvent("load", this, r, options, o);
13411         if(options.callback){
13412             options.callback.call(options.scope || this, r, options, true);
13413         }
13414     },
13415
13416
13417     /**
13418      * Loads data from a passed data block. A Reader which understands the format of the data
13419      * must have been configured in the constructor.
13420      * @param {Object} data The data block from which to read the Records.  The format of the data expected
13421      * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
13422      * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
13423      */
13424     loadData : function(o, append){
13425         var r = this.reader.readRecords(o);
13426         this.loadRecords(r, {add: append}, true);
13427     },
13428     
13429      /**
13430      * using 'cn' the nested child reader read the child array into it's child stores.
13431      * @param {Object} rec The record with a 'children array
13432      */
13433     loadDataFromChildren : function(rec)
13434     {
13435         this.loadData(this.reader.toLoadData(rec));
13436     },
13437     
13438
13439     /**
13440      * Gets the number of cached records.
13441      * <p>
13442      * <em>If using paging, this may not be the total size of the dataset. If the data object
13443      * used by the Reader contains the dataset size, then the getTotalCount() function returns
13444      * the data set size</em>
13445      */
13446     getCount : function(){
13447         return this.data.length || 0;
13448     },
13449
13450     /**
13451      * Gets the total number of records in the dataset as returned by the server.
13452      * <p>
13453      * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
13454      * the dataset size</em>
13455      */
13456     getTotalCount : function(){
13457         return this.totalLength || 0;
13458     },
13459
13460     /**
13461      * Returns the sort state of the Store as an object with two properties:
13462      * <pre><code>
13463  field {String} The name of the field by which the Records are sorted
13464  direction {String} The sort order, "ASC" or "DESC"
13465      * </code></pre>
13466      */
13467     getSortState : function(){
13468         return this.sortInfo;
13469     },
13470
13471     // private
13472     applySort : function(){
13473         if(this.sortInfo && !this.remoteSort){
13474             var s = this.sortInfo, f = s.field;
13475             var st = this.fields.get(f).sortType;
13476             var fn = function(r1, r2){
13477                 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
13478                 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
13479             };
13480             this.data.sort(s.direction, fn);
13481             if(this.snapshot && this.snapshot != this.data){
13482                 this.snapshot.sort(s.direction, fn);
13483             }
13484         }
13485     },
13486
13487     /**
13488      * Sets the default sort column and order to be used by the next load operation.
13489      * @param {String} fieldName The name of the field to sort by.
13490      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
13491      */
13492     setDefaultSort : function(field, dir){
13493         this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
13494     },
13495
13496     /**
13497      * Sort the Records.
13498      * If remote sorting is used, the sort is performed on the server, and the cache is
13499      * reloaded. If local sorting is used, the cache is sorted internally.
13500      * @param {String} fieldName The name of the field to sort by.
13501      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
13502      */
13503     sort : function(fieldName, dir){
13504         var f = this.fields.get(fieldName);
13505         if(!dir){
13506             this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
13507             
13508             if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
13509                 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
13510             }else{
13511                 dir = f.sortDir;
13512             }
13513         }
13514         this.sortToggle[f.name] = dir;
13515         this.sortInfo = {field: f.name, direction: dir};
13516         if(!this.remoteSort){
13517             this.applySort();
13518             this.fireEvent("datachanged", this);
13519         }else{
13520             this.load(this.lastOptions);
13521         }
13522     },
13523
13524     /**
13525      * Calls the specified function for each of the Records in the cache.
13526      * @param {Function} fn The function to call. The Record is passed as the first parameter.
13527      * Returning <em>false</em> aborts and exits the iteration.
13528      * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
13529      */
13530     each : function(fn, scope){
13531         this.data.each(fn, scope);
13532     },
13533
13534     /**
13535      * Gets all records modified since the last commit.  Modified records are persisted across load operations
13536      * (e.g., during paging).
13537      * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
13538      */
13539     getModifiedRecords : function(){
13540         return this.modified;
13541     },
13542
13543     // private
13544     createFilterFn : function(property, value, anyMatch){
13545         if(!value.exec){ // not a regex
13546             value = String(value);
13547             if(value.length == 0){
13548                 return false;
13549             }
13550             value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
13551         }
13552         return function(r){
13553             return value.test(r.data[property]);
13554         };
13555     },
13556
13557     /**
13558      * Sums the value of <i>property</i> for each record between start and end and returns the result.
13559      * @param {String} property A field on your records
13560      * @param {Number} start The record index to start at (defaults to 0)
13561      * @param {Number} end The last record index to include (defaults to length - 1)
13562      * @return {Number} The sum
13563      */
13564     sum : function(property, start, end){
13565         var rs = this.data.items, v = 0;
13566         start = start || 0;
13567         end = (end || end === 0) ? end : rs.length-1;
13568
13569         for(var i = start; i <= end; i++){
13570             v += (rs[i].data[property] || 0);
13571         }
13572         return v;
13573     },
13574
13575     /**
13576      * Filter the records by a specified property.
13577      * @param {String} field A field on your records
13578      * @param {String/RegExp} value Either a string that the field
13579      * should start with or a RegExp to test against the field
13580      * @param {Boolean} anyMatch True to match any part not just the beginning
13581      */
13582     filter : function(property, value, anyMatch){
13583         var fn = this.createFilterFn(property, value, anyMatch);
13584         return fn ? this.filterBy(fn) : this.clearFilter();
13585     },
13586
13587     /**
13588      * Filter by a function. The specified function will be called with each
13589      * record in this data source. If the function returns true the record is included,
13590      * otherwise it is filtered.
13591      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
13592      * @param {Object} scope (optional) The scope of the function (defaults to this)
13593      */
13594     filterBy : function(fn, scope){
13595         this.snapshot = this.snapshot || this.data;
13596         this.data = this.queryBy(fn, scope||this);
13597         this.fireEvent("datachanged", this);
13598     },
13599
13600     /**
13601      * Query the records by a specified property.
13602      * @param {String} field A field on your records
13603      * @param {String/RegExp} value Either a string that the field
13604      * should start with or a RegExp to test against the field
13605      * @param {Boolean} anyMatch True to match any part not just the beginning
13606      * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
13607      */
13608     query : function(property, value, anyMatch){
13609         var fn = this.createFilterFn(property, value, anyMatch);
13610         return fn ? this.queryBy(fn) : this.data.clone();
13611     },
13612
13613     /**
13614      * Query by a function. The specified function will be called with each
13615      * record in this data source. If the function returns true the record is included
13616      * in the results.
13617      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
13618      * @param {Object} scope (optional) The scope of the function (defaults to this)
13619       @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
13620      **/
13621     queryBy : function(fn, scope){
13622         var data = this.snapshot || this.data;
13623         return data.filterBy(fn, scope||this);
13624     },
13625
13626     /**
13627      * Collects unique values for a particular dataIndex from this store.
13628      * @param {String} dataIndex The property to collect
13629      * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
13630      * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
13631      * @return {Array} An array of the unique values
13632      **/
13633     collect : function(dataIndex, allowNull, bypassFilter){
13634         var d = (bypassFilter === true && this.snapshot) ?
13635                 this.snapshot.items : this.data.items;
13636         var v, sv, r = [], l = {};
13637         for(var i = 0, len = d.length; i < len; i++){
13638             v = d[i].data[dataIndex];
13639             sv = String(v);
13640             if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
13641                 l[sv] = true;
13642                 r[r.length] = v;
13643             }
13644         }
13645         return r;
13646     },
13647
13648     /**
13649      * Revert to a view of the Record cache with no filtering applied.
13650      * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
13651      */
13652     clearFilter : function(suppressEvent){
13653         if(this.snapshot && this.snapshot != this.data){
13654             this.data = this.snapshot;
13655             delete this.snapshot;
13656             if(suppressEvent !== true){
13657                 this.fireEvent("datachanged", this);
13658             }
13659         }
13660     },
13661
13662     // private
13663     afterEdit : function(record){
13664         if(this.modified.indexOf(record) == -1){
13665             this.modified.push(record);
13666         }
13667         this.fireEvent("update", this, record, Roo.data.Record.EDIT);
13668     },
13669     
13670     // private
13671     afterReject : function(record){
13672         this.modified.remove(record);
13673         this.fireEvent("update", this, record, Roo.data.Record.REJECT);
13674     },
13675
13676     // private
13677     afterCommit : function(record){
13678         this.modified.remove(record);
13679         this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
13680     },
13681
13682     /**
13683      * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
13684      * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
13685      */
13686     commitChanges : function(){
13687         var m = this.modified.slice(0);
13688         this.modified = [];
13689         for(var i = 0, len = m.length; i < len; i++){
13690             m[i].commit();
13691         }
13692     },
13693
13694     /**
13695      * Cancel outstanding changes on all changed records.
13696      */
13697     rejectChanges : function(){
13698         var m = this.modified.slice(0);
13699         this.modified = [];
13700         for(var i = 0, len = m.length; i < len; i++){
13701             m[i].reject();
13702         }
13703     },
13704
13705     onMetaChange : function(meta, rtype, o){
13706         this.recordType = rtype;
13707         this.fields = rtype.prototype.fields;
13708         delete this.snapshot;
13709         this.sortInfo = meta.sortInfo || this.sortInfo;
13710         this.modified = [];
13711         this.fireEvent('metachange', this, this.reader.meta);
13712     },
13713     
13714     moveIndex : function(data, type)
13715     {
13716         var index = this.indexOf(data);
13717         
13718         var newIndex = index + type;
13719         
13720         this.remove(data);
13721         
13722         this.insert(newIndex, data);
13723         
13724     }
13725 });/*
13726  * Based on:
13727  * Ext JS Library 1.1.1
13728  * Copyright(c) 2006-2007, Ext JS, LLC.
13729  *
13730  * Originally Released Under LGPL - original licence link has changed is not relivant.
13731  *
13732  * Fork - LGPL
13733  * <script type="text/javascript">
13734  */
13735
13736 /**
13737  * @class Roo.data.SimpleStore
13738  * @extends Roo.data.Store
13739  * Small helper class to make creating Stores from Array data easier.
13740  * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
13741  * @cfg {Array} fields An array of field definition objects, or field name strings.
13742  * @cfg {Object} an existing reader (eg. copied from another store)
13743  * @cfg {Array} data The multi-dimensional array of data
13744  * @constructor
13745  * @param {Object} config
13746  */
13747 Roo.data.SimpleStore = function(config)
13748 {
13749     Roo.data.SimpleStore.superclass.constructor.call(this, {
13750         isLocal : true,
13751         reader: typeof(config.reader) != 'undefined' ? config.reader : new Roo.data.ArrayReader({
13752                 id: config.id
13753             },
13754             Roo.data.Record.create(config.fields)
13755         ),
13756         proxy : new Roo.data.MemoryProxy(config.data)
13757     });
13758     this.load();
13759 };
13760 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
13761  * Based on:
13762  * Ext JS Library 1.1.1
13763  * Copyright(c) 2006-2007, Ext JS, LLC.
13764  *
13765  * Originally Released Under LGPL - original licence link has changed is not relivant.
13766  *
13767  * Fork - LGPL
13768  * <script type="text/javascript">
13769  */
13770
13771 /**
13772 /**
13773  * @extends Roo.data.Store
13774  * @class Roo.data.JsonStore
13775  * Small helper class to make creating Stores for JSON data easier. <br/>
13776 <pre><code>
13777 var store = new Roo.data.JsonStore({
13778     url: 'get-images.php',
13779     root: 'images',
13780     fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
13781 });
13782 </code></pre>
13783  * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
13784  * JsonReader and HttpProxy (unless inline data is provided).</b>
13785  * @cfg {Array} fields An array of field definition objects, or field name strings.
13786  * @constructor
13787  * @param {Object} config
13788  */
13789 Roo.data.JsonStore = function(c){
13790     Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
13791         proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
13792         reader: new Roo.data.JsonReader(c, c.fields)
13793     }));
13794 };
13795 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
13796  * Based on:
13797  * Ext JS Library 1.1.1
13798  * Copyright(c) 2006-2007, Ext JS, LLC.
13799  *
13800  * Originally Released Under LGPL - original licence link has changed is not relivant.
13801  *
13802  * Fork - LGPL
13803  * <script type="text/javascript">
13804  */
13805
13806  
13807 Roo.data.Field = function(config){
13808     if(typeof config == "string"){
13809         config = {name: config};
13810     }
13811     Roo.apply(this, config);
13812     
13813     if(!this.type){
13814         this.type = "auto";
13815     }
13816     
13817     var st = Roo.data.SortTypes;
13818     // named sortTypes are supported, here we look them up
13819     if(typeof this.sortType == "string"){
13820         this.sortType = st[this.sortType];
13821     }
13822     
13823     // set default sortType for strings and dates
13824     if(!this.sortType){
13825         switch(this.type){
13826             case "string":
13827                 this.sortType = st.asUCString;
13828                 break;
13829             case "date":
13830                 this.sortType = st.asDate;
13831                 break;
13832             default:
13833                 this.sortType = st.none;
13834         }
13835     }
13836
13837     // define once
13838     var stripRe = /[\$,%]/g;
13839
13840     // prebuilt conversion function for this field, instead of
13841     // switching every time we're reading a value
13842     if(!this.convert){
13843         var cv, dateFormat = this.dateFormat;
13844         switch(this.type){
13845             case "":
13846             case "auto":
13847             case undefined:
13848                 cv = function(v){ return v; };
13849                 break;
13850             case "string":
13851                 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
13852                 break;
13853             case "int":
13854                 cv = function(v){
13855                     return v !== undefined && v !== null && v !== '' ?
13856                            parseInt(String(v).replace(stripRe, ""), 10) : '';
13857                     };
13858                 break;
13859             case "float":
13860                 cv = function(v){
13861                     return v !== undefined && v !== null && v !== '' ?
13862                            parseFloat(String(v).replace(stripRe, ""), 10) : ''; 
13863                     };
13864                 break;
13865             case "bool":
13866             case "boolean":
13867                 cv = function(v){ return v === true || v === "true" || v == 1; };
13868                 break;
13869             case "date":
13870                 cv = function(v){
13871                     if(!v){
13872                         return '';
13873                     }
13874                     if(v instanceof Date){
13875                         return v;
13876                     }
13877                     if(dateFormat){
13878                         if(dateFormat == "timestamp"){
13879                             return new Date(v*1000);
13880                         }
13881                         return Date.parseDate(v, dateFormat);
13882                     }
13883                     var parsed = Date.parse(v);
13884                     return parsed ? new Date(parsed) : null;
13885                 };
13886              break;
13887             
13888         }
13889         this.convert = cv;
13890     }
13891 };
13892
13893 Roo.data.Field.prototype = {
13894     dateFormat: null,
13895     defaultValue: "",
13896     mapping: null,
13897     sortType : null,
13898     sortDir : "ASC"
13899 };/*
13900  * Based on:
13901  * Ext JS Library 1.1.1
13902  * Copyright(c) 2006-2007, Ext JS, LLC.
13903  *
13904  * Originally Released Under LGPL - original licence link has changed is not relivant.
13905  *
13906  * Fork - LGPL
13907  * <script type="text/javascript">
13908  */
13909  
13910 // Base class for reading structured data from a data source.  This class is intended to be
13911 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
13912
13913 /**
13914  * @class Roo.data.DataReader
13915  * Base class for reading structured data from a data source.  This class is intended to be
13916  * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
13917  */
13918
13919 Roo.data.DataReader = function(meta, recordType){
13920     
13921     this.meta = meta;
13922     
13923     this.recordType = recordType instanceof Array ? 
13924         Roo.data.Record.create(recordType) : recordType;
13925 };
13926
13927 Roo.data.DataReader.prototype = {
13928     
13929     
13930     readerType : 'Data',
13931      /**
13932      * Create an empty record
13933      * @param {Object} data (optional) - overlay some values
13934      * @return {Roo.data.Record} record created.
13935      */
13936     newRow :  function(d) {
13937         var da =  {};
13938         this.recordType.prototype.fields.each(function(c) {
13939             switch( c.type) {
13940                 case 'int' : da[c.name] = 0; break;
13941                 case 'date' : da[c.name] = new Date(); break;
13942                 case 'float' : da[c.name] = 0.0; break;
13943                 case 'boolean' : da[c.name] = false; break;
13944                 default : da[c.name] = ""; break;
13945             }
13946             
13947         });
13948         return new this.recordType(Roo.apply(da, d));
13949     }
13950     
13951     
13952 };/*
13953  * Based on:
13954  * Ext JS Library 1.1.1
13955  * Copyright(c) 2006-2007, Ext JS, LLC.
13956  *
13957  * Originally Released Under LGPL - original licence link has changed is not relivant.
13958  *
13959  * Fork - LGPL
13960  * <script type="text/javascript">
13961  */
13962
13963 /**
13964  * @class Roo.data.DataProxy
13965  * @extends Roo.data.Observable
13966  * This class is an abstract base class for implementations which provide retrieval of
13967  * unformatted data objects.<br>
13968  * <p>
13969  * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
13970  * (of the appropriate type which knows how to parse the data object) to provide a block of
13971  * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
13972  * <p>
13973  * Custom implementations must implement the load method as described in
13974  * {@link Roo.data.HttpProxy#load}.
13975  */
13976 Roo.data.DataProxy = function(){
13977     this.addEvents({
13978         /**
13979          * @event beforeload
13980          * Fires before a network request is made to retrieve a data object.
13981          * @param {Object} This DataProxy object.
13982          * @param {Object} params The params parameter to the load function.
13983          */
13984         beforeload : true,
13985         /**
13986          * @event load
13987          * Fires before the load method's callback is called.
13988          * @param {Object} This DataProxy object.
13989          * @param {Object} o The data object.
13990          * @param {Object} arg The callback argument object passed to the load function.
13991          */
13992         load : true,
13993         /**
13994          * @event loadexception
13995          * Fires if an Exception occurs during data retrieval.
13996          * @param {Object} This DataProxy object.
13997          * @param {Object} o The data object.
13998          * @param {Object} arg The callback argument object passed to the load function.
13999          * @param {Object} e The Exception.
14000          */
14001         loadexception : true
14002     });
14003     Roo.data.DataProxy.superclass.constructor.call(this);
14004 };
14005
14006 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
14007
14008     /**
14009      * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
14010      */
14011 /*
14012  * Based on:
14013  * Ext JS Library 1.1.1
14014  * Copyright(c) 2006-2007, Ext JS, LLC.
14015  *
14016  * Originally Released Under LGPL - original licence link has changed is not relivant.
14017  *
14018  * Fork - LGPL
14019  * <script type="text/javascript">
14020  */
14021 /**
14022  * @class Roo.data.MemoryProxy
14023  * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
14024  * to the Reader when its load method is called.
14025  * @constructor
14026  * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
14027  */
14028 Roo.data.MemoryProxy = function(data){
14029     if (data.data) {
14030         data = data.data;
14031     }
14032     Roo.data.MemoryProxy.superclass.constructor.call(this);
14033     this.data = data;
14034 };
14035
14036 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
14037     
14038     /**
14039      * Load data from the requested source (in this case an in-memory
14040      * data object passed to the constructor), read the data object into
14041      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
14042      * process that block using the passed callback.
14043      * @param {Object} params This parameter is not used by the MemoryProxy class.
14044      * @param {Roo.data.DataReader} reader The Reader object which converts the data
14045      * object into a block of Roo.data.Records.
14046      * @param {Function} callback The function into which to pass the block of Roo.data.records.
14047      * The function must be passed <ul>
14048      * <li>The Record block object</li>
14049      * <li>The "arg" argument from the load function</li>
14050      * <li>A boolean success indicator</li>
14051      * </ul>
14052      * @param {Object} scope The scope in which to call the callback
14053      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
14054      */
14055     load : function(params, reader, callback, scope, arg){
14056         params = params || {};
14057         var result;
14058         try {
14059             result = reader.readRecords(params.data ? params.data :this.data);
14060         }catch(e){
14061             this.fireEvent("loadexception", this, arg, null, e);
14062             callback.call(scope, null, arg, false);
14063             return;
14064         }
14065         callback.call(scope, result, arg, true);
14066     },
14067     
14068     // private
14069     update : function(params, records){
14070         
14071     }
14072 });/*
14073  * Based on:
14074  * Ext JS Library 1.1.1
14075  * Copyright(c) 2006-2007, Ext JS, LLC.
14076  *
14077  * Originally Released Under LGPL - original licence link has changed is not relivant.
14078  *
14079  * Fork - LGPL
14080  * <script type="text/javascript">
14081  */
14082 /**
14083  * @class Roo.data.HttpProxy
14084  * @extends Roo.data.DataProxy
14085  * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
14086  * configured to reference a certain URL.<br><br>
14087  * <p>
14088  * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
14089  * from which the running page was served.<br><br>
14090  * <p>
14091  * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
14092  * <p>
14093  * Be aware that to enable the browser to parse an XML document, the server must set
14094  * the Content-Type header in the HTTP response to "text/xml".
14095  * @constructor
14096  * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
14097  * an {@link Roo.data.Connection} object.  If a Connection config is passed, the singleton {@link Roo.Ajax} object
14098  * will be used to make the request.
14099  */
14100 Roo.data.HttpProxy = function(conn){
14101     Roo.data.HttpProxy.superclass.constructor.call(this);
14102     // is conn a conn config or a real conn?
14103     this.conn = conn;
14104     this.useAjax = !conn || !conn.events;
14105   
14106 };
14107
14108 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
14109     // thse are take from connection...
14110     
14111     /**
14112      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
14113      */
14114     /**
14115      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
14116      * extra parameters to each request made by this object. (defaults to undefined)
14117      */
14118     /**
14119      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
14120      *  to each request made by this object. (defaults to undefined)
14121      */
14122     /**
14123      * @cfg {String} method (Optional) The default HTTP method to be used for requests. (defaults to undefined; if not set but parms are present will use POST, otherwise GET)
14124      */
14125     /**
14126      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
14127      */
14128      /**
14129      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
14130      * @type Boolean
14131      */
14132   
14133
14134     /**
14135      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
14136      * @type Boolean
14137      */
14138     /**
14139      * Return the {@link Roo.data.Connection} object being used by this Proxy.
14140      * @return {Connection} The Connection object. This object may be used to subscribe to events on
14141      * a finer-grained basis than the DataProxy events.
14142      */
14143     getConnection : function(){
14144         return this.useAjax ? Roo.Ajax : this.conn;
14145     },
14146
14147     /**
14148      * Load data from the configured {@link Roo.data.Connection}, read the data object into
14149      * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
14150      * process that block using the passed callback.
14151      * @param {Object} params An object containing properties which are to be used as HTTP parameters
14152      * for the request to the remote server.
14153      * @param {Roo.data.DataReader} reader The Reader object which converts the data
14154      * object into a block of Roo.data.Records.
14155      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
14156      * The function must be passed <ul>
14157      * <li>The Record block object</li>
14158      * <li>The "arg" argument from the load function</li>
14159      * <li>A boolean success indicator</li>
14160      * </ul>
14161      * @param {Object} scope The scope in which to call the callback
14162      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
14163      */
14164     load : function(params, reader, callback, scope, arg){
14165         if(this.fireEvent("beforeload", this, params) !== false){
14166             var  o = {
14167                 params : params || {},
14168                 request: {
14169                     callback : callback,
14170                     scope : scope,
14171                     arg : arg
14172                 },
14173                 reader: reader,
14174                 callback : this.loadResponse,
14175                 scope: this
14176             };
14177             if(this.useAjax){
14178                 Roo.applyIf(o, this.conn);
14179                 if(this.activeRequest){
14180                     Roo.Ajax.abort(this.activeRequest);
14181                 }
14182                 this.activeRequest = Roo.Ajax.request(o);
14183             }else{
14184                 this.conn.request(o);
14185             }
14186         }else{
14187             callback.call(scope||this, null, arg, false);
14188         }
14189     },
14190
14191     // private
14192     loadResponse : function(o, success, response){
14193         delete this.activeRequest;
14194         if(!success){
14195             this.fireEvent("loadexception", this, o, response);
14196             o.request.callback.call(o.request.scope, null, o.request.arg, false);
14197             return;
14198         }
14199         var result;
14200         try {
14201             result = o.reader.read(response);
14202         }catch(e){
14203             this.fireEvent("loadexception", this, o, response, e);
14204             o.request.callback.call(o.request.scope, null, o.request.arg, false);
14205             return;
14206         }
14207         
14208         this.fireEvent("load", this, o, o.request.arg);
14209         o.request.callback.call(o.request.scope, result, o.request.arg, true);
14210     },
14211
14212     // private
14213     update : function(dataSet){
14214
14215     },
14216
14217     // private
14218     updateResponse : function(dataSet){
14219
14220     }
14221 });/*
14222  * Based on:
14223  * Ext JS Library 1.1.1
14224  * Copyright(c) 2006-2007, Ext JS, LLC.
14225  *
14226  * Originally Released Under LGPL - original licence link has changed is not relivant.
14227  *
14228  * Fork - LGPL
14229  * <script type="text/javascript">
14230  */
14231
14232 /**
14233  * @class Roo.data.ScriptTagProxy
14234  * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
14235  * other than the originating domain of the running page.<br><br>
14236  * <p>
14237  * <em>Note that if you are retrieving data from a page that is in a domain that is NOT the same as the originating domain
14238  * of the running page, you must use this class, rather than DataProxy.</em><br><br>
14239  * <p>
14240  * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
14241  * source code that is used as the source inside a &lt;script> tag.<br><br>
14242  * <p>
14243  * In order for the browser to process the returned data, the server must wrap the data object
14244  * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
14245  * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
14246  * depending on whether the callback name was passed:
14247  * <p>
14248  * <pre><code>
14249 boolean scriptTag = false;
14250 String cb = request.getParameter("callback");
14251 if (cb != null) {
14252     scriptTag = true;
14253     response.setContentType("text/javascript");
14254 } else {
14255     response.setContentType("application/x-json");
14256 }
14257 Writer out = response.getWriter();
14258 if (scriptTag) {
14259     out.write(cb + "(");
14260 }
14261 out.print(dataBlock.toJsonString());
14262 if (scriptTag) {
14263     out.write(");");
14264 }
14265 </pre></code>
14266  *
14267  * @constructor
14268  * @param {Object} config A configuration object.
14269  */
14270 Roo.data.ScriptTagProxy = function(config){
14271     Roo.data.ScriptTagProxy.superclass.constructor.call(this);
14272     Roo.apply(this, config);
14273     this.head = document.getElementsByTagName("head")[0];
14274 };
14275
14276 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
14277
14278 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
14279     /**
14280      * @cfg {String} url The URL from which to request the data object.
14281      */
14282     /**
14283      * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
14284      */
14285     timeout : 30000,
14286     /**
14287      * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
14288      * the server the name of the callback function set up by the load call to process the returned data object.
14289      * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
14290      * javascript output which calls this named function passing the data object as its only parameter.
14291      */
14292     callbackParam : "callback",
14293     /**
14294      *  @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
14295      * name to the request.
14296      */
14297     nocache : true,
14298
14299     /**
14300      * Load data from the configured URL, read the data object into
14301      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
14302      * process that block using the passed callback.
14303      * @param {Object} params An object containing properties which are to be used as HTTP parameters
14304      * for the request to the remote server.
14305      * @param {Roo.data.DataReader} reader The Reader object which converts the data
14306      * object into a block of Roo.data.Records.
14307      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
14308      * The function must be passed <ul>
14309      * <li>The Record block object</li>
14310      * <li>The "arg" argument from the load function</li>
14311      * <li>A boolean success indicator</li>
14312      * </ul>
14313      * @param {Object} scope The scope in which to call the callback
14314      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
14315      */
14316     load : function(params, reader, callback, scope, arg){
14317         if(this.fireEvent("beforeload", this, params) !== false){
14318
14319             var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
14320
14321             var url = this.url;
14322             url += (url.indexOf("?") != -1 ? "&" : "?") + p;
14323             if(this.nocache){
14324                 url += "&_dc=" + (new Date().getTime());
14325             }
14326             var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
14327             var trans = {
14328                 id : transId,
14329                 cb : "stcCallback"+transId,
14330                 scriptId : "stcScript"+transId,
14331                 params : params,
14332                 arg : arg,
14333                 url : url,
14334                 callback : callback,
14335                 scope : scope,
14336                 reader : reader
14337             };
14338             var conn = this;
14339
14340             window[trans.cb] = function(o){
14341                 conn.handleResponse(o, trans);
14342             };
14343
14344             url += String.format("&{0}={1}", this.callbackParam, trans.cb);
14345
14346             if(this.autoAbort !== false){
14347                 this.abort();
14348             }
14349
14350             trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
14351
14352             var script = document.createElement("script");
14353             script.setAttribute("src", url);
14354             script.setAttribute("type", "text/javascript");
14355             script.setAttribute("id", trans.scriptId);
14356             this.head.appendChild(script);
14357
14358             this.trans = trans;
14359         }else{
14360             callback.call(scope||this, null, arg, false);
14361         }
14362     },
14363
14364     // private
14365     isLoading : function(){
14366         return this.trans ? true : false;
14367     },
14368
14369     /**
14370      * Abort the current server request.
14371      */
14372     abort : function(){
14373         if(this.isLoading()){
14374             this.destroyTrans(this.trans);
14375         }
14376     },
14377
14378     // private
14379     destroyTrans : function(trans, isLoaded){
14380         this.head.removeChild(document.getElementById(trans.scriptId));
14381         clearTimeout(trans.timeoutId);
14382         if(isLoaded){
14383             window[trans.cb] = undefined;
14384             try{
14385                 delete window[trans.cb];
14386             }catch(e){}
14387         }else{
14388             // if hasn't been loaded, wait for load to remove it to prevent script error
14389             window[trans.cb] = function(){
14390                 window[trans.cb] = undefined;
14391                 try{
14392                     delete window[trans.cb];
14393                 }catch(e){}
14394             };
14395         }
14396     },
14397
14398     // private
14399     handleResponse : function(o, trans){
14400         this.trans = false;
14401         this.destroyTrans(trans, true);
14402         var result;
14403         try {
14404             result = trans.reader.readRecords(o);
14405         }catch(e){
14406             this.fireEvent("loadexception", this, o, trans.arg, e);
14407             trans.callback.call(trans.scope||window, null, trans.arg, false);
14408             return;
14409         }
14410         this.fireEvent("load", this, o, trans.arg);
14411         trans.callback.call(trans.scope||window, result, trans.arg, true);
14412     },
14413
14414     // private
14415     handleFailure : function(trans){
14416         this.trans = false;
14417         this.destroyTrans(trans, false);
14418         this.fireEvent("loadexception", this, null, trans.arg);
14419         trans.callback.call(trans.scope||window, null, trans.arg, false);
14420     }
14421 });/*
14422  * Based on:
14423  * Ext JS Library 1.1.1
14424  * Copyright(c) 2006-2007, Ext JS, LLC.
14425  *
14426  * Originally Released Under LGPL - original licence link has changed is not relivant.
14427  *
14428  * Fork - LGPL
14429  * <script type="text/javascript">
14430  */
14431
14432 /**
14433  * @class Roo.data.JsonReader
14434  * @extends Roo.data.DataReader
14435  * Data reader class to create an Array of Roo.data.Record objects from a JSON response
14436  * based on mappings in a provided Roo.data.Record constructor.
14437  * 
14438  * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
14439  * in the reply previously. 
14440  * 
14441  * <p>
14442  * Example code:
14443  * <pre><code>
14444 var RecordDef = Roo.data.Record.create([
14445     {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
14446     {name: 'occupation'}                 // This field will use "occupation" as the mapping.
14447 ]);
14448 var myReader = new Roo.data.JsonReader({
14449     totalProperty: "results",    // The property which contains the total dataset size (optional)
14450     root: "rows",                // The property which contains an Array of row objects
14451     id: "id"                     // The property within each row object that provides an ID for the record (optional)
14452 }, RecordDef);
14453 </code></pre>
14454  * <p>
14455  * This would consume a JSON file like this:
14456  * <pre><code>
14457 { 'results': 2, 'rows': [
14458     { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
14459     { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
14460 }
14461 </code></pre>
14462  * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
14463  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
14464  * paged from the remote server.
14465  * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
14466  * @cfg {String} root name of the property which contains the Array of row objects.
14467  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
14468  * @cfg {Array} fields Array of field definition objects
14469  * @constructor
14470  * Create a new JsonReader
14471  * @param {Object} meta Metadata configuration options
14472  * @param {Object} recordType Either an Array of field definition objects,
14473  * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
14474  */
14475 Roo.data.JsonReader = function(meta, recordType){
14476     
14477     meta = meta || {};
14478     // set some defaults:
14479     Roo.applyIf(meta, {
14480         totalProperty: 'total',
14481         successProperty : 'success',
14482         root : 'data',
14483         id : 'id'
14484     });
14485     
14486     Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
14487 };
14488 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
14489     
14490     readerType : 'Json',
14491     
14492     /**
14493      * @prop {Boolean} metaFromRemote  - if the meta data was loaded from the remote source.
14494      * Used by Store query builder to append _requestMeta to params.
14495      * 
14496      */
14497     metaFromRemote : false,
14498     /**
14499      * This method is only used by a DataProxy which has retrieved data from a remote server.
14500      * @param {Object} response The XHR object which contains the JSON data in its responseText.
14501      * @return {Object} data A data block which is used by an Roo.data.Store object as
14502      * a cache of Roo.data.Records.
14503      */
14504     read : function(response){
14505         var json = response.responseText;
14506        
14507         var o = /* eval:var:o */ eval("("+json+")");
14508         if(!o) {
14509             throw {message: "JsonReader.read: Json object not found"};
14510         }
14511         
14512         if(o.metaData){
14513             
14514             delete this.ef;
14515             this.metaFromRemote = true;
14516             this.meta = o.metaData;
14517             this.recordType = Roo.data.Record.create(o.metaData.fields);
14518             this.onMetaChange(this.meta, this.recordType, o);
14519         }
14520         return this.readRecords(o);
14521     },
14522
14523     // private function a store will implement
14524     onMetaChange : function(meta, recordType, o){
14525
14526     },
14527
14528     /**
14529          * @ignore
14530          */
14531     simpleAccess: function(obj, subsc) {
14532         return obj[subsc];
14533     },
14534
14535         /**
14536          * @ignore
14537          */
14538     getJsonAccessor: function(){
14539         var re = /[\[\.]/;
14540         return function(expr) {
14541             try {
14542                 return(re.test(expr))
14543                     ? new Function("obj", "return obj." + expr)
14544                     : function(obj){
14545                         return obj[expr];
14546                     };
14547             } catch(e){}
14548             return Roo.emptyFn;
14549         };
14550     }(),
14551
14552     /**
14553      * Create a data block containing Roo.data.Records from an XML document.
14554      * @param {Object} o An object which contains an Array of row objects in the property specified
14555      * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
14556      * which contains the total size of the dataset.
14557      * @return {Object} data A data block which is used by an Roo.data.Store object as
14558      * a cache of Roo.data.Records.
14559      */
14560     readRecords : function(o){
14561         /**
14562          * After any data loads, the raw JSON data is available for further custom processing.
14563          * @type Object
14564          */
14565         this.o = o;
14566         var s = this.meta, Record = this.recordType,
14567             f = Record ? Record.prototype.fields : null, fi = f ? f.items : [], fl = f ? f.length : 0;
14568
14569 //      Generate extraction functions for the totalProperty, the root, the id, and for each field
14570         if (!this.ef) {
14571             if(s.totalProperty) {
14572                     this.getTotal = this.getJsonAccessor(s.totalProperty);
14573                 }
14574                 if(s.successProperty) {
14575                     this.getSuccess = this.getJsonAccessor(s.successProperty);
14576                 }
14577                 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
14578                 if (s.id) {
14579                         var g = this.getJsonAccessor(s.id);
14580                         this.getId = function(rec) {
14581                                 var r = g(rec);  
14582                                 return (r === undefined || r === "") ? null : r;
14583                         };
14584                 } else {
14585                         this.getId = function(){return null;};
14586                 }
14587             this.ef = [];
14588             for(var jj = 0; jj < fl; jj++){
14589                 f = fi[jj];
14590                 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
14591                 this.ef[jj] = this.getJsonAccessor(map);
14592             }
14593         }
14594
14595         var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
14596         if(s.totalProperty){
14597             var vt = parseInt(this.getTotal(o), 10);
14598             if(!isNaN(vt)){
14599                 totalRecords = vt;
14600             }
14601         }
14602         if(s.successProperty){
14603             var vs = this.getSuccess(o);
14604             if(vs === false || vs === 'false'){
14605                 success = false;
14606             }
14607         }
14608         var records = [];
14609         for(var i = 0; i < c; i++){
14610                 var n = root[i];
14611             var values = {};
14612             var id = this.getId(n);
14613             for(var j = 0; j < fl; j++){
14614                 f = fi[j];
14615             var v = this.ef[j](n);
14616             if (!f.convert) {
14617                 Roo.log('missing convert for ' + f.name);
14618                 Roo.log(f);
14619                 continue;
14620             }
14621             values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
14622             }
14623             var record = new Record(values, id);
14624             record.json = n;
14625             records[i] = record;
14626         }
14627         return {
14628             raw : o,
14629             success : success,
14630             records : records,
14631             totalRecords : totalRecords
14632         };
14633     },
14634     // used when loading children.. @see loadDataFromChildren
14635     toLoadData: function(rec)
14636     {
14637         // expect rec just to be an array.. eg [a,b,c, [...] << cn ]
14638         var data = typeof(rec.data.cn) == 'undefined' ? [] : rec.data.cn;
14639         return { data : data, total : data.length };
14640         
14641     }
14642 });/*
14643  * Based on:
14644  * Ext JS Library 1.1.1
14645  * Copyright(c) 2006-2007, Ext JS, LLC.
14646  *
14647  * Originally Released Under LGPL - original licence link has changed is not relivant.
14648  *
14649  * Fork - LGPL
14650  * <script type="text/javascript">
14651  */
14652
14653 /**
14654  * @class Roo.data.ArrayReader
14655  * @extends Roo.data.DataReader
14656  * Data reader class to create an Array of Roo.data.Record objects from an Array.
14657  * Each element of that Array represents a row of data fields. The
14658  * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
14659  * of the field definition if it exists, or the field's ordinal position in the definition.<br>
14660  * <p>
14661  * Example code:.
14662  * <pre><code>
14663 var RecordDef = Roo.data.Record.create([
14664     {name: 'name', mapping: 1},         // "mapping" only needed if an "id" field is present which
14665     {name: 'occupation', mapping: 2}    // precludes using the ordinal position as the index.
14666 ]);
14667 var myReader = new Roo.data.ArrayReader({
14668     id: 0                     // The subscript within row Array that provides an ID for the Record (optional)
14669 }, RecordDef);
14670 </code></pre>
14671  * <p>
14672  * This would consume an Array like this:
14673  * <pre><code>
14674 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
14675   </code></pre>
14676  
14677  * @constructor
14678  * Create a new JsonReader
14679  * @param {Object} meta Metadata configuration options.
14680  * @param {Object|Array} recordType Either an Array of field definition objects
14681  * 
14682  * @cfg {Array} fields Array of field definition objects
14683  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
14684  * as specified to {@link Roo.data.Record#create},
14685  * or an {@link Roo.data.Record} object
14686  *
14687  * 
14688  * created using {@link Roo.data.Record#create}.
14689  */
14690 Roo.data.ArrayReader = function(meta, recordType)
14691 {    
14692     Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType||meta.fields);
14693 };
14694
14695 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
14696     
14697       /**
14698      * Create a data block containing Roo.data.Records from an XML document.
14699      * @param {Object} o An Array of row objects which represents the dataset.
14700      * @return {Object} A data block which is used by an {@link Roo.data.Store} object as
14701      * a cache of Roo.data.Records.
14702      */
14703     readRecords : function(o)
14704     {
14705         var sid = this.meta ? this.meta.id : null;
14706         var recordType = this.recordType, fields = recordType.prototype.fields;
14707         var records = [];
14708         var root = o;
14709         for(var i = 0; i < root.length; i++){
14710                 var n = root[i];
14711             var values = {};
14712             var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
14713             for(var j = 0, jlen = fields.length; j < jlen; j++){
14714                 var f = fields.items[j];
14715                 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
14716                 var v = n[k] !== undefined ? n[k] : f.defaultValue;
14717                 v = f.convert(v);
14718                 values[f.name] = v;
14719             }
14720             var record = new recordType(values, id);
14721             record.json = n;
14722             records[records.length] = record;
14723         }
14724         return {
14725             records : records,
14726             totalRecords : records.length
14727         };
14728     },
14729     // used when loading children.. @see loadDataFromChildren
14730     toLoadData: function(rec)
14731     {
14732         // expect rec just to be an array.. eg [a,b,c, [...] << cn ]
14733         return typeof(rec.data.cn) == 'undefined' ? [] : rec.data.cn;
14734         
14735     }
14736     
14737     
14738 });/*
14739  * - LGPL
14740  * * 
14741  */
14742
14743 /**
14744  * @class Roo.bootstrap.ComboBox
14745  * @extends Roo.bootstrap.TriggerField
14746  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
14747  * @cfg {Boolean} append (true|false) default false
14748  * @cfg {Boolean} autoFocus (true|false) auto focus the first item, default true
14749  * @cfg {Boolean} tickable ComboBox with tickable selections (true|false), default false
14750  * @cfg {Boolean} triggerList trigger show the list or not (true|false) default true
14751  * @cfg {Boolean} showToggleBtn show toggle button or not (true|false) default true
14752  * @cfg {String} btnPosition set the position of the trigger button (left | right) default right
14753  * @cfg {Boolean} animate default true
14754  * @cfg {Boolean} emptyResultText only for touch device
14755  * @cfg {String} triggerText multiple combobox trigger button text default 'Select'
14756  * @cfg {String} emptyTitle default ''
14757  * @cfg {Number} width fixed with? experimental
14758  * @constructor
14759  * Create a new ComboBox.
14760  * @param {Object} config Configuration options
14761  */
14762 Roo.bootstrap.ComboBox = function(config){
14763     Roo.bootstrap.ComboBox.superclass.constructor.call(this, config);
14764     this.addEvents({
14765         /**
14766          * @event expand
14767          * Fires when the dropdown list is expanded
14768         * @param {Roo.bootstrap.ComboBox} combo This combo box
14769         */
14770         'expand' : true,
14771         /**
14772          * @event collapse
14773          * Fires when the dropdown list is collapsed
14774         * @param {Roo.bootstrap.ComboBox} combo This combo box
14775         */
14776         'collapse' : true,
14777         /**
14778          * @event beforeselect
14779          * Fires before a list item is selected. Return false to cancel the selection.
14780         * @param {Roo.bootstrap.ComboBox} combo This combo box
14781         * @param {Roo.data.Record} record The data record returned from the underlying store
14782         * @param {Number} index The index of the selected item in the dropdown list
14783         */
14784         'beforeselect' : true,
14785         /**
14786          * @event select
14787          * Fires when a list item is selected
14788         * @param {Roo.bootstrap.ComboBox} combo This combo box
14789         * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
14790         * @param {Number} index The index of the selected item in the dropdown list
14791         */
14792         'select' : true,
14793         /**
14794          * @event beforequery
14795          * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
14796          * The event object passed has these properties:
14797         * @param {Roo.bootstrap.ComboBox} combo This combo box
14798         * @param {String} query The query
14799         * @param {Boolean} forceAll true to force "all" query
14800         * @param {Boolean} cancel true to cancel the query
14801         * @param {Object} e The query event object
14802         */
14803         'beforequery': true,
14804          /**
14805          * @event add
14806          * Fires when the 'add' icon is pressed (add a listener to enable add button)
14807         * @param {Roo.bootstrap.ComboBox} combo This combo box
14808         */
14809         'add' : true,
14810         /**
14811          * @event edit
14812          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
14813         * @param {Roo.bootstrap.ComboBox} combo This combo box
14814         * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
14815         */
14816         'edit' : true,
14817         /**
14818          * @event remove
14819          * Fires when the remove value from the combobox array
14820         * @param {Roo.bootstrap.ComboBox} combo This combo box
14821         */
14822         'remove' : true,
14823         /**
14824          * @event afterremove
14825          * Fires when the remove value from the combobox array
14826         * @param {Roo.bootstrap.ComboBox} combo This combo box
14827         */
14828         'afterremove' : true,
14829         /**
14830          * @event specialfilter
14831          * Fires when specialfilter
14832             * @param {Roo.bootstrap.ComboBox} combo This combo box
14833             */
14834         'specialfilter' : true,
14835         /**
14836          * @event tick
14837          * Fires when tick the element
14838             * @param {Roo.bootstrap.ComboBox} combo This combo box
14839             */
14840         'tick' : true,
14841         /**
14842          * @event touchviewdisplay
14843          * Fires when touch view require special display (default is using displayField)
14844             * @param {Roo.bootstrap.ComboBox} combo This combo box
14845             * @param {Object} cfg set html .
14846             */
14847         'touchviewdisplay' : true
14848         
14849     });
14850     
14851     this.item = [];
14852     this.tickItems = [];
14853     
14854     this.selectedIndex = -1;
14855     if(this.mode == 'local'){
14856         if(config.queryDelay === undefined){
14857             this.queryDelay = 10;
14858         }
14859         if(config.minChars === undefined){
14860             this.minChars = 0;
14861         }
14862     }
14863 };
14864
14865 Roo.extend(Roo.bootstrap.ComboBox, Roo.bootstrap.TriggerField, {
14866      
14867     /**
14868      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
14869      * rendering into an Roo.Editor, defaults to false)
14870      */
14871     /**
14872      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
14873      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
14874      */
14875     /**
14876      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
14877      */
14878     /**
14879      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
14880      * the dropdown list (defaults to undefined, with no header element)
14881      */
14882
14883      /**
14884      * @cfg {String/Roo.Template} tpl The template to use to render the output default is  '<a class="dropdown-item" href="#">{' + this.displayField + '}</a>' 
14885      */
14886      
14887      /**
14888      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
14889      */
14890     listWidth: undefined,
14891     /**
14892      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
14893      * mode = 'remote' or 'text' if mode = 'local')
14894      */
14895     displayField: undefined,
14896     
14897     /**
14898      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
14899      * mode = 'remote' or 'value' if mode = 'local'). 
14900      * Note: use of a valueField requires the user make a selection
14901      * in order for a value to be mapped.
14902      */
14903     valueField: undefined,
14904     /**
14905      * @cfg {String} modalTitle The title of the dialog that pops up on mobile views.
14906      */
14907     modalTitle : '',
14908     
14909     /**
14910      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
14911      * field's data value (defaults to the underlying DOM element's name)
14912      */
14913     hiddenName: undefined,
14914     /**
14915      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
14916      */
14917     listClass: '',
14918     /**
14919      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
14920      */
14921     selectedClass: 'active',
14922     
14923     /**
14924      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
14925      */
14926     shadow:'sides',
14927     /**
14928      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
14929      * anchor positions (defaults to 'tl-bl')
14930      */
14931     listAlign: 'tl-bl?',
14932     /**
14933      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
14934      */
14935     maxHeight: 300,
14936     /**
14937      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
14938      * query specified by the allQuery config option (defaults to 'query')
14939      */
14940     triggerAction: 'query',
14941     /**
14942      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
14943      * (defaults to 4, does not apply if editable = false)
14944      */
14945     minChars : 4,
14946     /**
14947      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
14948      * delay (typeAheadDelay) if it matches a known value (defaults to false)
14949      */
14950     typeAhead: false,
14951     /**
14952      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
14953      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
14954      */
14955     queryDelay: 500,
14956     /**
14957      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
14958      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
14959      */
14960     pageSize: 0,
14961     /**
14962      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
14963      * when editable = true (defaults to false)
14964      */
14965     selectOnFocus:false,
14966     /**
14967      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
14968      */
14969     queryParam: 'query',
14970     /**
14971      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
14972      * when mode = 'remote' (defaults to 'Loading...')
14973      */
14974     loadingText: 'Loading...',
14975     /**
14976      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
14977      */
14978     resizable: false,
14979     /**
14980      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
14981      */
14982     handleHeight : 8,
14983     /**
14984      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
14985      * traditional select (defaults to true)
14986      */
14987     editable: true,
14988     /**
14989      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
14990      */
14991     allQuery: '',
14992     /**
14993      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
14994      */
14995     mode: 'remote',
14996     /**
14997      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
14998      * listWidth has a higher value)
14999      */
15000     minListWidth : 70,
15001     /**
15002      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
15003      * allow the user to set arbitrary text into the field (defaults to false)
15004      */
15005     forceSelection:false,
15006     /**
15007      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
15008      * if typeAhead = true (defaults to 250)
15009      */
15010     typeAheadDelay : 250,
15011     /**
15012      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
15013      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
15014      */
15015     valueNotFoundText : undefined,
15016     /**
15017      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
15018      */
15019     blockFocus : false,
15020     
15021     /**
15022      * @cfg {Boolean} disableClear Disable showing of clear button.
15023      */
15024     disableClear : false,
15025     /**
15026      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
15027      */
15028     alwaysQuery : false,
15029     
15030     /**
15031      * @cfg {Boolean} multiple  (true|false) ComboBobArray, default false
15032      */
15033     multiple : false,
15034     
15035     /**
15036      * @cfg {String} invalidClass DEPRICATED - uses BS4 is-valid now
15037      */
15038     invalidClass : "has-warning",
15039     
15040     /**
15041      * @cfg {String} validClass DEPRICATED - uses BS4 is-valid now
15042      */
15043     validClass : "has-success",
15044     
15045     /**
15046      * @cfg {Boolean} specialFilter (true|false) special filter default false
15047      */
15048     specialFilter : false,
15049     
15050     /**
15051      * @cfg {Boolean} mobileTouchView (true|false) show mobile touch view when using a mobile default true
15052      */
15053     mobileTouchView : true,
15054     
15055     /**
15056      * @cfg {Boolean} useNativeIOS (true|false) render it as classic select for ios, not support dynamic load data (default false)
15057      */
15058     useNativeIOS : false,
15059     
15060     /**
15061      * @cfg {Boolean} mobile_restrict_height (true|false) restrict height for touch view
15062      */
15063     mobile_restrict_height : false,
15064     
15065     ios_options : false,
15066     
15067     //private
15068     addicon : false,
15069     editicon: false,
15070     
15071     page: 0,
15072     hasQuery: false,
15073     append: false,
15074     loadNext: false,
15075     autoFocus : true,
15076     tickable : false,
15077     btnPosition : 'right',
15078     triggerList : true,
15079     showToggleBtn : true,
15080     animate : true,
15081     emptyResultText: 'Empty',
15082     triggerText : 'Select',
15083     emptyTitle : '',
15084     width : false,
15085     
15086     // element that contains real text value.. (when hidden is used..)
15087     
15088     getAutoCreate : function()
15089     {   
15090         var cfg = false;
15091         //render
15092         /*
15093          * Render classic select for iso
15094          */
15095         
15096         if(Roo.isIOS && this.useNativeIOS){
15097             cfg = this.getAutoCreateNativeIOS();
15098             return cfg;
15099         }
15100         
15101         /*
15102          * Touch Devices
15103          */
15104         
15105         if(Roo.isTouch && this.mobileTouchView){
15106             cfg = this.getAutoCreateTouchView();
15107             return cfg;;
15108         }
15109         
15110         /*
15111          *  Normal ComboBox
15112          */
15113         if(!this.tickable){
15114             cfg = Roo.bootstrap.ComboBox.superclass.getAutoCreate.call(this);
15115             return cfg;
15116         }
15117         
15118         /*
15119          *  ComboBox with tickable selections
15120          */
15121              
15122         var align = this.labelAlign || this.parentLabelAlign();
15123         
15124         cfg = {
15125             cls : 'form-group roo-combobox-tickable' //input-group
15126         };
15127         
15128         var btn_text_select = '';
15129         var btn_text_done = '';
15130         var btn_text_cancel = '';
15131         
15132         if (this.btn_text_show) {
15133             btn_text_select = 'Select';
15134             btn_text_done = 'Done';
15135             btn_text_cancel = 'Cancel'; 
15136         }
15137         
15138         var buttons = {
15139             tag : 'div',
15140             cls : 'tickable-buttons',
15141             cn : [
15142                 {
15143                     tag : 'button',
15144                     type : 'button',
15145                     cls : 'btn btn-link btn-edit pull-' + this.btnPosition,
15146                     //html : this.triggerText
15147                     html: btn_text_select
15148                 },
15149                 {
15150                     tag : 'button',
15151                     type : 'button',
15152                     name : 'ok',
15153                     cls : 'btn btn-link btn-ok pull-' + this.btnPosition,
15154                     //html : 'Done'
15155                     html: btn_text_done
15156                 },
15157                 {
15158                     tag : 'button',
15159                     type : 'button',
15160                     name : 'cancel',
15161                     cls : 'btn btn-link btn-cancel pull-' + this.btnPosition,
15162                     //html : 'Cancel'
15163                     html: btn_text_cancel
15164                 }
15165             ]
15166         };
15167         
15168         if(this.editable){
15169             buttons.cn.unshift({
15170                 tag: 'input',
15171                 cls: 'roo-select2-search-field-input'
15172             });
15173         }
15174         
15175         var _this = this;
15176         
15177         Roo.each(buttons.cn, function(c){
15178             if (_this.size) {
15179                 c.cls += ' btn-' + _this.size;
15180             }
15181
15182             if (_this.disabled) {
15183                 c.disabled = true;
15184             }
15185         });
15186         
15187         var box = {
15188             tag: 'div',
15189             style : 'display: contents',
15190             cn: [
15191                 {
15192                     tag: 'input',
15193                     type : 'hidden',
15194                     cls: 'form-hidden-field'
15195                 },
15196                 {
15197                     tag: 'ul',
15198                     cls: 'roo-select2-choices',
15199                     cn:[
15200                         {
15201                             tag: 'li',
15202                             cls: 'roo-select2-search-field',
15203                             cn: [
15204                                 buttons
15205                             ]
15206                         }
15207                     ]
15208                 }
15209             ]
15210         };
15211         
15212         var combobox = {
15213             cls: 'roo-select2-container input-group roo-select2-container-multi',
15214             cn: [
15215                 
15216                 box
15217 //                {
15218 //                    tag: 'ul',
15219 //                    cls: 'typeahead typeahead-long dropdown-menu',
15220 //                    style: 'display:none; max-height:' + this.maxHeight + 'px;'
15221 //                }
15222             ]
15223         };
15224         
15225         if(this.hasFeedback && !this.allowBlank){
15226             
15227             var feedback = {
15228                 tag: 'span',
15229                 cls: 'glyphicon form-control-feedback'
15230             };
15231
15232             combobox.cn.push(feedback);
15233         }
15234         
15235         
15236         
15237         var indicator = {
15238             tag : 'i',
15239             cls : 'roo-required-indicator ' + (this.indicatorpos == 'right'  ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
15240             tooltip : 'This field is required'
15241         };
15242         if (Roo.bootstrap.version == 4) {
15243             indicator = {
15244                 tag : 'i',
15245                 style : 'display:none'
15246             };
15247         }
15248         if (align ==='left' && this.fieldLabel.length) {
15249             
15250             cfg.cls += ' roo-form-group-label-left'  + (Roo.bootstrap.version == 4 ? ' row' : '');
15251             
15252             cfg.cn = [
15253                 indicator,
15254                 {
15255                     tag: 'label',
15256                     'for' :  id,
15257                     cls : 'control-label col-form-label',
15258                     html : this.fieldLabel
15259
15260                 },
15261                 {
15262                     cls : "", 
15263                     cn: [
15264                         combobox
15265                     ]
15266                 }
15267
15268             ];
15269             
15270             var labelCfg = cfg.cn[1];
15271             var contentCfg = cfg.cn[2];
15272             
15273
15274             if(this.indicatorpos == 'right'){
15275                 
15276                 cfg.cn = [
15277                     {
15278                         tag: 'label',
15279                         'for' :  id,
15280                         cls : 'control-label col-form-label',
15281                         cn : [
15282                             {
15283                                 tag : 'span',
15284                                 html : this.fieldLabel
15285                             },
15286                             indicator
15287                         ]
15288                     },
15289                     {
15290                         cls : "",
15291                         cn: [
15292                             combobox
15293                         ]
15294                     }
15295
15296                 ];
15297                 
15298                 
15299                 
15300                 labelCfg = cfg.cn[0];
15301                 contentCfg = cfg.cn[1];
15302             
15303             }
15304             
15305             if(this.labelWidth > 12){
15306                 labelCfg.style = "width: " + this.labelWidth + 'px';
15307             }
15308             if(this.width * 1 > 0){
15309                 contentCfg.style = "width: " + this.width + 'px';
15310             }
15311             if(this.labelWidth < 13 && this.labelmd == 0){
15312                 this.labelmd = this.labelWidth;
15313             }
15314             
15315             if(this.labellg > 0){
15316                 labelCfg.cls += ' col-lg-' + this.labellg;
15317                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
15318             }
15319             
15320             if(this.labelmd > 0){
15321                 labelCfg.cls += ' col-md-' + this.labelmd;
15322                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
15323             }
15324             
15325             if(this.labelsm > 0){
15326                 labelCfg.cls += ' col-sm-' + this.labelsm;
15327                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
15328             }
15329             
15330             if(this.labelxs > 0){
15331                 labelCfg.cls += ' col-xs-' + this.labelxs;
15332                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
15333             }
15334                 
15335                 
15336         } else if ( this.fieldLabel.length) {
15337 //                Roo.log(" label");
15338                  cfg.cn = [
15339                    indicator,
15340                     {
15341                         tag: 'label',
15342                         //cls : 'input-group-addon',
15343                         html : this.fieldLabel
15344                     },
15345                     combobox
15346                 ];
15347                 
15348                 if(this.indicatorpos == 'right'){
15349                     cfg.cn = [
15350                         {
15351                             tag: 'label',
15352                             //cls : 'input-group-addon',
15353                             html : this.fieldLabel
15354                         },
15355                         indicator,
15356                         combobox
15357                     ];
15358                     
15359                 }
15360
15361         } else {
15362             
15363 //                Roo.log(" no label && no align");
15364                 cfg = combobox
15365                      
15366                 
15367         }
15368          
15369         var settings=this;
15370         ['xs','sm','md','lg'].map(function(size){
15371             if (settings[size]) {
15372                 cfg.cls += ' col-' + size + '-' + settings[size];
15373             }
15374         });
15375         
15376         return cfg;
15377         
15378     },
15379     
15380     _initEventsCalled : false,
15381     
15382     // private
15383     initEvents: function()
15384     {   
15385         if (this._initEventsCalled) { // as we call render... prevent looping...
15386             return;
15387         }
15388         this._initEventsCalled = true;
15389         
15390         if (!this.store) {
15391             throw "can not find store for combo";
15392         }
15393         
15394         this.indicator = this.indicatorEl();
15395         
15396         this.store = Roo.factory(this.store, Roo.data);
15397         this.store.parent = this;
15398         
15399         // if we are building from html. then this element is so complex, that we can not really
15400         // use the rendered HTML.
15401         // so we have to trash and replace the previous code.
15402         if (Roo.XComponent.build_from_html) {
15403             // remove this element....
15404             var e = this.el.dom, k=0;
15405             while (e ) { e = e.previousSibling;  ++k;}
15406
15407             this.el.remove();
15408             
15409             this.el=false;
15410             this.rendered = false;
15411             
15412             this.render(this.parent().getChildContainer(true), k);
15413         }
15414         
15415         if(Roo.isIOS && this.useNativeIOS){
15416             this.initIOSView();
15417             return;
15418         }
15419         
15420         /*
15421          * Touch Devices
15422          */
15423         
15424         if(Roo.isTouch && this.mobileTouchView){
15425             this.initTouchView();
15426             return;
15427         }
15428         
15429         if(this.tickable){
15430             this.initTickableEvents();
15431             return;
15432         }
15433         
15434         Roo.bootstrap.ComboBox.superclass.initEvents.call(this);
15435         
15436         if(this.hiddenName){
15437             
15438             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
15439             
15440             this.hiddenField.dom.value =
15441                 this.hiddenValue !== undefined ? this.hiddenValue :
15442                 this.value !== undefined ? this.value : '';
15443
15444             // prevent input submission
15445             this.el.dom.removeAttribute('name');
15446             this.hiddenField.dom.setAttribute('name', this.hiddenName);
15447              
15448              
15449         }
15450         //if(Roo.isGecko){
15451         //    this.el.dom.setAttribute('autocomplete', 'off');
15452         //}
15453         
15454         var cls = 'x-combo-list';
15455         
15456         //this.list = new Roo.Layer({
15457         //    shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
15458         //});
15459         
15460         var _this = this;
15461         
15462         (function(){
15463             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
15464             _this.list.setWidth(lw);
15465         }).defer(100);
15466         
15467         this.list.on('mouseover', this.onViewOver, this);
15468         this.list.on('mousemove', this.onViewMove, this);
15469         this.list.on('scroll', this.onViewScroll, this);
15470         
15471         /*
15472         this.list.swallowEvent('mousewheel');
15473         this.assetHeight = 0;
15474
15475         if(this.title){
15476             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
15477             this.assetHeight += this.header.getHeight();
15478         }
15479
15480         this.innerList = this.list.createChild({cls:cls+'-inner'});
15481         this.innerList.on('mouseover', this.onViewOver, this);
15482         this.innerList.on('mousemove', this.onViewMove, this);
15483         this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
15484         
15485         if(this.allowBlank && !this.pageSize && !this.disableClear){
15486             this.footer = this.list.createChild({cls:cls+'-ft'});
15487             this.pageTb = new Roo.Toolbar(this.footer);
15488            
15489         }
15490         if(this.pageSize){
15491             this.footer = this.list.createChild({cls:cls+'-ft'});
15492             this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
15493                     {pageSize: this.pageSize});
15494             
15495         }
15496         
15497         if (this.pageTb && this.allowBlank && !this.disableClear) {
15498             var _this = this;
15499             this.pageTb.add(new Roo.Toolbar.Fill(), {
15500                 cls: 'x-btn-icon x-btn-clear',
15501                 text: '&#160;',
15502                 handler: function()
15503                 {
15504                     _this.collapse();
15505                     _this.clearValue();
15506                     _this.onSelect(false, -1);
15507                 }
15508             });
15509         }
15510         if (this.footer) {
15511             this.assetHeight += this.footer.getHeight();
15512         }
15513         */
15514             
15515         if(!this.tpl){
15516             this.tpl = Roo.bootstrap.version == 4 ?
15517                 '<a class="dropdown-item" href="#">{' + this.displayField + '}</a>' :  // 4 does not need <li> and it get's really confisued.
15518                 '<li><a class="dropdown-item" href="#">{' + this.displayField + '}</a></li>';
15519         }
15520
15521         this.view = new Roo.View(this.list, this.tpl, {
15522             singleSelect:true, store: this.store, selectedClass: this.selectedClass
15523         });
15524         //this.view.wrapEl.setDisplayed(false);
15525         this.view.on('click', this.onViewClick, this);
15526         
15527         
15528         this.store.on('beforeload', this.onBeforeLoad, this);
15529         this.store.on('load', this.onLoad, this);
15530         this.store.on('loadexception', this.onLoadException, this);
15531         /*
15532         if(this.resizable){
15533             this.resizer = new Roo.Resizable(this.list,  {
15534                pinned:true, handles:'se'
15535             });
15536             this.resizer.on('resize', function(r, w, h){
15537                 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
15538                 this.listWidth = w;
15539                 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
15540                 this.restrictHeight();
15541             }, this);
15542             this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
15543         }
15544         */
15545         if(!this.editable){
15546             this.editable = true;
15547             this.setEditable(false);
15548         }
15549         
15550         /*
15551         
15552         if (typeof(this.events.add.listeners) != 'undefined') {
15553             
15554             this.addicon = this.wrap.createChild(
15555                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });  
15556        
15557             this.addicon.on('click', function(e) {
15558                 this.fireEvent('add', this);
15559             }, this);
15560         }
15561         if (typeof(this.events.edit.listeners) != 'undefined') {
15562             
15563             this.editicon = this.wrap.createChild(
15564                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });  
15565             if (this.addicon) {
15566                 this.editicon.setStyle('margin-left', '40px');
15567             }
15568             this.editicon.on('click', function(e) {
15569                 
15570                 // we fire even  if inothing is selected..
15571                 this.fireEvent('edit', this, this.lastData );
15572                 
15573             }, this);
15574         }
15575         */
15576         
15577         this.keyNav = new Roo.KeyNav(this.inputEl(), {
15578             "up" : function(e){
15579                 this.inKeyMode = true;
15580                 this.selectPrev();
15581             },
15582
15583             "down" : function(e){
15584                 if(!this.isExpanded()){
15585                     this.onTriggerClick();
15586                 }else{
15587                     this.inKeyMode = true;
15588                     this.selectNext();
15589                 }
15590             },
15591
15592             "enter" : function(e){
15593 //                this.onViewClick();
15594                 //return true;
15595                 this.collapse();
15596                 
15597                 if(this.fireEvent("specialkey", this, e)){
15598                     this.onViewClick(false);
15599                 }
15600                 
15601                 return true;
15602             },
15603
15604             "esc" : function(e){
15605                 this.collapse();
15606             },
15607
15608             "tab" : function(e){
15609                 this.collapse();
15610                 
15611                 if(this.fireEvent("specialkey", this, e)){
15612                     this.onViewClick(false);
15613                 }
15614                 
15615                 return true;
15616             },
15617
15618             scope : this,
15619
15620             doRelay : function(foo, bar, hname){
15621                 if(hname == 'down' || this.scope.isExpanded()){
15622                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
15623                 }
15624                 return true;
15625             },
15626
15627             forceKeyDown: true
15628         });
15629         
15630         
15631         this.queryDelay = Math.max(this.queryDelay || 10,
15632                 this.mode == 'local' ? 10 : 250);
15633         
15634         
15635         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
15636         
15637         if(this.typeAhead){
15638             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
15639         }
15640         if(this.editable !== false){
15641             this.inputEl().on("keyup", this.onKeyUp, this);
15642         }
15643         if(this.forceSelection){
15644             this.inputEl().on('blur', this.doForce, this);
15645         }
15646         
15647         if(this.multiple){
15648             this.choices = this.el.select('ul.roo-select2-choices', true).first();
15649             this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
15650         }
15651     },
15652     
15653     initTickableEvents: function()
15654     {   
15655         this.createList();
15656         
15657         if(this.hiddenName){
15658             
15659             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
15660             
15661             this.hiddenField.dom.value =
15662                 this.hiddenValue !== undefined ? this.hiddenValue :
15663                 this.value !== undefined ? this.value : '';
15664
15665             // prevent input submission
15666             this.el.dom.removeAttribute('name');
15667             this.hiddenField.dom.setAttribute('name', this.hiddenName);
15668              
15669              
15670         }
15671         
15672 //        this.list = this.el.select('ul.dropdown-menu',true).first();
15673         
15674         this.choices = this.el.select('ul.roo-select2-choices', true).first();
15675         this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
15676         if(this.triggerList){
15677             this.searchField.on("click", this.onSearchFieldClick, this, {preventDefault:true});
15678         }
15679          
15680         this.trigger = this.el.select('.tickable-buttons > .btn-edit', true).first();
15681         this.trigger.on("click", this.onTickableTriggerClick, this, {preventDefault:true});
15682         
15683         this.okBtn = this.el.select('.tickable-buttons > .btn-ok', true).first();
15684         this.cancelBtn = this.el.select('.tickable-buttons > .btn-cancel', true).first();
15685         
15686         this.okBtn.on('click', this.onTickableFooterButtonClick, this, this.okBtn);
15687         this.cancelBtn.on('click', this.onTickableFooterButtonClick, this, this.cancelBtn);
15688         
15689         this.trigger.setVisibilityMode(Roo.Element.DISPLAY);
15690         this.okBtn.setVisibilityMode(Roo.Element.DISPLAY);
15691         this.cancelBtn.setVisibilityMode(Roo.Element.DISPLAY);
15692         
15693         this.okBtn.hide();
15694         this.cancelBtn.hide();
15695         
15696         var _this = this;
15697         
15698         (function(){
15699             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
15700             _this.list.setWidth(lw);
15701         }).defer(100);
15702         
15703         this.list.on('mouseover', this.onViewOver, this);
15704         this.list.on('mousemove', this.onViewMove, this);
15705         
15706         this.list.on('scroll', this.onViewScroll, this);
15707         
15708         if(!this.tpl){
15709             this.tpl = '<li class="roo-select2-result"><div class="checkbox"><input id="{roo-id}"' + 
15710                 'type="checkbox" {roo-data-checked}><label for="{roo-id}"><b>{' + this.displayField + '}</b></label></div></li>';
15711         }
15712
15713         this.view = new Roo.View(this.list, this.tpl, {
15714             singleSelect:true,
15715             tickable:true,
15716             parent:this,
15717             store: this.store,
15718             selectedClass: this.selectedClass
15719         });
15720         
15721         //this.view.wrapEl.setDisplayed(false);
15722         this.view.on('click', this.onViewClick, this);
15723         
15724         
15725         
15726         this.store.on('beforeload', this.onBeforeLoad, this);
15727         this.store.on('load', this.onLoad, this);
15728         this.store.on('loadexception', this.onLoadException, this);
15729         
15730         if(this.editable){
15731             this.keyNav = new Roo.KeyNav(this.tickableInputEl(), {
15732                 "up" : function(e){
15733                     this.inKeyMode = true;
15734                     this.selectPrev();
15735                 },
15736
15737                 "down" : function(e){
15738                     this.inKeyMode = true;
15739                     this.selectNext();
15740                 },
15741
15742                 "enter" : function(e){
15743                     if(this.fireEvent("specialkey", this, e)){
15744                         this.onViewClick(false);
15745                     }
15746                     
15747                     return true;
15748                 },
15749
15750                 "esc" : function(e){
15751                     this.onTickableFooterButtonClick(e, false, false);
15752                 },
15753
15754                 "tab" : function(e){
15755                     this.fireEvent("specialkey", this, e);
15756                     
15757                     this.onTickableFooterButtonClick(e, false, false);
15758                     
15759                     return true;
15760                 },
15761
15762                 scope : this,
15763
15764                 doRelay : function(e, fn, key){
15765                     if(this.scope.isExpanded()){
15766                        return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
15767                     }
15768                     return true;
15769                 },
15770
15771                 forceKeyDown: true
15772             });
15773         }
15774         
15775         this.queryDelay = Math.max(this.queryDelay || 10,
15776                 this.mode == 'local' ? 10 : 250);
15777         
15778         
15779         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
15780         
15781         if(this.typeAhead){
15782             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
15783         }
15784         
15785         if(this.editable !== false){
15786             this.tickableInputEl().on("keyup", this.onKeyUp, this);
15787         }
15788         
15789         this.indicator = this.indicatorEl();
15790         
15791         if(this.indicator){
15792             this.indicator.setVisibilityMode(Roo.Element.DISPLAY);
15793             this.indicator.hide();
15794         }
15795         
15796     },
15797
15798     onDestroy : function(){
15799         if(this.view){
15800             this.view.setStore(null);
15801             this.view.el.removeAllListeners();
15802             this.view.el.remove();
15803             this.view.purgeListeners();
15804         }
15805         if(this.list){
15806             this.list.dom.innerHTML  = '';
15807         }
15808         
15809         if(this.store){
15810             this.store.un('beforeload', this.onBeforeLoad, this);
15811             this.store.un('load', this.onLoad, this);
15812             this.store.un('loadexception', this.onLoadException, this);
15813         }
15814         Roo.bootstrap.ComboBox.superclass.onDestroy.call(this);
15815     },
15816
15817     // private
15818     fireKey : function(e){
15819         if(e.isNavKeyPress() && !this.list.isVisible()){
15820             this.fireEvent("specialkey", this, e);
15821         }
15822     },
15823
15824     // private
15825     onResize: function(w, h)
15826     {
15827         
15828         
15829 //        Roo.bootstrap.ComboBox.superclass.onResize.apply(this, arguments);
15830 //        
15831 //        if(typeof w != 'number'){
15832 //            // we do not handle it!?!?
15833 //            return;
15834 //        }
15835 //        var tw = this.trigger.getWidth();
15836 //       // tw += this.addicon ? this.addicon.getWidth() : 0;
15837 //       // tw += this.editicon ? this.editicon.getWidth() : 0;
15838 //        var x = w - tw;
15839 //        this.inputEl().setWidth( this.adjustWidth('input', x));
15840 //            
15841 //        //this.trigger.setStyle('left', x+'px');
15842 //        
15843 //        if(this.list && this.listWidth === undefined){
15844 //            var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
15845 //            this.list.setWidth(lw);
15846 //            this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
15847 //        }
15848         
15849     
15850         
15851     },
15852
15853     /**
15854      * Allow or prevent the user from directly editing the field text.  If false is passed,
15855      * the user will only be able to select from the items defined in the dropdown list.  This method
15856      * is the runtime equivalent of setting the 'editable' config option at config time.
15857      * @param {Boolean} value True to allow the user to directly edit the field text
15858      */
15859     setEditable : function(value){
15860         if(value == this.editable){
15861             return;
15862         }
15863         this.editable = value;
15864         if(!value){
15865             this.inputEl().dom.setAttribute('readOnly', true);
15866             this.inputEl().on('mousedown', this.onTriggerClick,  this);
15867             this.inputEl().addClass('x-combo-noedit');
15868         }else{
15869             this.inputEl().dom.setAttribute('readOnly', false);
15870             this.inputEl().un('mousedown', this.onTriggerClick,  this);
15871             this.inputEl().removeClass('x-combo-noedit');
15872         }
15873     },
15874
15875     // private
15876     
15877     onBeforeLoad : function(combo,opts){
15878         if(!this.hasFocus){
15879             return;
15880         }
15881          if (!opts.add) {
15882             this.list.dom.innerHTML = '<li class="loading-indicator">'+(this.loadingText||'loading')+'</li>' ;
15883          }
15884         this.restrictHeight();
15885         this.selectedIndex = -1;
15886     },
15887
15888     // private
15889     onLoad : function(){
15890         
15891         this.hasQuery = false;
15892         
15893         if(!this.hasFocus){
15894             return;
15895         }
15896         
15897         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
15898             this.loading.hide();
15899         }
15900         
15901         if(this.store.getCount() > 0){
15902             
15903             this.expand();
15904             this.restrictHeight();
15905             if(this.lastQuery == this.allQuery){
15906                 if(this.editable && !this.tickable){
15907                     this.inputEl().dom.select();
15908                 }
15909                 
15910                 if(
15911                     !this.selectByValue(this.value, true) &&
15912                     this.autoFocus && 
15913                     (
15914                         !this.store.lastOptions ||
15915                         typeof(this.store.lastOptions.add) == 'undefined' || 
15916                         this.store.lastOptions.add != true
15917                     )
15918                 ){
15919                     this.select(0, true);
15920                 }
15921             }else{
15922                 if(this.autoFocus){
15923                     this.selectNext();
15924                 }
15925                 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
15926                     this.taTask.delay(this.typeAheadDelay);
15927                 }
15928             }
15929         }else{
15930             this.onEmptyResults();
15931         }
15932         
15933         //this.el.focus();
15934     },
15935     // private
15936     onLoadException : function()
15937     {
15938         this.hasQuery = false;
15939         
15940         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
15941             this.loading.hide();
15942         }
15943         
15944         if(this.tickable && this.editable){
15945             return;
15946         }
15947         
15948         this.collapse();
15949         // only causes errors at present
15950         //Roo.log(this.store.reader.jsonData);
15951         //if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
15952             // fixme
15953             //Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
15954         //}
15955         
15956         
15957     },
15958     // private
15959     onTypeAhead : function(){
15960         if(this.store.getCount() > 0){
15961             var r = this.store.getAt(0);
15962             var newValue = r.data[this.displayField];
15963             var len = newValue.length;
15964             var selStart = this.getRawValue().length;
15965             
15966             if(selStart != len){
15967                 this.setRawValue(newValue);
15968                 this.selectText(selStart, newValue.length);
15969             }
15970         }
15971     },
15972
15973     // private
15974     onSelect : function(record, index){
15975         
15976         if(this.fireEvent('beforeselect', this, record, index) !== false){
15977         
15978             this.setFromData(index > -1 ? record.data : false);
15979             
15980             this.collapse();
15981             this.fireEvent('select', this, record, index);
15982         }
15983     },
15984
15985     /**
15986      * Returns the currently selected field value or empty string if no value is set.
15987      * @return {String} value The selected value
15988      */
15989     getValue : function()
15990     {
15991         if(Roo.isIOS && this.useNativeIOS){
15992             return this.ios_options[this.inputEl().dom.selectedIndex].data[this.valueField];
15993         }
15994         
15995         if(this.multiple){
15996             return (this.hiddenField) ? this.hiddenField.dom.value : this.value;
15997         }
15998         
15999         if(this.valueField){
16000             return typeof this.value != 'undefined' ? this.value : '';
16001         }else{
16002             return Roo.bootstrap.ComboBox.superclass.getValue.call(this);
16003         }
16004     },
16005     
16006     getRawValue : function()
16007     {
16008         if(Roo.isIOS && this.useNativeIOS){
16009             return this.ios_options[this.inputEl().dom.selectedIndex].data[this.displayField];
16010         }
16011         
16012         var v = this.inputEl().getValue();
16013         
16014         return v;
16015     },
16016
16017     /**
16018      * Clears any text/value currently set in the field
16019      */
16020     clearValue : function(){
16021         
16022         if(this.hiddenField){
16023             this.hiddenField.dom.value = '';
16024         }
16025         this.value = '';
16026         this.setRawValue('');
16027         this.lastSelectionText = '';
16028         this.lastData = false;
16029         
16030         var close = this.closeTriggerEl();
16031         
16032         if(close){
16033             close.hide();
16034         }
16035         
16036         this.validate();
16037         
16038     },
16039
16040     /**
16041      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
16042      * will be displayed in the field.  If the value does not match the data value of an existing item,
16043      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
16044      * Otherwise the field will be blank (although the value will still be set).
16045      * @param {String} value The value to match
16046      */
16047     setValue : function(v)
16048     {
16049         if(Roo.isIOS && this.useNativeIOS){
16050             this.setIOSValue(v);
16051             return;
16052         }
16053         
16054         if(this.multiple){
16055             this.syncValue();
16056             return;
16057         }
16058         
16059         var text = v;
16060         if(this.valueField){
16061             var r = this.findRecord(this.valueField, v);
16062             if(r){
16063                 text = r.data[this.displayField];
16064             }else if(this.valueNotFoundText !== undefined){
16065                 text = this.valueNotFoundText;
16066             }
16067         }
16068         this.lastSelectionText = text;
16069         if(this.hiddenField){
16070             this.hiddenField.dom.value = v;
16071         }
16072         Roo.bootstrap.ComboBox.superclass.setValue.call(this, text);
16073         this.value = v;
16074         
16075         var close = this.closeTriggerEl();
16076         
16077         if(close){
16078             (v && (v.length || v * 1 > 0)) ? close.show() : close.hide();
16079         }
16080         
16081         this.validate();
16082     },
16083     /**
16084      * @property {Object} the last set data for the element
16085      */
16086     
16087     lastData : false,
16088     /**
16089      * Sets the value of the field based on a object which is related to the record format for the store.
16090      * @param {Object} value the value to set as. or false on reset?
16091      */
16092     setFromData : function(o){
16093         
16094         if(this.multiple){
16095             this.addItem(o);
16096             return;
16097         }
16098             
16099         var dv = ''; // display value
16100         var vv = ''; // value value..
16101         this.lastData = o;
16102         if (this.displayField) {
16103             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
16104         } else {
16105             // this is an error condition!!!
16106             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
16107         }
16108         
16109         if(this.valueField){
16110             vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
16111         }
16112         
16113         var close = this.closeTriggerEl();
16114         
16115         if(close){
16116             if(dv.length || vv * 1 > 0){
16117                 close.show() ;
16118                 this.blockFocus=true;
16119             } else {
16120                 close.hide();
16121             }             
16122         }
16123         
16124         if(this.hiddenField){
16125             this.hiddenField.dom.value = vv;
16126             
16127             this.lastSelectionText = dv;
16128             Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
16129             this.value = vv;
16130             return;
16131         }
16132         // no hidden field.. - we store the value in 'value', but still display
16133         // display field!!!!
16134         this.lastSelectionText = dv;
16135         Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
16136         this.value = vv;
16137         
16138         
16139         
16140     },
16141     // private
16142     reset : function(){
16143         // overridden so that last data is reset..
16144         
16145         if(this.multiple){
16146             this.clearItem();
16147             return;
16148         }
16149         
16150         this.setValue(this.originalValue);
16151         //this.clearInvalid();
16152         this.lastData = false;
16153         if (this.view) {
16154             this.view.clearSelections();
16155         }
16156         
16157         this.validate();
16158     },
16159     // private
16160     findRecord : function(prop, value){
16161         var record;
16162         if(this.store.getCount() > 0){
16163             this.store.each(function(r){
16164                 if(r.data[prop] == value){
16165                     record = r;
16166                     return false;
16167                 }
16168                 return true;
16169             });
16170         }
16171         return record;
16172     },
16173     
16174     getName: function()
16175     {
16176         // returns hidden if it's set..
16177         if (!this.rendered) {return ''};
16178         return !this.hiddenName && this.inputEl().dom.name  ? this.inputEl().dom.name : (this.hiddenName || '');
16179         
16180     },
16181     // private
16182     onViewMove : function(e, t){
16183         this.inKeyMode = false;
16184     },
16185
16186     // private
16187     onViewOver : function(e, t){
16188         if(this.inKeyMode){ // prevent key nav and mouse over conflicts
16189             return;
16190         }
16191         var item = this.view.findItemFromChild(t);
16192         
16193         if(item){
16194             var index = this.view.indexOf(item);
16195             this.select(index, false);
16196         }
16197     },
16198
16199     // private
16200     onViewClick : function(view, doFocus, el, e)
16201     {
16202         var index = this.view.getSelectedIndexes()[0];
16203         
16204         var r = this.store.getAt(index);
16205         
16206         if(this.tickable){
16207             
16208             if(typeof(e) != 'undefined' && e.getTarget().nodeName.toLowerCase() != 'input'){
16209                 return;
16210             }
16211             
16212             var rm = false;
16213             var _this = this;
16214             
16215             Roo.each(this.tickItems, function(v,k){
16216                 
16217                 if(typeof(v) != 'undefined' && v[_this.valueField] == r.data[_this.valueField]){
16218                     Roo.log(v);
16219                     _this.tickItems.splice(k, 1);
16220                     
16221                     if(typeof(e) == 'undefined' && view == false){
16222                         Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = false;
16223                     }
16224                     
16225                     rm = true;
16226                     return;
16227                 }
16228             });
16229             
16230             if(rm){
16231                 return;
16232             }
16233             
16234             if(this.fireEvent('tick', this, r, index, Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked) !== false){
16235                 this.tickItems.push(r.data);
16236             }
16237             
16238             if(typeof(e) == 'undefined' && view == false){
16239                 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = true;
16240             }
16241                     
16242             return;
16243         }
16244         
16245         if(r){
16246             this.onSelect(r, index);
16247         }
16248         if(doFocus !== false && !this.blockFocus){
16249             this.inputEl().focus();
16250         }
16251     },
16252
16253     // private
16254     restrictHeight : function(){
16255         //this.innerList.dom.style.height = '';
16256         //var inner = this.innerList.dom;
16257         //var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
16258         //this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
16259         //this.list.beginUpdate();
16260         //this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
16261         this.list.alignTo(this.inputEl(), this.listAlign);
16262         this.list.alignTo(this.inputEl(), this.listAlign);
16263         //this.list.endUpdate();
16264     },
16265
16266     // private
16267     onEmptyResults : function(){
16268         
16269         if(this.tickable && this.editable){
16270             this.hasFocus = false;
16271             this.restrictHeight();
16272             return;
16273         }
16274         
16275         this.collapse();
16276     },
16277
16278     /**
16279      * Returns true if the dropdown list is expanded, else false.
16280      */
16281     isExpanded : function(){
16282         return this.list.isVisible();
16283     },
16284
16285     /**
16286      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
16287      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
16288      * @param {String} value The data value of the item to select
16289      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
16290      * selected item if it is not currently in view (defaults to true)
16291      * @return {Boolean} True if the value matched an item in the list, else false
16292      */
16293     selectByValue : function(v, scrollIntoView){
16294         if(v !== undefined && v !== null){
16295             var r = this.findRecord(this.valueField || this.displayField, v);
16296             if(r){
16297                 this.select(this.store.indexOf(r), scrollIntoView);
16298                 return true;
16299             }
16300         }
16301         return false;
16302     },
16303
16304     /**
16305      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
16306      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
16307      * @param {Number} index The zero-based index of the list item to select
16308      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
16309      * selected item if it is not currently in view (defaults to true)
16310      */
16311     select : function(index, scrollIntoView){
16312         this.selectedIndex = index;
16313         this.view.select(index);
16314         if(scrollIntoView !== false){
16315             var el = this.view.getNode(index);
16316             /*
16317              * el && !this.multiple && !this.tickable // not sure why we disable multiple before..
16318              */
16319             if(el){
16320                 this.list.scrollChildIntoView(el, false);
16321             }
16322         }
16323     },
16324
16325     // private
16326     selectNext : function(){
16327         var ct = this.store.getCount();
16328         if(ct > 0){
16329             if(this.selectedIndex == -1){
16330                 this.select(0);
16331             }else if(this.selectedIndex < ct-1){
16332                 this.select(this.selectedIndex+1);
16333             }
16334         }
16335     },
16336
16337     // private
16338     selectPrev : function(){
16339         var ct = this.store.getCount();
16340         if(ct > 0){
16341             if(this.selectedIndex == -1){
16342                 this.select(0);
16343             }else if(this.selectedIndex != 0){
16344                 this.select(this.selectedIndex-1);
16345             }
16346         }
16347     },
16348
16349     // private
16350     onKeyUp : function(e){
16351         if(this.editable !== false && !e.isSpecialKey()){
16352             this.lastKey = e.getKey();
16353             this.dqTask.delay(this.queryDelay);
16354         }
16355     },
16356
16357     // private
16358     validateBlur : function(){
16359         return !this.list || !this.list.isVisible();   
16360     },
16361
16362     // private
16363     initQuery : function(){
16364         
16365         var v = this.getRawValue();
16366         
16367         if(this.tickable && this.editable){
16368             v = this.tickableInputEl().getValue();
16369         }
16370         
16371         this.doQuery(v);
16372     },
16373
16374     // private
16375     doForce : function(){
16376         if(this.inputEl().dom.value.length > 0){
16377             this.inputEl().dom.value =
16378                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
16379              
16380         }
16381     },
16382
16383     /**
16384      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
16385      * query allowing the query action to be canceled if needed.
16386      * @param {String} query The SQL query to execute
16387      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
16388      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
16389      * saved in the current store (defaults to false)
16390      */
16391     doQuery : function(q, forceAll){
16392         
16393         if(q === undefined || q === null){
16394             q = '';
16395         }
16396         var qe = {
16397             query: q,
16398             forceAll: forceAll,
16399             combo: this,
16400             cancel:false
16401         };
16402         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
16403             return false;
16404         }
16405         q = qe.query;
16406         
16407         forceAll = qe.forceAll;
16408         if(forceAll === true || (q.length >= this.minChars)){
16409             
16410             this.hasQuery = true;
16411             
16412             if(this.lastQuery != q || this.alwaysQuery){
16413                 this.lastQuery = q;
16414                 if(this.mode == 'local'){
16415                     this.selectedIndex = -1;
16416                     if(forceAll){
16417                         this.store.clearFilter();
16418                     }else{
16419                         
16420                         if(this.specialFilter){
16421                             this.fireEvent('specialfilter', this);
16422                             this.onLoad();
16423                             return;
16424                         }
16425                         
16426                         this.store.filter(this.displayField, q);
16427                     }
16428                     
16429                     this.store.fireEvent("datachanged", this.store);
16430                     
16431                     this.onLoad();
16432                     
16433                     
16434                 }else{
16435                     
16436                     this.store.baseParams[this.queryParam] = q;
16437                     
16438                     var options = {params : this.getParams(q)};
16439                     
16440                     if(this.loadNext){
16441                         options.add = true;
16442                         options.params.start = this.page * this.pageSize;
16443                     }
16444                     
16445                     this.store.load(options);
16446                     
16447                     /*
16448                      *  this code will make the page width larger, at the beginning, the list not align correctly, 
16449                      *  we should expand the list on onLoad
16450                      *  so command out it
16451                      */
16452 //                    this.expand();
16453                 }
16454             }else{
16455                 this.selectedIndex = -1;
16456                 this.onLoad();   
16457             }
16458         }
16459         
16460         this.loadNext = false;
16461     },
16462     
16463     // private
16464     getParams : function(q){
16465         var p = {};
16466         //p[this.queryParam] = q;
16467         
16468         if(this.pageSize){
16469             p.start = 0;
16470             p.limit = this.pageSize;
16471         }
16472         return p;
16473     },
16474
16475     /**
16476      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
16477      */
16478     collapse : function(){
16479         if(!this.isExpanded()){
16480             return;
16481         }
16482         
16483         this.list.hide();
16484         
16485         this.hasFocus = false;
16486         
16487         if(this.tickable){
16488             this.okBtn.hide();
16489             this.cancelBtn.hide();
16490             this.trigger.show();
16491             
16492             if(this.editable){
16493                 this.tickableInputEl().dom.value = '';
16494                 this.tickableInputEl().blur();
16495             }
16496             
16497         }
16498         
16499         Roo.get(document).un('mousedown', this.collapseIf, this);
16500         Roo.get(document).un('mousewheel', this.collapseIf, this);
16501         if (!this.editable) {
16502             Roo.get(document).un('keydown', this.listKeyPress, this);
16503         }
16504         this.fireEvent('collapse', this);
16505         
16506         this.validate();
16507     },
16508
16509     // private
16510     collapseIf : function(e){
16511         var in_combo  = e.within(this.el);
16512         var in_list =  e.within(this.list);
16513         var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
16514         
16515         if (in_combo || in_list || is_list) {
16516             //e.stopPropagation();
16517             return;
16518         }
16519         
16520         if(this.tickable){
16521             this.onTickableFooterButtonClick(e, false, false);
16522         }
16523
16524         this.collapse();
16525         
16526     },
16527
16528     /**
16529      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
16530      */
16531     expand : function(){
16532        
16533         if(this.isExpanded() || !this.hasFocus){
16534             return;
16535         }
16536         
16537         var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
16538         this.list.setWidth(lw);
16539         
16540         Roo.log('expand');
16541         
16542         this.list.show();
16543         
16544         this.restrictHeight();
16545         
16546         if(this.tickable){
16547             
16548             this.tickItems = Roo.apply([], this.item);
16549             
16550             this.okBtn.show();
16551             this.cancelBtn.show();
16552             this.trigger.hide();
16553             
16554             if(this.editable){
16555                 this.tickableInputEl().focus();
16556             }
16557             
16558         }
16559         
16560         Roo.get(document).on('mousedown', this.collapseIf, this);
16561         Roo.get(document).on('mousewheel', this.collapseIf, this);
16562         if (!this.editable) {
16563             Roo.get(document).on('keydown', this.listKeyPress, this);
16564         }
16565         
16566         this.fireEvent('expand', this);
16567     },
16568
16569     // private
16570     // Implements the default empty TriggerField.onTriggerClick function
16571     onTriggerClick : function(e)
16572     {
16573         Roo.log('trigger click');
16574         
16575         if(this.disabled || !this.triggerList){
16576             return;
16577         }
16578         
16579         this.page = 0;
16580         this.loadNext = false;
16581         
16582         if(this.isExpanded()){
16583             this.collapse();
16584             if (!this.blockFocus) {
16585                 this.inputEl().focus();
16586             }
16587             
16588         }else {
16589             this.hasFocus = true;
16590             if(this.triggerAction == 'all') {
16591                 this.doQuery(this.allQuery, true);
16592             } else {
16593                 this.doQuery(this.getRawValue());
16594             }
16595             if (!this.blockFocus) {
16596                 this.inputEl().focus();
16597             }
16598         }
16599     },
16600     
16601     onTickableTriggerClick : function(e)
16602     {
16603         if(this.disabled){
16604             return;
16605         }
16606         
16607         this.page = 0;
16608         this.loadNext = false;
16609         this.hasFocus = true;
16610         
16611         if(this.triggerAction == 'all') {
16612             this.doQuery(this.allQuery, true);
16613         } else {
16614             this.doQuery(this.getRawValue());
16615         }
16616     },
16617     
16618     onSearchFieldClick : function(e)
16619     {
16620         if(this.hasFocus && !this.disabled && e.getTarget().nodeName.toLowerCase() != 'button'){
16621             this.onTickableFooterButtonClick(e, false, false);
16622             return;
16623         }
16624         
16625         if(this.hasFocus || this.disabled || e.getTarget().nodeName.toLowerCase() == 'button'){
16626             return;
16627         }
16628         
16629         this.page = 0;
16630         this.loadNext = false;
16631         this.hasFocus = true;
16632         
16633         if(this.triggerAction == 'all') {
16634             this.doQuery(this.allQuery, true);
16635         } else {
16636             this.doQuery(this.getRawValue());
16637         }
16638     },
16639     
16640     listKeyPress : function(e)
16641     {
16642         //Roo.log('listkeypress');
16643         // scroll to first matching element based on key pres..
16644         if (e.isSpecialKey()) {
16645             return false;
16646         }
16647         var k = String.fromCharCode(e.getKey()).toUpperCase();
16648         //Roo.log(k);
16649         var match  = false;
16650         var csel = this.view.getSelectedNodes();
16651         var cselitem = false;
16652         if (csel.length) {
16653             var ix = this.view.indexOf(csel[0]);
16654             cselitem  = this.store.getAt(ix);
16655             if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
16656                 cselitem = false;
16657             }
16658             
16659         }
16660         
16661         this.store.each(function(v) { 
16662             if (cselitem) {
16663                 // start at existing selection.
16664                 if (cselitem.id == v.id) {
16665                     cselitem = false;
16666                 }
16667                 return true;
16668             }
16669                 
16670             if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
16671                 match = this.store.indexOf(v);
16672                 return false;
16673             }
16674             return true;
16675         }, this);
16676         
16677         if (match === false) {
16678             return true; // no more action?
16679         }
16680         // scroll to?
16681         this.view.select(match);
16682         var sn = Roo.get(this.view.getSelectedNodes()[0]);
16683         sn.scrollIntoView(sn.dom.parentNode, false);
16684     },
16685     
16686     onViewScroll : function(e, t){
16687         
16688         if(this.view.el.getScroll().top == 0 ||this.view.el.getScroll().top < this.view.el.dom.scrollHeight - this.view.el.dom.clientHeight || !this.hasFocus || !this.append || this.hasQuery){
16689             return;
16690         }
16691         
16692         this.hasQuery = true;
16693         
16694         this.loading = this.list.select('.loading', true).first();
16695         
16696         if(this.loading === null){
16697             this.list.createChild({
16698                 tag: 'div',
16699                 cls: 'loading roo-select2-more-results roo-select2-active',
16700                 html: 'Loading more results...'
16701             });
16702             
16703             this.loading = this.list.select('.loading', true).first();
16704             
16705             this.loading.setVisibilityMode(Roo.Element.DISPLAY);
16706             
16707             this.loading.hide();
16708         }
16709         
16710         this.loading.show();
16711         
16712         var _combo = this;
16713         
16714         this.page++;
16715         this.loadNext = true;
16716         
16717         (function() { _combo.doQuery(_combo.allQuery, true); }).defer(500);
16718         
16719         return;
16720     },
16721     
16722     addItem : function(o)
16723     {   
16724         var dv = ''; // display value
16725         
16726         if (this.displayField) {
16727             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
16728         } else {
16729             // this is an error condition!!!
16730             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
16731         }
16732         
16733         if(!dv.length){
16734             return;
16735         }
16736         
16737         var choice = this.choices.createChild({
16738             tag: 'li',
16739             cls: 'roo-select2-search-choice',
16740             cn: [
16741                 {
16742                     tag: 'div',
16743                     html: dv
16744                 },
16745                 {
16746                     tag: 'a',
16747                     href: '#',
16748                     cls: 'roo-select2-search-choice-close fa fa-times',
16749                     tabindex: '-1'
16750                 }
16751             ]
16752             
16753         }, this.searchField);
16754         
16755         var close = choice.select('a.roo-select2-search-choice-close', true).first();
16756         
16757         close.on('click', this.onRemoveItem, this, { item : choice, data : o} );
16758         
16759         this.item.push(o);
16760         
16761         this.lastData = o;
16762         
16763         this.syncValue();
16764         
16765         this.inputEl().dom.value = '';
16766         
16767         this.validate();
16768     },
16769     
16770     onRemoveItem : function(e, _self, o)
16771     {
16772         e.preventDefault();
16773         
16774         this.lastItem = Roo.apply([], this.item);
16775         
16776         var index = this.item.indexOf(o.data) * 1;
16777         
16778         if( index < 0){
16779             Roo.log('not this item?!');
16780             return;
16781         }
16782         
16783         this.item.splice(index, 1);
16784         o.item.remove();
16785         
16786         this.syncValue();
16787         
16788         this.fireEvent('remove', this, e);
16789         
16790         this.validate();
16791         
16792     },
16793     
16794     syncValue : function()
16795     {
16796         if(!this.item.length){
16797             this.clearValue();
16798             return;
16799         }
16800             
16801         var value = [];
16802         var _this = this;
16803         Roo.each(this.item, function(i){
16804             if(_this.valueField){
16805                 value.push(i[_this.valueField]);
16806                 return;
16807             }
16808
16809             value.push(i);
16810         });
16811
16812         this.value = value.join(',');
16813
16814         if(this.hiddenField){
16815             this.hiddenField.dom.value = this.value;
16816         }
16817         
16818         this.store.fireEvent("datachanged", this.store);
16819         
16820         this.validate();
16821     },
16822     
16823     clearItem : function()
16824     {
16825         if(!this.multiple){
16826             return;
16827         }
16828         
16829         this.item = [];
16830         
16831         Roo.each(this.choices.select('>li.roo-select2-search-choice', true).elements, function(c){
16832            c.remove();
16833         });
16834         
16835         this.syncValue();
16836         
16837         this.validate();
16838         
16839         if(this.tickable && !Roo.isTouch){
16840             this.view.refresh();
16841         }
16842     },
16843     
16844     inputEl: function ()
16845     {
16846         if(Roo.isIOS && this.useNativeIOS){
16847             return this.el.select('select.roo-ios-select', true).first();
16848         }
16849         
16850         if(Roo.isTouch && this.mobileTouchView){
16851             return this.el.select('input.form-control',true).first();
16852         }
16853         
16854         if(this.tickable){
16855             return this.searchField;
16856         }
16857         
16858         return this.el.select('input.form-control',true).first();
16859     },
16860     
16861     onTickableFooterButtonClick : function(e, btn, el)
16862     {
16863         e.preventDefault();
16864         
16865         this.lastItem = Roo.apply([], this.item);
16866         
16867         if(btn && btn.name == 'cancel'){
16868             this.tickItems = Roo.apply([], this.item);
16869             this.collapse();
16870             return;
16871         }
16872         
16873         this.clearItem();
16874         
16875         var _this = this;
16876         
16877         Roo.each(this.tickItems, function(o){
16878             _this.addItem(o);
16879         });
16880         
16881         this.collapse();
16882         
16883     },
16884     
16885     validate : function()
16886     {
16887         if(this.getVisibilityEl().hasClass('hidden')){
16888             return true;
16889         }
16890         
16891         var v = this.getRawValue();
16892         
16893         if(this.multiple){
16894             v = this.getValue();
16895         }
16896         
16897         if(this.disabled || this.allowBlank || v.length){
16898             this.markValid();
16899             return true;
16900         }
16901         
16902         this.markInvalid();
16903         return false;
16904     },
16905     
16906     tickableInputEl : function()
16907     {
16908         if(!this.tickable || !this.editable){
16909             return this.inputEl();
16910         }
16911         
16912         return this.inputEl().select('.roo-select2-search-field-input', true).first();
16913     },
16914     
16915     
16916     getAutoCreateTouchView : function()
16917     {
16918         var id = Roo.id();
16919         
16920         var cfg = {
16921             cls: 'form-group' //input-group
16922         };
16923         
16924         var input =  {
16925             tag: 'input',
16926             id : id,
16927             type : this.inputType,
16928             cls : 'form-control x-combo-noedit',
16929             autocomplete: 'new-password',
16930             placeholder : this.placeholder || '',
16931             readonly : true
16932         };
16933         
16934         if (this.name) {
16935             input.name = this.name;
16936         }
16937         
16938         if (this.size) {
16939             input.cls += ' input-' + this.size;
16940         }
16941         
16942         if (this.disabled) {
16943             input.disabled = true;
16944         }
16945         
16946         var inputblock = {
16947             cls : 'roo-combobox-wrap',
16948             cn : [
16949                 input
16950             ]
16951         };
16952         
16953         if(this.before){
16954             inputblock.cls += ' input-group';
16955             
16956             inputblock.cn.unshift({
16957                 tag :'span',
16958                 cls : 'input-group-addon input-group-prepend input-group-text',
16959                 html : this.before
16960             });
16961         }
16962         
16963         if(this.removable && !this.multiple){
16964             inputblock.cls += ' roo-removable';
16965             
16966             inputblock.cn.push({
16967                 tag: 'button',
16968                 html : 'x',
16969                 cls : 'roo-combo-removable-btn close'
16970             });
16971         }
16972
16973         if(this.hasFeedback && !this.allowBlank){
16974             
16975             inputblock.cls += ' has-feedback';
16976             
16977             inputblock.cn.push({
16978                 tag: 'span',
16979                 cls: 'glyphicon form-control-feedback'
16980             });
16981             
16982         }
16983         
16984         if (this.after) {
16985             
16986             inputblock.cls += (this.before) ? '' : ' input-group';
16987             
16988             inputblock.cn.push({
16989                 tag :'span',
16990                 cls : 'input-group-addon input-group-append input-group-text',
16991                 html : this.after
16992             });
16993         }
16994
16995         
16996         var ibwrap = inputblock;
16997         
16998         if(this.multiple){
16999             ibwrap = {
17000                 tag: 'ul',
17001                 cls: 'roo-select2-choices',
17002                 cn:[
17003                     {
17004                         tag: 'li',
17005                         cls: 'roo-select2-search-field',
17006                         cn: [
17007
17008                             inputblock
17009                         ]
17010                     }
17011                 ]
17012             };
17013         
17014             
17015         }
17016         
17017         var combobox = {
17018             cls: 'roo-select2-container input-group roo-touchview-combobox ',
17019             cn: [
17020                 {
17021                     tag: 'input',
17022                     type : 'hidden',
17023                     cls: 'form-hidden-field'
17024                 },
17025                 ibwrap
17026             ]
17027         };
17028         
17029         if(!this.multiple && this.showToggleBtn){
17030             
17031             var caret = {
17032                 cls: 'caret'
17033             };
17034             
17035             if (this.caret != false) {
17036                 caret = {
17037                      tag: 'i',
17038                      cls: 'fa fa-' + this.caret
17039                 };
17040                 
17041             }
17042             
17043             combobox.cn.push({
17044                 tag :'span',
17045                 cls : 'input-group-addon input-group-append input-group-text btn dropdown-toggle',
17046                 cn : [
17047                     Roo.bootstrap.version == 3 ? caret : '',
17048                     {
17049                         tag: 'span',
17050                         cls: 'combobox-clear',
17051                         cn  : [
17052                             {
17053                                 tag : 'i',
17054                                 cls: 'icon-remove'
17055                             }
17056                         ]
17057                     }
17058                 ]
17059
17060             })
17061         }
17062         
17063         if(this.multiple){
17064             combobox.cls += ' roo-select2-container-multi';
17065         }
17066         
17067         var align = this.labelAlign || this.parentLabelAlign();
17068         
17069         if (align ==='left' && this.fieldLabel.length) {
17070
17071             cfg.cn = [
17072                 {
17073                    tag : 'i',
17074                    cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
17075                    tooltip : 'This field is required'
17076                 },
17077                 {
17078                     tag: 'label',
17079                     cls : 'control-label col-form-label',
17080                     html : this.fieldLabel
17081
17082                 },
17083                 {
17084                     cls : 'roo-combobox-wrap ', 
17085                     cn: [
17086                         combobox
17087                     ]
17088                 }
17089             ];
17090             
17091             var labelCfg = cfg.cn[1];
17092             var contentCfg = cfg.cn[2];
17093             
17094
17095             if(this.indicatorpos == 'right'){
17096                 cfg.cn = [
17097                     {
17098                         tag: 'label',
17099                         'for' :  id,
17100                         cls : 'control-label col-form-label',
17101                         cn : [
17102                             {
17103                                 tag : 'span',
17104                                 html : this.fieldLabel
17105                             },
17106                             {
17107                                 tag : 'i',
17108                                 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
17109                                 tooltip : 'This field is required'
17110                             }
17111                         ]
17112                     },
17113                     {
17114                         cls : "roo-combobox-wrap ",
17115                         cn: [
17116                             combobox
17117                         ]
17118                     }
17119
17120                 ];
17121                 
17122                 labelCfg = cfg.cn[0];
17123                 contentCfg = cfg.cn[1];
17124             }
17125             
17126            
17127             
17128             if(this.labelWidth > 12){
17129                 labelCfg.style = "width: " + this.labelWidth + 'px';
17130             }
17131            
17132             if(this.labelWidth < 13 && this.labelmd == 0){
17133                 this.labelmd = this.labelWidth;
17134             }
17135             
17136             if(this.labellg > 0){
17137                 labelCfg.cls += ' col-lg-' + this.labellg;
17138                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
17139             }
17140             
17141             if(this.labelmd > 0){
17142                 labelCfg.cls += ' col-md-' + this.labelmd;
17143                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
17144             }
17145             
17146             if(this.labelsm > 0){
17147                 labelCfg.cls += ' col-sm-' + this.labelsm;
17148                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
17149             }
17150             
17151             if(this.labelxs > 0){
17152                 labelCfg.cls += ' col-xs-' + this.labelxs;
17153                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
17154             }
17155                 
17156                 
17157         } else if ( this.fieldLabel.length) {
17158             cfg.cn = [
17159                 {
17160                    tag : 'i',
17161                    cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
17162                    tooltip : 'This field is required'
17163                 },
17164                 {
17165                     tag: 'label',
17166                     cls : 'control-label',
17167                     html : this.fieldLabel
17168
17169                 },
17170                 {
17171                     cls : '', 
17172                     cn: [
17173                         combobox
17174                     ]
17175                 }
17176             ];
17177             
17178             if(this.indicatorpos == 'right'){
17179                 cfg.cn = [
17180                     {
17181                         tag: 'label',
17182                         cls : 'control-label',
17183                         html : this.fieldLabel,
17184                         cn : [
17185                             {
17186                                tag : 'i',
17187                                cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
17188                                tooltip : 'This field is required'
17189                             }
17190                         ]
17191                     },
17192                     {
17193                         cls : '', 
17194                         cn: [
17195                             combobox
17196                         ]
17197                     }
17198                 ];
17199             }
17200         } else {
17201             cfg.cn = combobox;    
17202         }
17203         
17204         
17205         var settings = this;
17206         
17207         ['xs','sm','md','lg'].map(function(size){
17208             if (settings[size]) {
17209                 cfg.cls += ' col-' + size + '-' + settings[size];
17210             }
17211         });
17212         
17213         return cfg;
17214     },
17215     
17216     initTouchView : function()
17217     {
17218         this.renderTouchView();
17219         
17220         this.touchViewEl.on('scroll', function(){
17221             this.el.dom.scrollTop = 0;
17222         }, this);
17223         
17224         this.originalValue = this.getValue();
17225         
17226         this.triggerEl = this.el.select('span.dropdown-toggle',true).first();
17227         
17228         this.inputEl().on("click", this.showTouchView, this);
17229         if (this.triggerEl) {
17230             this.triggerEl.on("click", this.showTouchView, this);
17231         }
17232         
17233         
17234         this.touchViewFooterEl.select('.roo-touch-view-cancel', true).first().on('click', this.hideTouchView, this);
17235         this.touchViewFooterEl.select('.roo-touch-view-ok', true).first().on('click', this.setTouchViewValue, this);
17236         
17237         this.maskEl = new Roo.LoadMask(this.touchViewEl, { store : this.store, msgCls: 'roo-el-mask-msg' });
17238         
17239         this.store.on('beforeload', this.onTouchViewBeforeLoad, this);
17240         this.store.on('load', this.onTouchViewLoad, this);
17241         this.store.on('loadexception', this.onTouchViewLoadException, this);
17242         
17243         if(this.hiddenName){
17244             
17245             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
17246             
17247             this.hiddenField.dom.value =
17248                 this.hiddenValue !== undefined ? this.hiddenValue :
17249                 this.value !== undefined ? this.value : '';
17250         
17251             this.el.dom.removeAttribute('name');
17252             this.hiddenField.dom.setAttribute('name', this.hiddenName);
17253         }
17254         
17255         if(this.multiple){
17256             this.choices = this.el.select('ul.roo-select2-choices', true).first();
17257             this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
17258         }
17259         
17260         if(this.removable && !this.multiple){
17261             var close = this.closeTriggerEl();
17262             if(close){
17263                 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
17264                 close.on('click', this.removeBtnClick, this, close);
17265             }
17266         }
17267         /*
17268          * fix the bug in Safari iOS8
17269          */
17270         this.inputEl().on("focus", function(e){
17271             document.activeElement.blur();
17272         }, this);
17273         
17274         this._touchViewMask = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
17275         
17276         return;
17277         
17278         
17279     },
17280     
17281     renderTouchView : function()
17282     {
17283         this.touchViewEl = Roo.get(document.body).createChild(Roo.bootstrap.ComboBox.touchViewTemplate);
17284         this.touchViewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17285         
17286         this.touchViewHeaderEl = this.touchViewEl.select('.modal-header', true).first();
17287         this.touchViewHeaderEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17288         
17289         this.touchViewBodyEl = this.touchViewEl.select('.modal-body', true).first();
17290         this.touchViewBodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17291         this.touchViewBodyEl.setStyle('overflow', 'auto');
17292         
17293         this.touchViewListGroup = this.touchViewBodyEl.select('.list-group', true).first();
17294         this.touchViewListGroup.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17295         
17296         this.touchViewFooterEl = this.touchViewEl.select('.modal-footer', true).first();
17297         this.touchViewFooterEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17298         
17299     },
17300     
17301     showTouchView : function()
17302     {
17303         if(this.disabled){
17304             return;
17305         }
17306         
17307         this.touchViewHeaderEl.hide();
17308
17309         if(this.modalTitle.length){
17310             this.touchViewHeaderEl.dom.innerHTML = this.modalTitle;
17311             this.touchViewHeaderEl.show();
17312         }
17313
17314         this.touchViewEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
17315         this.touchViewEl.show();
17316
17317         this.touchViewEl.select('.modal-dialog', true).first().setStyle({ margin : '0px', width : '100%'});
17318         
17319         //this.touchViewEl.select('.modal-dialog > .modal-content', true).first().setSize(
17320         //        Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
17321
17322         var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
17323
17324         if(this.modalTitle.length){
17325             bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
17326         }
17327         
17328         this.touchViewBodyEl.setHeight(bodyHeight);
17329
17330         if(this.animate){
17331             var _this = this;
17332             (function(){ _this.touchViewEl.addClass(['in','show']); }).defer(50);
17333         }else{
17334             this.touchViewEl.addClass(['in','show']);
17335         }
17336         
17337         if(this._touchViewMask){
17338             Roo.get(document.body).addClass("x-body-masked");
17339             this._touchViewMask.setSize(Roo.lib.Dom.getViewWidth(true),   Roo.lib.Dom.getViewHeight(true));
17340             this._touchViewMask.setStyle('z-index', 10000);
17341             this._touchViewMask.addClass('show');
17342         }
17343         
17344         this.doTouchViewQuery();
17345         
17346     },
17347     
17348     hideTouchView : function()
17349     {
17350         this.touchViewEl.removeClass(['in','show']);
17351
17352         if(this.animate){
17353             var _this = this;
17354             (function(){ _this.touchViewEl.setStyle('display', 'none'); }).defer(150);
17355         }else{
17356             this.touchViewEl.setStyle('display', 'none');
17357         }
17358         
17359         if(this._touchViewMask){
17360             this._touchViewMask.removeClass('show');
17361             Roo.get(document.body).removeClass("x-body-masked");
17362         }
17363     },
17364     
17365     setTouchViewValue : function()
17366     {
17367         if(this.multiple){
17368             this.clearItem();
17369         
17370             var _this = this;
17371
17372             Roo.each(this.tickItems, function(o){
17373                 this.addItem(o);
17374             }, this);
17375         }
17376         
17377         this.hideTouchView();
17378     },
17379     
17380     doTouchViewQuery : function()
17381     {
17382         var qe = {
17383             query: '',
17384             forceAll: true,
17385             combo: this,
17386             cancel:false
17387         };
17388         
17389         if(this.fireEvent('beforequery', qe) ===false || qe.cancel){
17390             return false;
17391         }
17392         
17393         if(!this.alwaysQuery || this.mode == 'local'){
17394             this.onTouchViewLoad();
17395             return;
17396         }
17397         
17398         this.store.load();
17399     },
17400     
17401     onTouchViewBeforeLoad : function(combo,opts)
17402     {
17403         return;
17404     },
17405
17406     // private
17407     onTouchViewLoad : function()
17408     {
17409         if(this.store.getCount() < 1){
17410             this.onTouchViewEmptyResults();
17411             return;
17412         }
17413         
17414         this.clearTouchView();
17415         
17416         var rawValue = this.getRawValue();
17417         
17418         var template = (this.multiple) ? Roo.bootstrap.ComboBox.listItemCheckbox : Roo.bootstrap.ComboBox.listItemRadio;
17419         
17420         this.tickItems = [];
17421         
17422         this.store.data.each(function(d, rowIndex){
17423             var row = this.touchViewListGroup.createChild(template);
17424             
17425             if(typeof(d.data.cls) != 'undefined' && d.data.cls.length){
17426                 row.addClass(d.data.cls);
17427             }
17428             
17429             if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
17430                 var cfg = {
17431                     data : d.data,
17432                     html : d.data[this.displayField]
17433                 };
17434                 
17435                 if(this.fireEvent('touchviewdisplay', this, cfg) !== false){
17436                     row.select('.roo-combobox-list-group-item-value', true).first().dom.innerHTML = cfg.html;
17437                 }
17438             }
17439             row.removeClass('selected');
17440             if(!this.multiple && this.valueField &&
17441                     typeof(d.data[this.valueField]) != 'undefined' && d.data[this.valueField] == this.getValue())
17442             {
17443                 // radio buttons..
17444                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
17445                 row.addClass('selected');
17446             }
17447             
17448             if(this.multiple && this.valueField &&
17449                     typeof(d.data[this.valueField]) != 'undefined' && this.getValue().indexOf(d.data[this.valueField]) != -1)
17450             {
17451                 
17452                 // checkboxes...
17453                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
17454                 this.tickItems.push(d.data);
17455             }
17456             
17457             row.on('click', this.onTouchViewClick, this, {row : row, rowIndex : rowIndex});
17458             
17459         }, this);
17460         
17461         var firstChecked = this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).first();
17462         
17463         var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
17464
17465         if(this.modalTitle.length){
17466             bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
17467         }
17468
17469         var listHeight = this.touchViewListGroup.getHeight() + this.touchViewBodyEl.getPadding('tb') * 2;
17470         
17471         if(this.mobile_restrict_height && listHeight < bodyHeight){
17472             this.touchViewBodyEl.setHeight(listHeight);
17473         }
17474         
17475         var _this = this;
17476         
17477         if(firstChecked && listHeight > bodyHeight){
17478             (function() { firstChecked.findParent('li').scrollIntoView(_this.touchViewListGroup.dom); }).defer(500);
17479         }
17480         
17481     },
17482     
17483     onTouchViewLoadException : function()
17484     {
17485         this.hideTouchView();
17486     },
17487     
17488     onTouchViewEmptyResults : function()
17489     {
17490         this.clearTouchView();
17491         
17492         this.touchViewListGroup.createChild(Roo.bootstrap.ComboBox.emptyResult);
17493         
17494         this.touchViewListGroup.select('.roo-combobox-touch-view-empty-result', true).first().dom.innerHTML = this.emptyResultText;
17495         
17496     },
17497     
17498     clearTouchView : function()
17499     {
17500         this.touchViewListGroup.dom.innerHTML = '';
17501     },
17502     
17503     onTouchViewClick : function(e, el, o)
17504     {
17505         e.preventDefault();
17506         
17507         var row = o.row;
17508         var rowIndex = o.rowIndex;
17509         
17510         var r = this.store.getAt(rowIndex);
17511         
17512         if(this.fireEvent('beforeselect', this, r, rowIndex) !== false){
17513             
17514             if(!this.multiple){
17515                 Roo.each(this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).elements, function(c){
17516                     c.dom.removeAttribute('checked');
17517                 }, this);
17518
17519                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
17520
17521                 this.setFromData(r.data);
17522
17523                 var close = this.closeTriggerEl();
17524
17525                 if(close){
17526                     close.show();
17527                 }
17528
17529                 this.hideTouchView();
17530
17531                 this.fireEvent('select', this, r, rowIndex);
17532
17533                 return;
17534             }
17535
17536             if(this.valueField && typeof(r.data[this.valueField]) != 'undefined' && this.getValue().indexOf(r.data[this.valueField]) != -1){
17537                 row.select('.roo-combobox-list-group-item-box > input', true).first().dom.removeAttribute('checked');
17538                 this.tickItems.splice(this.tickItems.indexOf(r.data), 1);
17539                 return;
17540             }
17541
17542             row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
17543             this.addItem(r.data);
17544             this.tickItems.push(r.data);
17545         }
17546     },
17547     
17548     getAutoCreateNativeIOS : function()
17549     {
17550         var cfg = {
17551             cls: 'form-group' //input-group,
17552         };
17553         
17554         var combobox =  {
17555             tag: 'select',
17556             cls : 'roo-ios-select'
17557         };
17558         
17559         if (this.name) {
17560             combobox.name = this.name;
17561         }
17562         
17563         if (this.disabled) {
17564             combobox.disabled = true;
17565         }
17566         
17567         var settings = this;
17568         
17569         ['xs','sm','md','lg'].map(function(size){
17570             if (settings[size]) {
17571                 cfg.cls += ' col-' + size + '-' + settings[size];
17572             }
17573         });
17574         
17575         cfg.cn = combobox;
17576         
17577         return cfg;
17578         
17579     },
17580     
17581     initIOSView : function()
17582     {
17583         this.store.on('load', this.onIOSViewLoad, this);
17584         
17585         return;
17586     },
17587     
17588     onIOSViewLoad : function()
17589     {
17590         if(this.store.getCount() < 1){
17591             return;
17592         }
17593         
17594         this.clearIOSView();
17595         
17596         if(this.allowBlank) {
17597             
17598             var default_text = '-- SELECT --';
17599             
17600             if(this.placeholder.length){
17601                 default_text = this.placeholder;
17602             }
17603             
17604             if(this.emptyTitle.length){
17605                 default_text += ' - ' + this.emptyTitle + ' -';
17606             }
17607             
17608             var opt = this.inputEl().createChild({
17609                 tag: 'option',
17610                 value : 0,
17611                 html : default_text
17612             });
17613             
17614             var o = {};
17615             o[this.valueField] = 0;
17616             o[this.displayField] = default_text;
17617             
17618             this.ios_options.push({
17619                 data : o,
17620                 el : opt
17621             });
17622             
17623         }
17624         
17625         this.store.data.each(function(d, rowIndex){
17626             
17627             var html = '';
17628             
17629             if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
17630                 html = d.data[this.displayField];
17631             }
17632             
17633             var value = '';
17634             
17635             if(this.valueField && typeof(d.data[this.valueField]) != 'undefined'){
17636                 value = d.data[this.valueField];
17637             }
17638             
17639             var option = {
17640                 tag: 'option',
17641                 value : value,
17642                 html : html
17643             };
17644             
17645             if(this.value == d.data[this.valueField]){
17646                 option['selected'] = true;
17647             }
17648             
17649             var opt = this.inputEl().createChild(option);
17650             
17651             this.ios_options.push({
17652                 data : d.data,
17653                 el : opt
17654             });
17655             
17656         }, this);
17657         
17658         this.inputEl().on('change', function(){
17659            this.fireEvent('select', this);
17660         }, this);
17661         
17662     },
17663     
17664     clearIOSView: function()
17665     {
17666         this.inputEl().dom.innerHTML = '';
17667         
17668         this.ios_options = [];
17669     },
17670     
17671     setIOSValue: function(v)
17672     {
17673         this.value = v;
17674         
17675         if(!this.ios_options){
17676             return;
17677         }
17678         
17679         Roo.each(this.ios_options, function(opts){
17680            
17681            opts.el.dom.removeAttribute('selected');
17682            
17683            if(opts.data[this.valueField] != v){
17684                return;
17685            }
17686            
17687            opts.el.dom.setAttribute('selected', true);
17688            
17689         }, this);
17690     }
17691
17692     /** 
17693     * @cfg {Boolean} grow 
17694     * @hide 
17695     */
17696     /** 
17697     * @cfg {Number} growMin 
17698     * @hide 
17699     */
17700     /** 
17701     * @cfg {Number} growMax 
17702     * @hide 
17703     */
17704     /**
17705      * @hide
17706      * @method autoSize
17707      */
17708 });
17709
17710 Roo.apply(Roo.bootstrap.ComboBox,  {
17711     
17712     header : {
17713         tag: 'div',
17714         cls: 'modal-header',
17715         cn: [
17716             {
17717                 tag: 'h4',
17718                 cls: 'modal-title'
17719             }
17720         ]
17721     },
17722     
17723     body : {
17724         tag: 'div',
17725         cls: 'modal-body',
17726         cn: [
17727             {
17728                 tag: 'ul',
17729                 cls: 'list-group'
17730             }
17731         ]
17732     },
17733     
17734     listItemRadio : {
17735         tag: 'li',
17736         cls: 'list-group-item',
17737         cn: [
17738             {
17739                 tag: 'span',
17740                 cls: 'roo-combobox-list-group-item-value'
17741             },
17742             {
17743                 tag: 'div',
17744                 cls: 'roo-combobox-list-group-item-box pull-xs-right radio-inline radio radio-info',
17745                 cn: [
17746                     {
17747                         tag: 'input',
17748                         type: 'radio'
17749                     },
17750                     {
17751                         tag: 'label'
17752                     }
17753                 ]
17754             }
17755         ]
17756     },
17757     
17758     listItemCheckbox : {
17759         tag: 'li',
17760         cls: 'list-group-item',
17761         cn: [
17762             {
17763                 tag: 'span',
17764                 cls: 'roo-combobox-list-group-item-value'
17765             },
17766             {
17767                 tag: 'div',
17768                 cls: 'roo-combobox-list-group-item-box pull-xs-right checkbox-inline checkbox checkbox-info',
17769                 cn: [
17770                     {
17771                         tag: 'input',
17772                         type: 'checkbox'
17773                     },
17774                     {
17775                         tag: 'label'
17776                     }
17777                 ]
17778             }
17779         ]
17780     },
17781     
17782     emptyResult : {
17783         tag: 'div',
17784         cls: 'alert alert-danger roo-combobox-touch-view-empty-result'
17785     },
17786     
17787     footer : {
17788         tag: 'div',
17789         cls: 'modal-footer',
17790         cn: [
17791             {
17792                 tag: 'div',
17793                 cls: 'row',
17794                 cn: [
17795                     {
17796                         tag: 'div',
17797                         cls: 'col-xs-6 text-left',
17798                         cn: {
17799                             tag: 'button',
17800                             cls: 'btn btn-danger roo-touch-view-cancel',
17801                             html: 'Cancel'
17802                         }
17803                     },
17804                     {
17805                         tag: 'div',
17806                         cls: 'col-xs-6 text-right',
17807                         cn: {
17808                             tag: 'button',
17809                             cls: 'btn btn-success roo-touch-view-ok',
17810                             html: 'OK'
17811                         }
17812                     }
17813                 ]
17814             }
17815         ]
17816         
17817     }
17818 });
17819
17820 Roo.apply(Roo.bootstrap.ComboBox,  {
17821     
17822     touchViewTemplate : {
17823         tag: 'div',
17824         cls: 'modal fade roo-combobox-touch-view',
17825         cn: [
17826             {
17827                 tag: 'div',
17828                 cls: 'modal-dialog',
17829                 style : 'position:fixed', // we have to fix position....
17830                 cn: [
17831                     {
17832                         tag: 'div',
17833                         cls: 'modal-content',
17834                         cn: [
17835                             Roo.bootstrap.ComboBox.header,
17836                             Roo.bootstrap.ComboBox.body,
17837                             Roo.bootstrap.ComboBox.footer
17838                         ]
17839                     }
17840                 ]
17841             }
17842         ]
17843     }
17844 });/*
17845  * Based on:
17846  * Ext JS Library 1.1.1
17847  * Copyright(c) 2006-2007, Ext JS, LLC.
17848  *
17849  * Originally Released Under LGPL - original licence link has changed is not relivant.
17850  *
17851  * Fork - LGPL
17852  * <script type="text/javascript">
17853  */
17854
17855 /**
17856  * @class Roo.View
17857  * @extends Roo.util.Observable
17858  * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template. 
17859  * This class also supports single and multi selection modes. <br>
17860  * Create a data model bound view:
17861  <pre><code>
17862  var store = new Roo.data.Store(...);
17863
17864  var view = new Roo.View({
17865     el : "my-element",
17866     tpl : '&lt;div id="{0}"&gt;{2} - {1}&lt;/div&gt;', // auto create template
17867  
17868     singleSelect: true,
17869     selectedClass: "ydataview-selected",
17870     store: store
17871  });
17872
17873  // listen for node click?
17874  view.on("click", function(vw, index, node, e){
17875  alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
17876  });
17877
17878  // load XML data
17879  dataModel.load("foobar.xml");
17880  </code></pre>
17881  For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
17882  * <br><br>
17883  * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
17884  * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
17885  * 
17886  * Note: old style constructor is still suported (container, template, config)
17887  * 
17888  * @constructor
17889  * Create a new View
17890  * @param {Object} config The config object
17891  * 
17892  */
17893 Roo.View = function(config, depreciated_tpl, depreciated_config){
17894     
17895     this.parent = false;
17896     
17897     if (typeof(depreciated_tpl) == 'undefined') {
17898         // new way.. - universal constructor.
17899         Roo.apply(this, config);
17900         this.el  = Roo.get(this.el);
17901     } else {
17902         // old format..
17903         this.el  = Roo.get(config);
17904         this.tpl = depreciated_tpl;
17905         Roo.apply(this, depreciated_config);
17906     }
17907     this.wrapEl  = this.el.wrap().wrap();
17908     ///this.el = this.wrapEla.appendChild(document.createElement("div"));
17909     
17910     
17911     if(typeof(this.tpl) == "string"){
17912         this.tpl = new Roo.Template(this.tpl);
17913     } else {
17914         // support xtype ctors..
17915         this.tpl = new Roo.factory(this.tpl, Roo);
17916     }
17917     
17918     
17919     this.tpl.compile();
17920     
17921     /** @private */
17922     this.addEvents({
17923         /**
17924          * @event beforeclick
17925          * Fires before a click is processed. Returns false to cancel the default action.
17926          * @param {Roo.View} this
17927          * @param {Number} index The index of the target node
17928          * @param {HTMLElement} node The target node
17929          * @param {Roo.EventObject} e The raw event object
17930          */
17931             "beforeclick" : true,
17932         /**
17933          * @event click
17934          * Fires when a template node is clicked.
17935          * @param {Roo.View} this
17936          * @param {Number} index The index of the target node
17937          * @param {HTMLElement} node The target node
17938          * @param {Roo.EventObject} e The raw event object
17939          */
17940             "click" : true,
17941         /**
17942          * @event dblclick
17943          * Fires when a template node is double clicked.
17944          * @param {Roo.View} this
17945          * @param {Number} index The index of the target node
17946          * @param {HTMLElement} node The target node
17947          * @param {Roo.EventObject} e The raw event object
17948          */
17949             "dblclick" : true,
17950         /**
17951          * @event contextmenu
17952          * Fires when a template node is right clicked.
17953          * @param {Roo.View} this
17954          * @param {Number} index The index of the target node
17955          * @param {HTMLElement} node The target node
17956          * @param {Roo.EventObject} e The raw event object
17957          */
17958             "contextmenu" : true,
17959         /**
17960          * @event selectionchange
17961          * Fires when the selected nodes change.
17962          * @param {Roo.View} this
17963          * @param {Array} selections Array of the selected nodes
17964          */
17965             "selectionchange" : true,
17966     
17967         /**
17968          * @event beforeselect
17969          * Fires before a selection is made. If any handlers return false, the selection is cancelled.
17970          * @param {Roo.View} this
17971          * @param {HTMLElement} node The node to be selected
17972          * @param {Array} selections Array of currently selected nodes
17973          */
17974             "beforeselect" : true,
17975         /**
17976          * @event preparedata
17977          * Fires on every row to render, to allow you to change the data.
17978          * @param {Roo.View} this
17979          * @param {Object} data to be rendered (change this)
17980          */
17981           "preparedata" : true
17982           
17983           
17984         });
17985
17986
17987
17988     this.el.on({
17989         "click": this.onClick,
17990         "dblclick": this.onDblClick,
17991         "contextmenu": this.onContextMenu,
17992         scope:this
17993     });
17994
17995     this.selections = [];
17996     this.nodes = [];
17997     this.cmp = new Roo.CompositeElementLite([]);
17998     if(this.store){
17999         this.store = Roo.factory(this.store, Roo.data);
18000         this.setStore(this.store, true);
18001     }
18002     
18003     if ( this.footer && this.footer.xtype) {
18004            
18005          var fctr = this.wrapEl.appendChild(document.createElement("div"));
18006         
18007         this.footer.dataSource = this.store;
18008         this.footer.container = fctr;
18009         this.footer = Roo.factory(this.footer, Roo);
18010         fctr.insertFirst(this.el);
18011         
18012         // this is a bit insane - as the paging toolbar seems to detach the el..
18013 //        dom.parentNode.parentNode.parentNode
18014          // they get detached?
18015     }
18016     
18017     
18018     Roo.View.superclass.constructor.call(this);
18019     
18020     
18021 };
18022
18023 Roo.extend(Roo.View, Roo.util.Observable, {
18024     
18025      /**
18026      * @cfg {Roo.data.Store} store Data store to load data from.
18027      */
18028     store : false,
18029     
18030     /**
18031      * @cfg {String|Roo.Element} el The container element.
18032      */
18033     el : '',
18034     
18035     /**
18036      * @cfg {String|Roo.Template} tpl The template used by this View 
18037      */
18038     tpl : false,
18039     /**
18040      * @cfg {String} dataName the named area of the template to use as the data area
18041      *                          Works with domtemplates roo-name="name"
18042      */
18043     dataName: false,
18044     /**
18045      * @cfg {String} selectedClass The css class to add to selected nodes
18046      */
18047     selectedClass : "x-view-selected",
18048      /**
18049      * @cfg {String} emptyText The empty text to show when nothing is loaded.
18050      */
18051     emptyText : "",
18052     
18053     /**
18054      * @cfg {String} text to display on mask (default Loading)
18055      */
18056     mask : false,
18057     /**
18058      * @cfg {Boolean} multiSelect Allow multiple selection
18059      */
18060     multiSelect : false,
18061     /**
18062      * @cfg {Boolean} singleSelect Allow single selection
18063      */
18064     singleSelect:  false,
18065     
18066     /**
18067      * @cfg {Boolean} toggleSelect - selecting 
18068      */
18069     toggleSelect : false,
18070     
18071     /**
18072      * @cfg {Boolean} tickable - selecting 
18073      */
18074     tickable : false,
18075     
18076     /**
18077      * Returns the element this view is bound to.
18078      * @return {Roo.Element}
18079      */
18080     getEl : function(){
18081         return this.wrapEl;
18082     },
18083     
18084     
18085
18086     /**
18087      * Refreshes the view. - called by datachanged on the store. - do not call directly.
18088      */
18089     refresh : function(){
18090         //Roo.log('refresh');
18091         var t = this.tpl;
18092         
18093         // if we are using something like 'domtemplate', then
18094         // the what gets used is:
18095         // t.applySubtemplate(NAME, data, wrapping data..)
18096         // the outer template then get' applied with
18097         //     the store 'extra data'
18098         // and the body get's added to the
18099         //      roo-name="data" node?
18100         //      <span class='roo-tpl-{name}'></span> ?????
18101         
18102         
18103         
18104         this.clearSelections();
18105         this.el.update("");
18106         var html = [];
18107         var records = this.store.getRange();
18108         if(records.length < 1) {
18109             
18110             // is this valid??  = should it render a template??
18111             
18112             this.el.update(this.emptyText);
18113             return;
18114         }
18115         var el = this.el;
18116         if (this.dataName) {
18117             this.el.update(t.apply(this.store.meta)); //????
18118             el = this.el.child('.roo-tpl-' + this.dataName);
18119         }
18120         
18121         for(var i = 0, len = records.length; i < len; i++){
18122             var data = this.prepareData(records[i].data, i, records[i]);
18123             this.fireEvent("preparedata", this, data, i, records[i]);
18124             
18125             var d = Roo.apply({}, data);
18126             
18127             if(this.tickable){
18128                 Roo.apply(d, {'roo-id' : Roo.id()});
18129                 
18130                 var _this = this;
18131             
18132                 Roo.each(this.parent.item, function(item){
18133                     if(item[_this.parent.valueField] != data[_this.parent.valueField]){
18134                         return;
18135                     }
18136                     Roo.apply(d, {'roo-data-checked' : 'checked'});
18137                 });
18138             }
18139             
18140             html[html.length] = Roo.util.Format.trim(
18141                 this.dataName ?
18142                     t.applySubtemplate(this.dataName, d, this.store.meta) :
18143                     t.apply(d)
18144             );
18145         }
18146         
18147         
18148         
18149         el.update(html.join(""));
18150         this.nodes = el.dom.childNodes;
18151         this.updateIndexes(0);
18152     },
18153     
18154
18155     /**
18156      * Function to override to reformat the data that is sent to
18157      * the template for each node.
18158      * DEPRICATED - use the preparedata event handler.
18159      * @param {Array/Object} data The raw data (array of colData for a data model bound view or
18160      * a JSON object for an UpdateManager bound view).
18161      */
18162     prepareData : function(data, index, record)
18163     {
18164         this.fireEvent("preparedata", this, data, index, record);
18165         return data;
18166     },
18167
18168     onUpdate : function(ds, record){
18169         // Roo.log('on update');   
18170         this.clearSelections();
18171         var index = this.store.indexOf(record);
18172         var n = this.nodes[index];
18173         this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
18174         n.parentNode.removeChild(n);
18175         this.updateIndexes(index, index);
18176     },
18177
18178     
18179     
18180 // --------- FIXME     
18181     onAdd : function(ds, records, index)
18182     {
18183         //Roo.log(['on Add', ds, records, index] );        
18184         this.clearSelections();
18185         if(this.nodes.length == 0){
18186             this.refresh();
18187             return;
18188         }
18189         var n = this.nodes[index];
18190         for(var i = 0, len = records.length; i < len; i++){
18191             var d = this.prepareData(records[i].data, i, records[i]);
18192             if(n){
18193                 this.tpl.insertBefore(n, d);
18194             }else{
18195                 
18196                 this.tpl.append(this.el, d);
18197             }
18198         }
18199         this.updateIndexes(index);
18200     },
18201
18202     onRemove : function(ds, record, index){
18203        // Roo.log('onRemove');
18204         this.clearSelections();
18205         var el = this.dataName  ?
18206             this.el.child('.roo-tpl-' + this.dataName) :
18207             this.el; 
18208         
18209         el.dom.removeChild(this.nodes[index]);
18210         this.updateIndexes(index);
18211     },
18212
18213     /**
18214      * Refresh an individual node.
18215      * @param {Number} index
18216      */
18217     refreshNode : function(index){
18218         this.onUpdate(this.store, this.store.getAt(index));
18219     },
18220
18221     updateIndexes : function(startIndex, endIndex){
18222         var ns = this.nodes;
18223         startIndex = startIndex || 0;
18224         endIndex = endIndex || ns.length - 1;
18225         for(var i = startIndex; i <= endIndex; i++){
18226             ns[i].nodeIndex = i;
18227         }
18228     },
18229
18230     /**
18231      * Changes the data store this view uses and refresh the view.
18232      * @param {Store} store
18233      */
18234     setStore : function(store, initial){
18235         if(!initial && this.store){
18236             this.store.un("datachanged", this.refresh);
18237             this.store.un("add", this.onAdd);
18238             this.store.un("remove", this.onRemove);
18239             this.store.un("update", this.onUpdate);
18240             this.store.un("clear", this.refresh);
18241             this.store.un("beforeload", this.onBeforeLoad);
18242             this.store.un("load", this.onLoad);
18243             this.store.un("loadexception", this.onLoad);
18244         }
18245         if(store){
18246           
18247             store.on("datachanged", this.refresh, this);
18248             store.on("add", this.onAdd, this);
18249             store.on("remove", this.onRemove, this);
18250             store.on("update", this.onUpdate, this);
18251             store.on("clear", this.refresh, this);
18252             store.on("beforeload", this.onBeforeLoad, this);
18253             store.on("load", this.onLoad, this);
18254             store.on("loadexception", this.onLoad, this);
18255         }
18256         
18257         if(store){
18258             this.refresh();
18259         }
18260     },
18261     /**
18262      * onbeforeLoad - masks the loading area.
18263      *
18264      */
18265     onBeforeLoad : function(store,opts)
18266     {
18267          //Roo.log('onBeforeLoad');   
18268         if (!opts.add) {
18269             this.el.update("");
18270         }
18271         this.el.mask(this.mask ? this.mask : "Loading" ); 
18272     },
18273     onLoad : function ()
18274     {
18275         this.el.unmask();
18276     },
18277     
18278
18279     /**
18280      * Returns the template node the passed child belongs to or null if it doesn't belong to one.
18281      * @param {HTMLElement} node
18282      * @return {HTMLElement} The template node
18283      */
18284     findItemFromChild : function(node){
18285         var el = this.dataName  ?
18286             this.el.child('.roo-tpl-' + this.dataName,true) :
18287             this.el.dom; 
18288         
18289         if(!node || node.parentNode == el){
18290                     return node;
18291             }
18292             var p = node.parentNode;
18293             while(p && p != el){
18294             if(p.parentNode == el){
18295                 return p;
18296             }
18297             p = p.parentNode;
18298         }
18299             return null;
18300     },
18301
18302     /** @ignore */
18303     onClick : function(e){
18304         var item = this.findItemFromChild(e.getTarget());
18305         if(item){
18306             var index = this.indexOf(item);
18307             if(this.onItemClick(item, index, e) !== false){
18308                 this.fireEvent("click", this, index, item, e);
18309             }
18310         }else{
18311             this.clearSelections();
18312         }
18313     },
18314
18315     /** @ignore */
18316     onContextMenu : function(e){
18317         var item = this.findItemFromChild(e.getTarget());
18318         if(item){
18319             this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
18320         }
18321     },
18322
18323     /** @ignore */
18324     onDblClick : function(e){
18325         var item = this.findItemFromChild(e.getTarget());
18326         if(item){
18327             this.fireEvent("dblclick", this, this.indexOf(item), item, e);
18328         }
18329     },
18330
18331     onItemClick : function(item, index, e)
18332     {
18333         if(this.fireEvent("beforeclick", this, index, item, e) === false){
18334             return false;
18335         }
18336         if (this.toggleSelect) {
18337             var m = this.isSelected(item) ? 'unselect' : 'select';
18338             //Roo.log(m);
18339             var _t = this;
18340             _t[m](item, true, false);
18341             return true;
18342         }
18343         if(this.multiSelect || this.singleSelect){
18344             if(this.multiSelect && e.shiftKey && this.lastSelection){
18345                 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
18346             }else{
18347                 this.select(item, this.multiSelect && e.ctrlKey);
18348                 this.lastSelection = item;
18349             }
18350             
18351             if(!this.tickable){
18352                 e.preventDefault();
18353             }
18354             
18355         }
18356         return true;
18357     },
18358
18359     /**
18360      * Get the number of selected nodes.
18361      * @return {Number}
18362      */
18363     getSelectionCount : function(){
18364         return this.selections.length;
18365     },
18366
18367     /**
18368      * Get the currently selected nodes.
18369      * @return {Array} An array of HTMLElements
18370      */
18371     getSelectedNodes : function(){
18372         return this.selections;
18373     },
18374
18375     /**
18376      * Get the indexes of the selected nodes.
18377      * @return {Array}
18378      */
18379     getSelectedIndexes : function(){
18380         var indexes = [], s = this.selections;
18381         for(var i = 0, len = s.length; i < len; i++){
18382             indexes.push(s[i].nodeIndex);
18383         }
18384         return indexes;
18385     },
18386
18387     /**
18388      * Clear all selections
18389      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
18390      */
18391     clearSelections : function(suppressEvent){
18392         if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
18393             this.cmp.elements = this.selections;
18394             this.cmp.removeClass(this.selectedClass);
18395             this.selections = [];
18396             if(!suppressEvent){
18397                 this.fireEvent("selectionchange", this, this.selections);
18398             }
18399         }
18400     },
18401
18402     /**
18403      * Returns true if the passed node is selected
18404      * @param {HTMLElement/Number} node The node or node index
18405      * @return {Boolean}
18406      */
18407     isSelected : function(node){
18408         var s = this.selections;
18409         if(s.length < 1){
18410             return false;
18411         }
18412         node = this.getNode(node);
18413         return s.indexOf(node) !== -1;
18414     },
18415
18416     /**
18417      * Selects nodes.
18418      * @param {Array/HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node, id of a template node or an array of any of those to select
18419      * @param {Boolean} keepExisting (optional) true to keep existing selections
18420      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
18421      */
18422     select : function(nodeInfo, keepExisting, suppressEvent){
18423         if(nodeInfo instanceof Array){
18424             if(!keepExisting){
18425                 this.clearSelections(true);
18426             }
18427             for(var i = 0, len = nodeInfo.length; i < len; i++){
18428                 this.select(nodeInfo[i], true, true);
18429             }
18430             return;
18431         } 
18432         var node = this.getNode(nodeInfo);
18433         if(!node || this.isSelected(node)){
18434             return; // already selected.
18435         }
18436         if(!keepExisting){
18437             this.clearSelections(true);
18438         }
18439         
18440         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
18441             Roo.fly(node).addClass(this.selectedClass);
18442             this.selections.push(node);
18443             if(!suppressEvent){
18444                 this.fireEvent("selectionchange", this, this.selections);
18445             }
18446         }
18447         
18448         
18449     },
18450       /**
18451      * Unselects nodes.
18452      * @param {Array/HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node, id of a template node or an array of any of those to select
18453      * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
18454      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
18455      */
18456     unselect : function(nodeInfo, keepExisting, suppressEvent)
18457     {
18458         if(nodeInfo instanceof Array){
18459             Roo.each(this.selections, function(s) {
18460                 this.unselect(s, nodeInfo);
18461             }, this);
18462             return;
18463         }
18464         var node = this.getNode(nodeInfo);
18465         if(!node || !this.isSelected(node)){
18466             //Roo.log("not selected");
18467             return; // not selected.
18468         }
18469         // fireevent???
18470         var ns = [];
18471         Roo.each(this.selections, function(s) {
18472             if (s == node ) {
18473                 Roo.fly(node).removeClass(this.selectedClass);
18474
18475                 return;
18476             }
18477             ns.push(s);
18478         },this);
18479         
18480         this.selections= ns;
18481         this.fireEvent("selectionchange", this, this.selections);
18482     },
18483
18484     /**
18485      * Gets a template node.
18486      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
18487      * @return {HTMLElement} The node or null if it wasn't found
18488      */
18489     getNode : function(nodeInfo){
18490         if(typeof nodeInfo == "string"){
18491             return document.getElementById(nodeInfo);
18492         }else if(typeof nodeInfo == "number"){
18493             return this.nodes[nodeInfo];
18494         }
18495         return nodeInfo;
18496     },
18497
18498     /**
18499      * Gets a range template nodes.
18500      * @param {Number} startIndex
18501      * @param {Number} endIndex
18502      * @return {Array} An array of nodes
18503      */
18504     getNodes : function(start, end){
18505         var ns = this.nodes;
18506         start = start || 0;
18507         end = typeof end == "undefined" ? ns.length - 1 : end;
18508         var nodes = [];
18509         if(start <= end){
18510             for(var i = start; i <= end; i++){
18511                 nodes.push(ns[i]);
18512             }
18513         } else{
18514             for(var i = start; i >= end; i--){
18515                 nodes.push(ns[i]);
18516             }
18517         }
18518         return nodes;
18519     },
18520
18521     /**
18522      * Finds the index of the passed node
18523      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
18524      * @return {Number} The index of the node or -1
18525      */
18526     indexOf : function(node){
18527         node = this.getNode(node);
18528         if(typeof node.nodeIndex == "number"){
18529             return node.nodeIndex;
18530         }
18531         var ns = this.nodes;
18532         for(var i = 0, len = ns.length; i < len; i++){
18533             if(ns[i] == node){
18534                 return i;
18535             }
18536         }
18537         return -1;
18538     }
18539 });
18540 /*
18541  * - LGPL
18542  *
18543  * based on jquery fullcalendar
18544  * 
18545  */
18546
18547 Roo.bootstrap = Roo.bootstrap || {};
18548 /**
18549  * @class Roo.bootstrap.Calendar
18550  * @extends Roo.bootstrap.Component
18551  * Bootstrap Calendar class
18552  * @cfg {Boolean} loadMask (true|false) default false
18553  * @cfg {Object} header generate the user specific header of the calendar, default false
18554
18555  * @constructor
18556  * Create a new Container
18557  * @param {Object} config The config object
18558  */
18559
18560
18561
18562 Roo.bootstrap.Calendar = function(config){
18563     Roo.bootstrap.Calendar.superclass.constructor.call(this, config);
18564      this.addEvents({
18565         /**
18566              * @event select
18567              * Fires when a date is selected
18568              * @param {DatePicker} this
18569              * @param {Date} date The selected date
18570              */
18571         'select': true,
18572         /**
18573              * @event monthchange
18574              * Fires when the displayed month changes 
18575              * @param {DatePicker} this
18576              * @param {Date} date The selected month
18577              */
18578         'monthchange': true,
18579         /**
18580              * @event evententer
18581              * Fires when mouse over an event
18582              * @param {Calendar} this
18583              * @param {event} Event
18584              */
18585         'evententer': true,
18586         /**
18587              * @event eventleave
18588              * Fires when the mouse leaves an
18589              * @param {Calendar} this
18590              * @param {event}
18591              */
18592         'eventleave': true,
18593         /**
18594              * @event eventclick
18595              * Fires when the mouse click an
18596              * @param {Calendar} this
18597              * @param {event}
18598              */
18599         'eventclick': true
18600         
18601     });
18602
18603 };
18604
18605 Roo.extend(Roo.bootstrap.Calendar, Roo.bootstrap.Component,  {
18606     
18607      /**
18608      * @cfg {Number} startDay
18609      * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
18610      */
18611     startDay : 0,
18612     
18613     loadMask : false,
18614     
18615     header : false,
18616       
18617     getAutoCreate : function(){
18618         
18619         
18620         var fc_button = function(name, corner, style, content ) {
18621             return Roo.apply({},{
18622                 tag : 'span',
18623                 cls : 'fc-button fc-button-'+name+' fc-state-default ' + 
18624                          (corner.length ?
18625                             'fc-corner-' + corner.split(' ').join(' fc-corner-') :
18626                             ''
18627                         ),
18628                 html : '<SPAN class="fc-text-'+style+ '">'+content +'</SPAN>',
18629                 unselectable: 'on'
18630             });
18631         };
18632         
18633         var header = {};
18634         
18635         if(!this.header){
18636             header = {
18637                 tag : 'table',
18638                 cls : 'fc-header',
18639                 style : 'width:100%',
18640                 cn : [
18641                     {
18642                         tag: 'tr',
18643                         cn : [
18644                             {
18645                                 tag : 'td',
18646                                 cls : 'fc-header-left',
18647                                 cn : [
18648                                     fc_button('prev', 'left', 'arrow', '&#8249;' ),
18649                                     fc_button('next', 'right', 'arrow', '&#8250;' ),
18650                                     { tag: 'span', cls: 'fc-header-space' },
18651                                     fc_button('today', 'left right', '', 'today' )  // neds state disabled..
18652
18653
18654                                 ]
18655                             },
18656
18657                             {
18658                                 tag : 'td',
18659                                 cls : 'fc-header-center',
18660                                 cn : [
18661                                     {
18662                                         tag: 'span',
18663                                         cls: 'fc-header-title',
18664                                         cn : {
18665                                             tag: 'H2',
18666                                             html : 'month / year'
18667                                         }
18668                                     }
18669
18670                                 ]
18671                             },
18672                             {
18673                                 tag : 'td',
18674                                 cls : 'fc-header-right',
18675                                 cn : [
18676                               /*      fc_button('month', 'left', '', 'month' ),
18677                                     fc_button('week', '', '', 'week' ),
18678                                     fc_button('day', 'right', '', 'day' )
18679                                 */    
18680
18681                                 ]
18682                             }
18683
18684                         ]
18685                     }
18686                 ]
18687             };
18688         }
18689         
18690         header = this.header;
18691         
18692        
18693         var cal_heads = function() {
18694             var ret = [];
18695             // fixme - handle this.
18696             
18697             for (var i =0; i < Date.dayNames.length; i++) {
18698                 var d = Date.dayNames[i];
18699                 ret.push({
18700                     tag: 'th',
18701                     cls : 'fc-day-header fc-' + d.substring(0,3).toLowerCase() + ' fc-widget-header',
18702                     html : d.substring(0,3)
18703                 });
18704                 
18705             }
18706             ret[0].cls += ' fc-first';
18707             ret[6].cls += ' fc-last';
18708             return ret;
18709         };
18710         var cal_cell = function(n) {
18711             return  {
18712                 tag: 'td',
18713                 cls : 'fc-day fc-'+n + ' fc-widget-content', ///fc-other-month fc-past
18714                 cn : [
18715                     {
18716                         cn : [
18717                             {
18718                                 cls: 'fc-day-number',
18719                                 html: 'D'
18720                             },
18721                             {
18722                                 cls: 'fc-day-content',
18723                              
18724                                 cn : [
18725                                      {
18726                                         style: 'position: relative;' // height: 17px;
18727                                     }
18728                                 ]
18729                             }
18730                             
18731                             
18732                         ]
18733                     }
18734                 ]
18735                 
18736             }
18737         };
18738         var cal_rows = function() {
18739             
18740             var ret = [];
18741             for (var r = 0; r < 6; r++) {
18742                 var row= {
18743                     tag : 'tr',
18744                     cls : 'fc-week',
18745                     cn : []
18746                 };
18747                 
18748                 for (var i =0; i < Date.dayNames.length; i++) {
18749                     var d = Date.dayNames[i];
18750                     row.cn.push(cal_cell(d.substring(0,3).toLowerCase()));
18751
18752                 }
18753                 row.cn[0].cls+=' fc-first';
18754                 row.cn[0].cn[0].style = 'min-height:90px';
18755                 row.cn[6].cls+=' fc-last';
18756                 ret.push(row);
18757                 
18758             }
18759             ret[0].cls += ' fc-first';
18760             ret[4].cls += ' fc-prev-last';
18761             ret[5].cls += ' fc-last';
18762             return ret;
18763             
18764         };
18765         
18766         var cal_table = {
18767             tag: 'table',
18768             cls: 'fc-border-separate',
18769             style : 'width:100%',
18770             cellspacing  : 0,
18771             cn : [
18772                 { 
18773                     tag: 'thead',
18774                     cn : [
18775                         { 
18776                             tag: 'tr',
18777                             cls : 'fc-first fc-last',
18778                             cn : cal_heads()
18779                         }
18780                     ]
18781                 },
18782                 { 
18783                     tag: 'tbody',
18784                     cn : cal_rows()
18785                 }
18786                   
18787             ]
18788         };
18789          
18790          var cfg = {
18791             cls : 'fc fc-ltr',
18792             cn : [
18793                 header,
18794                 {
18795                     cls : 'fc-content',
18796                     style : "position: relative;",
18797                     cn : [
18798                         {
18799                             cls : 'fc-view fc-view-month fc-grid',
18800                             style : 'position: relative',
18801                             unselectable : 'on',
18802                             cn : [
18803                                 {
18804                                     cls : 'fc-event-container',
18805                                     style : 'position:absolute;z-index:8;top:0;left:0;'
18806                                 },
18807                                 cal_table
18808                             ]
18809                         }
18810                     ]
18811     
18812                 }
18813            ] 
18814             
18815         };
18816         
18817          
18818         
18819         return cfg;
18820     },
18821     
18822     
18823     initEvents : function()
18824     {
18825         if(!this.store){
18826             throw "can not find store for calendar";
18827         }
18828         
18829         var mark = {
18830             tag: "div",
18831             cls:"x-dlg-mask",
18832             style: "text-align:center",
18833             cn: [
18834                 {
18835                     tag: "div",
18836                     style: "background-color:white;width:50%;margin:250 auto",
18837                     cn: [
18838                         {
18839                             tag: "img",
18840                             src: Roo.rootURL + '/images/ux/lightbox/loading.gif' 
18841                         },
18842                         {
18843                             tag: "span",
18844                             html: "Loading"
18845                         }
18846                         
18847                     ]
18848                 }
18849             ]
18850         };
18851         this.maskEl = Roo.DomHelper.append(this.el.select('.fc-content', true).first(), mark, true);
18852         
18853         var size = this.el.select('.fc-content', true).first().getSize();
18854         this.maskEl.setSize(size.width, size.height);
18855         this.maskEl.enableDisplayMode("block");
18856         if(!this.loadMask){
18857             this.maskEl.hide();
18858         }
18859         
18860         this.store = Roo.factory(this.store, Roo.data);
18861         this.store.on('load', this.onLoad, this);
18862         this.store.on('beforeload', this.onBeforeLoad, this);
18863         
18864         this.resize();
18865         
18866         this.cells = this.el.select('.fc-day',true);
18867         //Roo.log(this.cells);
18868         this.textNodes = this.el.query('.fc-day-number');
18869         this.cells.addClassOnOver('fc-state-hover');
18870         
18871         this.el.select('.fc-button-prev',true).on('click', this.showPrevMonth, this);
18872         this.el.select('.fc-button-next',true).on('click', this.showNextMonth, this);
18873         this.el.select('.fc-button-today',true).on('click', this.showToday, this);
18874         this.el.select('.fc-button',true).addClassOnOver('fc-state-hover');
18875         
18876         this.on('monthchange', this.onMonthChange, this);
18877         
18878         this.update(new Date().clearTime());
18879     },
18880     
18881     resize : function() {
18882         var sz  = this.el.getSize();
18883         
18884         this.el.select('.fc-day-header',true).setWidth(sz.width / 7);
18885         this.el.select('.fc-day-content div',true).setHeight(34);
18886     },
18887     
18888     
18889     // private
18890     showPrevMonth : function(e){
18891         this.update(this.activeDate.add("mo", -1));
18892     },
18893     showToday : function(e){
18894         this.update(new Date().clearTime());
18895     },
18896     // private
18897     showNextMonth : function(e){
18898         this.update(this.activeDate.add("mo", 1));
18899     },
18900
18901     // private
18902     showPrevYear : function(){
18903         this.update(this.activeDate.add("y", -1));
18904     },
18905
18906     // private
18907     showNextYear : function(){
18908         this.update(this.activeDate.add("y", 1));
18909     },
18910
18911     
18912    // private
18913     update : function(date)
18914     {
18915         var vd = this.activeDate;
18916         this.activeDate = date;
18917 //        if(vd && this.el){
18918 //            var t = date.getTime();
18919 //            if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
18920 //                Roo.log('using add remove');
18921 //                
18922 //                this.fireEvent('monthchange', this, date);
18923 //                
18924 //                this.cells.removeClass("fc-state-highlight");
18925 //                this.cells.each(function(c){
18926 //                   if(c.dateValue == t){
18927 //                       c.addClass("fc-state-highlight");
18928 //                       setTimeout(function(){
18929 //                            try{c.dom.firstChild.focus();}catch(e){}
18930 //                       }, 50);
18931 //                       return false;
18932 //                   }
18933 //                   return true;
18934 //                });
18935 //                return;
18936 //            }
18937 //        }
18938         
18939         var days = date.getDaysInMonth();
18940         
18941         var firstOfMonth = date.getFirstDateOfMonth();
18942         var startingPos = firstOfMonth.getDay()-this.startDay;
18943         
18944         if(startingPos < this.startDay){
18945             startingPos += 7;
18946         }
18947         
18948         var pm = date.add(Date.MONTH, -1);
18949         var prevStart = pm.getDaysInMonth()-startingPos;
18950 //        
18951         this.cells = this.el.select('.fc-day',true);
18952         this.textNodes = this.el.query('.fc-day-number');
18953         this.cells.addClassOnOver('fc-state-hover');
18954         
18955         var cells = this.cells.elements;
18956         var textEls = this.textNodes;
18957         
18958         Roo.each(cells, function(cell){
18959             cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
18960         });
18961         
18962         days += startingPos;
18963
18964         // convert everything to numbers so it's fast
18965         var day = 86400000;
18966         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
18967         //Roo.log(d);
18968         //Roo.log(pm);
18969         //Roo.log(prevStart);
18970         
18971         var today = new Date().clearTime().getTime();
18972         var sel = date.clearTime().getTime();
18973         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
18974         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
18975         var ddMatch = this.disabledDatesRE;
18976         var ddText = this.disabledDatesText;
18977         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
18978         var ddaysText = this.disabledDaysText;
18979         var format = this.format;
18980         
18981         var setCellClass = function(cal, cell){
18982             cell.row = 0;
18983             cell.events = [];
18984             cell.more = [];
18985             //Roo.log('set Cell Class');
18986             cell.title = "";
18987             var t = d.getTime();
18988             
18989             //Roo.log(d);
18990             
18991             cell.dateValue = t;
18992             if(t == today){
18993                 cell.className += " fc-today";
18994                 cell.className += " fc-state-highlight";
18995                 cell.title = cal.todayText;
18996             }
18997             if(t == sel){
18998                 // disable highlight in other month..
18999                 //cell.className += " fc-state-highlight";
19000                 
19001             }
19002             // disabling
19003             if(t < min) {
19004                 cell.className = " fc-state-disabled";
19005                 cell.title = cal.minText;
19006                 return;
19007             }
19008             if(t > max) {
19009                 cell.className = " fc-state-disabled";
19010                 cell.title = cal.maxText;
19011                 return;
19012             }
19013             if(ddays){
19014                 if(ddays.indexOf(d.getDay()) != -1){
19015                     cell.title = ddaysText;
19016                     cell.className = " fc-state-disabled";
19017                 }
19018             }
19019             if(ddMatch && format){
19020                 var fvalue = d.dateFormat(format);
19021                 if(ddMatch.test(fvalue)){
19022                     cell.title = ddText.replace("%0", fvalue);
19023                     cell.className = " fc-state-disabled";
19024                 }
19025             }
19026             
19027             if (!cell.initialClassName) {
19028                 cell.initialClassName = cell.dom.className;
19029             }
19030             
19031             cell.dom.className = cell.initialClassName  + ' ' +  cell.className;
19032         };
19033
19034         var i = 0;
19035         
19036         for(; i < startingPos; i++) {
19037             textEls[i].innerHTML = (++prevStart);
19038             d.setDate(d.getDate()+1);
19039             
19040             cells[i].className = "fc-past fc-other-month";
19041             setCellClass(this, cells[i]);
19042         }
19043         
19044         var intDay = 0;
19045         
19046         for(; i < days; i++){
19047             intDay = i - startingPos + 1;
19048             textEls[i].innerHTML = (intDay);
19049             d.setDate(d.getDate()+1);
19050             
19051             cells[i].className = ''; // "x-date-active";
19052             setCellClass(this, cells[i]);
19053         }
19054         var extraDays = 0;
19055         
19056         for(; i < 42; i++) {
19057             textEls[i].innerHTML = (++extraDays);
19058             d.setDate(d.getDate()+1);
19059             
19060             cells[i].className = "fc-future fc-other-month";
19061             setCellClass(this, cells[i]);
19062         }
19063         
19064         this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
19065         
19066         var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
19067         
19068         this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
19069         this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
19070         
19071         if(totalRows != 6){
19072             this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
19073             this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
19074         }
19075         
19076         this.fireEvent('monthchange', this, date);
19077         
19078         
19079         /*
19080         if(!this.internalRender){
19081             var main = this.el.dom.firstChild;
19082             var w = main.offsetWidth;
19083             this.el.setWidth(w + this.el.getBorderWidth("lr"));
19084             Roo.fly(main).setWidth(w);
19085             this.internalRender = true;
19086             // opera does not respect the auto grow header center column
19087             // then, after it gets a width opera refuses to recalculate
19088             // without a second pass
19089             if(Roo.isOpera && !this.secondPass){
19090                 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
19091                 this.secondPass = true;
19092                 this.update.defer(10, this, [date]);
19093             }
19094         }
19095         */
19096         
19097     },
19098     
19099     findCell : function(dt) {
19100         dt = dt.clearTime().getTime();
19101         var ret = false;
19102         this.cells.each(function(c){
19103             //Roo.log("check " +c.dateValue + '?=' + dt);
19104             if(c.dateValue == dt){
19105                 ret = c;
19106                 return false;
19107             }
19108             return true;
19109         });
19110         
19111         return ret;
19112     },
19113     
19114     findCells : function(ev) {
19115         var s = ev.start.clone().clearTime().getTime();
19116        // Roo.log(s);
19117         var e= ev.end.clone().clearTime().getTime();
19118        // Roo.log(e);
19119         var ret = [];
19120         this.cells.each(function(c){
19121              ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
19122             
19123             if(c.dateValue > e){
19124                 return ;
19125             }
19126             if(c.dateValue < s){
19127                 return ;
19128             }
19129             ret.push(c);
19130         });
19131         
19132         return ret;    
19133     },
19134     
19135 //    findBestRow: function(cells)
19136 //    {
19137 //        var ret = 0;
19138 //        
19139 //        for (var i =0 ; i < cells.length;i++) {
19140 //            ret  = Math.max(cells[i].rows || 0,ret);
19141 //        }
19142 //        return ret;
19143 //        
19144 //    },
19145     
19146     
19147     addItem : function(ev)
19148     {
19149         // look for vertical location slot in
19150         var cells = this.findCells(ev);
19151         
19152 //        ev.row = this.findBestRow(cells);
19153         
19154         // work out the location.
19155         
19156         var crow = false;
19157         var rows = [];
19158         for(var i =0; i < cells.length; i++) {
19159             
19160             cells[i].row = cells[0].row;
19161             
19162             if(i == 0){
19163                 cells[i].row = cells[i].row + 1;
19164             }
19165             
19166             if (!crow) {
19167                 crow = {
19168                     start : cells[i],
19169                     end :  cells[i]
19170                 };
19171                 continue;
19172             }
19173             if (crow.start.getY() == cells[i].getY()) {
19174                 // on same row.
19175                 crow.end = cells[i];
19176                 continue;
19177             }
19178             // different row.
19179             rows.push(crow);
19180             crow = {
19181                 start: cells[i],
19182                 end : cells[i]
19183             };
19184             
19185         }
19186         
19187         rows.push(crow);
19188         ev.els = [];
19189         ev.rows = rows;
19190         ev.cells = cells;
19191         
19192         cells[0].events.push(ev);
19193         
19194         this.calevents.push(ev);
19195     },
19196     
19197     clearEvents: function() {
19198         
19199         if(!this.calevents){
19200             return;
19201         }
19202         
19203         Roo.each(this.cells.elements, function(c){
19204             c.row = 0;
19205             c.events = [];
19206             c.more = [];
19207         });
19208         
19209         Roo.each(this.calevents, function(e) {
19210             Roo.each(e.els, function(el) {
19211                 el.un('mouseenter' ,this.onEventEnter, this);
19212                 el.un('mouseleave' ,this.onEventLeave, this);
19213                 el.remove();
19214             },this);
19215         },this);
19216         
19217         Roo.each(Roo.select('.fc-more-event', true).elements, function(e){
19218             e.remove();
19219         });
19220         
19221     },
19222     
19223     renderEvents: function()
19224     {   
19225         var _this = this;
19226         
19227         this.cells.each(function(c) {
19228             
19229             if(c.row < 5){
19230                 return;
19231             }
19232             
19233             var ev = c.events;
19234             
19235             var r = 4;
19236             if(c.row != c.events.length){
19237                 r = 4 - (4 - (c.row - c.events.length));
19238             }
19239             
19240             c.events = ev.slice(0, r);
19241             c.more = ev.slice(r);
19242             
19243             if(c.more.length && c.more.length == 1){
19244                 c.events.push(c.more.pop());
19245             }
19246             
19247             c.row = (c.row - ev.length) + c.events.length + ((c.more.length) ? 1 : 0);
19248             
19249         });
19250             
19251         this.cells.each(function(c) {
19252             
19253             c.select('.fc-day-content div',true).first().setHeight(Math.max(34, c.row * 20));
19254             
19255             
19256             for (var e = 0; e < c.events.length; e++){
19257                 var ev = c.events[e];
19258                 var rows = ev.rows;
19259                 
19260                 for(var i = 0; i < rows.length; i++) {
19261                 
19262                     // how many rows should it span..
19263
19264                     var  cfg = {
19265                         cls : 'roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable',
19266                         style : 'position: absolute', // left: 387px; width: 121px; top: 359px;
19267
19268                         unselectable : "on",
19269                         cn : [
19270                             {
19271                                 cls: 'fc-event-inner',
19272                                 cn : [
19273     //                                {
19274     //                                  tag:'span',
19275     //                                  cls: 'fc-event-time',
19276     //                                  html : cells.length > 1 ? '' : ev.time
19277     //                                },
19278                                     {
19279                                       tag:'span',
19280                                       cls: 'fc-event-title',
19281                                       html : String.format('{0}', ev.title)
19282                                     }
19283
19284
19285                                 ]
19286                             },
19287                             {
19288                                 cls: 'ui-resizable-handle ui-resizable-e',
19289                                 html : '&nbsp;&nbsp;&nbsp'
19290                             }
19291
19292                         ]
19293                     };
19294
19295                     if (i == 0) {
19296                         cfg.cls += ' fc-event-start';
19297                     }
19298                     if ((i+1) == rows.length) {
19299                         cfg.cls += ' fc-event-end';
19300                     }
19301
19302                     var ctr = _this.el.select('.fc-event-container',true).first();
19303                     var cg = ctr.createChild(cfg);
19304
19305                     var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
19306                     var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
19307
19308                     var r = (c.more.length) ? 1 : 0;
19309                     cg.setXY([sbox.x +2, sbox.y + ((c.row - c.events.length - r + e) * 20)]);    
19310                     cg.setWidth(ebox.right - sbox.x -2);
19311
19312                     cg.on('mouseenter' ,_this.onEventEnter, _this, ev);
19313                     cg.on('mouseleave' ,_this.onEventLeave, _this, ev);
19314                     cg.on('click', _this.onEventClick, _this, ev);
19315
19316                     ev.els.push(cg);
19317                     
19318                 }
19319                 
19320             }
19321             
19322             
19323             if(c.more.length){
19324                 var  cfg = {
19325                     cls : 'fc-more-event roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable fc-event-start fc-event-end',
19326                     style : 'position: absolute',
19327                     unselectable : "on",
19328                     cn : [
19329                         {
19330                             cls: 'fc-event-inner',
19331                             cn : [
19332                                 {
19333                                   tag:'span',
19334                                   cls: 'fc-event-title',
19335                                   html : 'More'
19336                                 }
19337
19338
19339                             ]
19340                         },
19341                         {
19342                             cls: 'ui-resizable-handle ui-resizable-e',
19343                             html : '&nbsp;&nbsp;&nbsp'
19344                         }
19345
19346                     ]
19347                 };
19348
19349                 var ctr = _this.el.select('.fc-event-container',true).first();
19350                 var cg = ctr.createChild(cfg);
19351
19352                 var sbox = c.select('.fc-day-content',true).first().getBox();
19353                 var ebox = c.select('.fc-day-content',true).first().getBox();
19354                 //Roo.log(cg);
19355                 cg.setXY([sbox.x +2, sbox.y +((c.row - 1) * 20)]);    
19356                 cg.setWidth(ebox.right - sbox.x -2);
19357
19358                 cg.on('click', _this.onMoreEventClick, _this, c.more);
19359                 
19360             }
19361             
19362         });
19363         
19364         
19365         
19366     },
19367     
19368     onEventEnter: function (e, el,event,d) {
19369         this.fireEvent('evententer', this, el, event);
19370     },
19371     
19372     onEventLeave: function (e, el,event,d) {
19373         this.fireEvent('eventleave', this, el, event);
19374     },
19375     
19376     onEventClick: function (e, el,event,d) {
19377         this.fireEvent('eventclick', this, el, event);
19378     },
19379     
19380     onMonthChange: function () {
19381         this.store.load();
19382     },
19383     
19384     onMoreEventClick: function(e, el, more)
19385     {
19386         var _this = this;
19387         
19388         this.calpopover.placement = 'right';
19389         this.calpopover.setTitle('More');
19390         
19391         this.calpopover.setContent('');
19392         
19393         var ctr = this.calpopover.el.select('.popover-content', true).first();
19394         
19395         Roo.each(more, function(m){
19396             var cfg = {
19397                 cls : 'fc-event-hori fc-event-draggable',
19398                 html : m.title
19399             };
19400             var cg = ctr.createChild(cfg);
19401             
19402             cg.on('click', _this.onEventClick, _this, m);
19403         });
19404         
19405         this.calpopover.show(el);
19406         
19407         
19408     },
19409     
19410     onLoad: function () 
19411     {   
19412         this.calevents = [];
19413         var cal = this;
19414         
19415         if(this.store.getCount() > 0){
19416             this.store.data.each(function(d){
19417                cal.addItem({
19418                     id : d.data.id,
19419                     start: (typeof(d.data.start_dt) === 'string') ? new Date.parseDate(d.data.start_dt, 'Y-m-d H:i:s') : d.data.start_dt,
19420                     end : (typeof(d.data.end_dt) === 'string') ? new Date.parseDate(d.data.end_dt, 'Y-m-d H:i:s') : d.data.end_dt,
19421                     time : d.data.start_time,
19422                     title : d.data.title,
19423                     description : d.data.description,
19424                     venue : d.data.venue
19425                 });
19426             });
19427         }
19428         
19429         this.renderEvents();
19430         
19431         if(this.calevents.length && this.loadMask){
19432             this.maskEl.hide();
19433         }
19434     },
19435     
19436     onBeforeLoad: function()
19437     {
19438         this.clearEvents();
19439         if(this.loadMask){
19440             this.maskEl.show();
19441         }
19442     }
19443 });
19444
19445  
19446  /*
19447  * - LGPL
19448  *
19449  * element
19450  * 
19451  */
19452
19453 /**
19454  * @class Roo.bootstrap.Popover
19455  * @extends Roo.bootstrap.Component
19456  * Bootstrap Popover class
19457  * @cfg {String} html contents of the popover   (or false to use children..)
19458  * @cfg {String} title of popover (or false to hide)
19459  * @cfg {String} placement how it is placed
19460  * @cfg {String} trigger click || hover (or false to trigger manually)
19461  * @cfg {String} over what (parent or false to trigger manually.)
19462  * @cfg {Number} delay - delay before showing
19463  
19464  * @constructor
19465  * Create a new Popover
19466  * @param {Object} config The config object
19467  */
19468
19469 Roo.bootstrap.Popover = function(config){
19470     Roo.bootstrap.Popover.superclass.constructor.call(this, config);
19471     
19472     this.addEvents({
19473         // raw events
19474          /**
19475          * @event show
19476          * After the popover show
19477          * 
19478          * @param {Roo.bootstrap.Popover} this
19479          */
19480         "show" : true,
19481         /**
19482          * @event hide
19483          * After the popover hide
19484          * 
19485          * @param {Roo.bootstrap.Popover} this
19486          */
19487         "hide" : true
19488     });
19489 };
19490
19491 Roo.extend(Roo.bootstrap.Popover, Roo.bootstrap.Component,  {
19492     
19493     title: 'Fill in a title',
19494     html: false,
19495     
19496     placement : 'right',
19497     trigger : 'hover', // hover
19498     
19499     delay : 0,
19500     
19501     over: 'parent',
19502     
19503     can_build_overlaid : false,
19504     
19505     getChildContainer : function()
19506     {
19507         return this.el.select('.popover-content',true).first();
19508     },
19509     
19510     getAutoCreate : function(){
19511          
19512         var cfg = {
19513            cls : 'popover roo-dynamic',
19514            style: 'display:block',
19515            cn : [
19516                 {
19517                     cls : 'arrow'
19518                 },
19519                 {
19520                     cls : 'popover-inner',
19521                     cn : [
19522                         {
19523                             tag: 'h3',
19524                             cls: 'popover-title popover-header',
19525                             html : this.title
19526                         },
19527                         {
19528                             cls : 'popover-content popover-body',
19529                             html : this.html
19530                         }
19531                     ]
19532                     
19533                 }
19534            ]
19535         };
19536         
19537         return cfg;
19538     },
19539     setTitle: function(str)
19540     {
19541         this.title = str;
19542         this.el.select('.popover-title',true).first().dom.innerHTML = str;
19543     },
19544     setContent: function(str)
19545     {
19546         this.html = str;
19547         this.el.select('.popover-content',true).first().dom.innerHTML = str;
19548     },
19549     // as it get's added to the bottom of the page.
19550     onRender : function(ct, position)
19551     {
19552         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
19553         if(!this.el){
19554             var cfg = Roo.apply({},  this.getAutoCreate());
19555             cfg.id = Roo.id();
19556             
19557             if (this.cls) {
19558                 cfg.cls += ' ' + this.cls;
19559             }
19560             if (this.style) {
19561                 cfg.style = this.style;
19562             }
19563             //Roo.log("adding to ");
19564             this.el = Roo.get(document.body).createChild(cfg, position);
19565 //            Roo.log(this.el);
19566         }
19567         this.initEvents();
19568     },
19569     
19570     initEvents : function()
19571     {
19572         this.el.select('.popover-title',true).setVisibilityMode(Roo.Element.DISPLAY);
19573         this.el.enableDisplayMode('block');
19574         this.el.hide();
19575         if (this.over === false) {
19576             return; 
19577         }
19578         if (this.triggers === false) {
19579             return;
19580         }
19581         var on_el = (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
19582         var triggers = this.trigger ? this.trigger.split(' ') : [];
19583         Roo.each(triggers, function(trigger) {
19584         
19585             if (trigger == 'click') {
19586                 on_el.on('click', this.toggle, this);
19587             } else if (trigger != 'manual') {
19588                 var eventIn  = trigger == 'hover' ? 'mouseenter' : 'focusin';
19589                 var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout';
19590       
19591                 on_el.on(eventIn  ,this.enter, this);
19592                 on_el.on(eventOut, this.leave, this);
19593             }
19594         }, this);
19595         
19596     },
19597     
19598     
19599     // private
19600     timeout : null,
19601     hoverState : null,
19602     
19603     toggle : function () {
19604         this.hoverState == 'in' ? this.leave() : this.enter();
19605     },
19606     
19607     enter : function () {
19608         
19609         clearTimeout(this.timeout);
19610     
19611         this.hoverState = 'in';
19612     
19613         if (!this.delay || !this.delay.show) {
19614             this.show();
19615             return;
19616         }
19617         var _t = this;
19618         this.timeout = setTimeout(function () {
19619             if (_t.hoverState == 'in') {
19620                 _t.show();
19621             }
19622         }, this.delay.show)
19623     },
19624     
19625     leave : function() {
19626         clearTimeout(this.timeout);
19627     
19628         this.hoverState = 'out';
19629     
19630         if (!this.delay || !this.delay.hide) {
19631             this.hide();
19632             return;
19633         }
19634         var _t = this;
19635         this.timeout = setTimeout(function () {
19636             if (_t.hoverState == 'out') {
19637                 _t.hide();
19638             }
19639         }, this.delay.hide)
19640     },
19641     
19642     show : function (on_el)
19643     {
19644         if (!on_el) {
19645             on_el= (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
19646         }
19647         
19648         // set content.
19649         this.el.select('.popover-title',true).first().dom.innerHtml = this.title;
19650         if (this.html !== false) {
19651             this.el.select('.popover-content',true).first().dom.innerHtml = this.html;
19652         }
19653         this.el.removeClass([
19654             'fade','top','bottom', 'left', 'right','in',
19655             'bs-popover-top','bs-popover-bottom', 'bs-popover-left', 'bs-popover-right'
19656         ]);
19657         if (!this.title.length) {
19658             this.el.select('.popover-title',true).hide();
19659         }
19660         
19661         var placement = typeof this.placement == 'function' ?
19662             this.placement.call(this, this.el, on_el) :
19663             this.placement;
19664             
19665         var autoToken = /\s?auto?\s?/i;
19666         var autoPlace = autoToken.test(placement);
19667         if (autoPlace) {
19668             placement = placement.replace(autoToken, '') || 'top';
19669         }
19670         
19671         //this.el.detach()
19672         //this.el.setXY([0,0]);
19673         this.el.show();
19674         this.el.dom.style.display='block';
19675         this.el.addClass(placement);
19676         
19677         //this.el.appendTo(on_el);
19678         
19679         var p = this.getPosition();
19680         var box = this.el.getBox();
19681         
19682         if (autoPlace) {
19683             // fixme..
19684         }
19685         var align = Roo.bootstrap.Popover.alignment[placement];
19686         
19687 //        Roo.log(align);
19688         this.el.alignTo(on_el, align[0],align[1]);
19689         //var arrow = this.el.select('.arrow',true).first();
19690         //arrow.set(align[2], 
19691         
19692         this.el.addClass('in');
19693         
19694         
19695         if (this.el.hasClass('fade')) {
19696             // fade it?
19697         }
19698         
19699         this.hoverState = 'in';
19700         
19701         this.fireEvent('show', this);
19702         
19703     },
19704     hide : function()
19705     {
19706         this.el.setXY([0,0]);
19707         this.el.removeClass('in');
19708         this.el.hide();
19709         this.hoverState = null;
19710         
19711         this.fireEvent('hide', this);
19712     }
19713     
19714 });
19715
19716 Roo.bootstrap.Popover.alignment = {
19717     'left' : ['r-l', [-10,0], 'right bs-popover-right'],
19718     'right' : ['l-r', [10,0], 'left bs-popover-left'],
19719     'bottom' : ['t-b', [0,10], 'top bs-popover-top'],
19720     'top' : [ 'b-t', [0,-10], 'bottom bs-popover-bottom']
19721 };
19722
19723  /*
19724  * - LGPL
19725  *
19726  * Progress
19727  * 
19728  */
19729
19730 /**
19731  * @class Roo.bootstrap.Progress
19732  * @extends Roo.bootstrap.Component
19733  * Bootstrap Progress class
19734  * @cfg {Boolean} striped striped of the progress bar
19735  * @cfg {Boolean} active animated of the progress bar
19736  * 
19737  * 
19738  * @constructor
19739  * Create a new Progress
19740  * @param {Object} config The config object
19741  */
19742
19743 Roo.bootstrap.Progress = function(config){
19744     Roo.bootstrap.Progress.superclass.constructor.call(this, config);
19745 };
19746
19747 Roo.extend(Roo.bootstrap.Progress, Roo.bootstrap.Component,  {
19748     
19749     striped : false,
19750     active: false,
19751     
19752     getAutoCreate : function(){
19753         var cfg = {
19754             tag: 'div',
19755             cls: 'progress'
19756         };
19757         
19758         
19759         if(this.striped){
19760             cfg.cls += ' progress-striped';
19761         }
19762       
19763         if(this.active){
19764             cfg.cls += ' active';
19765         }
19766         
19767         
19768         return cfg;
19769     }
19770    
19771 });
19772
19773  
19774
19775  /*
19776  * - LGPL
19777  *
19778  * ProgressBar
19779  * 
19780  */
19781
19782 /**
19783  * @class Roo.bootstrap.ProgressBar
19784  * @extends Roo.bootstrap.Component
19785  * Bootstrap ProgressBar class
19786  * @cfg {Number} aria_valuenow aria-value now
19787  * @cfg {Number} aria_valuemin aria-value min
19788  * @cfg {Number} aria_valuemax aria-value max
19789  * @cfg {String} label label for the progress bar
19790  * @cfg {String} panel (success | info | warning | danger )
19791  * @cfg {String} role role of the progress bar
19792  * @cfg {String} sr_only text
19793  * 
19794  * 
19795  * @constructor
19796  * Create a new ProgressBar
19797  * @param {Object} config The config object
19798  */
19799
19800 Roo.bootstrap.ProgressBar = function(config){
19801     Roo.bootstrap.ProgressBar.superclass.constructor.call(this, config);
19802 };
19803
19804 Roo.extend(Roo.bootstrap.ProgressBar, Roo.bootstrap.Component,  {
19805     
19806     aria_valuenow : 0,
19807     aria_valuemin : 0,
19808     aria_valuemax : 100,
19809     label : false,
19810     panel : false,
19811     role : false,
19812     sr_only: false,
19813     
19814     getAutoCreate : function()
19815     {
19816         
19817         var cfg = {
19818             tag: 'div',
19819             cls: 'progress-bar',
19820             style: 'width:' + Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%'
19821         };
19822         
19823         if(this.sr_only){
19824             cfg.cn = {
19825                 tag: 'span',
19826                 cls: 'sr-only',
19827                 html: this.sr_only
19828             }
19829         }
19830         
19831         if(this.role){
19832             cfg.role = this.role;
19833         }
19834         
19835         if(this.aria_valuenow){
19836             cfg['aria-valuenow'] = this.aria_valuenow;
19837         }
19838         
19839         if(this.aria_valuemin){
19840             cfg['aria-valuemin'] = this.aria_valuemin;
19841         }
19842         
19843         if(this.aria_valuemax){
19844             cfg['aria-valuemax'] = this.aria_valuemax;
19845         }
19846         
19847         if(this.label && !this.sr_only){
19848             cfg.html = this.label;
19849         }
19850         
19851         if(this.panel){
19852             cfg.cls += ' progress-bar-' + this.panel;
19853         }
19854         
19855         return cfg;
19856     },
19857     
19858     update : function(aria_valuenow)
19859     {
19860         this.aria_valuenow = aria_valuenow;
19861         
19862         this.el.setStyle('width', Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%');
19863     }
19864    
19865 });
19866
19867  
19868
19869  /*
19870  * - LGPL
19871  *
19872  * column
19873  * 
19874  */
19875
19876 /**
19877  * @class Roo.bootstrap.TabGroup
19878  * @extends Roo.bootstrap.Column
19879  * Bootstrap Column class
19880  * @cfg {String} navId the navigation id (for use with navbars) - will be auto generated if it does not exist..
19881  * @cfg {Boolean} carousel true to make the group behave like a carousel
19882  * @cfg {Boolean} bullets show bullets for the panels
19883  * @cfg {Boolean} autoslide (true|false) auto slide .. default false
19884  * @cfg {Number} timer auto slide timer .. default 0 millisecond
19885  * @cfg {Boolean} showarrow (true|false) show arrow default true
19886  * 
19887  * @constructor
19888  * Create a new TabGroup
19889  * @param {Object} config The config object
19890  */
19891
19892 Roo.bootstrap.TabGroup = function(config){
19893     Roo.bootstrap.TabGroup.superclass.constructor.call(this, config);
19894     if (!this.navId) {
19895         this.navId = Roo.id();
19896     }
19897     this.tabs = [];
19898     Roo.bootstrap.TabGroup.register(this);
19899     
19900 };
19901
19902 Roo.extend(Roo.bootstrap.TabGroup, Roo.bootstrap.Column,  {
19903     
19904     carousel : false,
19905     transition : false,
19906     bullets : 0,
19907     timer : 0,
19908     autoslide : false,
19909     slideFn : false,
19910     slideOnTouch : false,
19911     showarrow : true,
19912     
19913     getAutoCreate : function()
19914     {
19915         var cfg = Roo.apply({}, Roo.bootstrap.TabGroup.superclass.getAutoCreate.call(this));
19916         
19917         cfg.cls += ' tab-content';
19918         
19919         if (this.carousel) {
19920             cfg.cls += ' carousel slide';
19921             
19922             cfg.cn = [{
19923                cls : 'carousel-inner',
19924                cn : []
19925             }];
19926         
19927             if(this.bullets  && !Roo.isTouch){
19928                 
19929                 var bullets = {
19930                     cls : 'carousel-bullets',
19931                     cn : []
19932                 };
19933                
19934                 if(this.bullets_cls){
19935                     bullets.cls = bullets.cls + ' ' + this.bullets_cls;
19936                 }
19937                 
19938                 bullets.cn.push({
19939                     cls : 'clear'
19940                 });
19941                 
19942                 cfg.cn[0].cn.push(bullets);
19943             }
19944             
19945             if(this.showarrow){
19946                 cfg.cn[0].cn.push({
19947                     tag : 'div',
19948                     class : 'carousel-arrow',
19949                     cn : [
19950                         {
19951                             tag : 'div',
19952                             class : 'carousel-prev',
19953                             cn : [
19954                                 {
19955                                     tag : 'i',
19956                                     class : 'fa fa-chevron-left'
19957                                 }
19958                             ]
19959                         },
19960                         {
19961                             tag : 'div',
19962                             class : 'carousel-next',
19963                             cn : [
19964                                 {
19965                                     tag : 'i',
19966                                     class : 'fa fa-chevron-right'
19967                                 }
19968                             ]
19969                         }
19970                     ]
19971                 });
19972             }
19973             
19974         }
19975         
19976         return cfg;
19977     },
19978     
19979     initEvents:  function()
19980     {
19981 //        if(Roo.isTouch && this.slideOnTouch && !this.showarrow){
19982 //            this.el.on("touchstart", this.onTouchStart, this);
19983 //        }
19984         
19985         if(this.autoslide){
19986             var _this = this;
19987             
19988             this.slideFn = window.setInterval(function() {
19989                 _this.showPanelNext();
19990             }, this.timer);
19991         }
19992         
19993         if(this.showarrow){
19994             this.el.select('.carousel-prev', true).first().on('click', this.showPanelPrev, this);
19995             this.el.select('.carousel-next', true).first().on('click', this.showPanelNext, this);
19996         }
19997         
19998         
19999     },
20000     
20001 //    onTouchStart : function(e, el, o)
20002 //    {
20003 //        if(!this.slideOnTouch || !Roo.isTouch || Roo.get(e.getTarget()).hasClass('roo-button-text')){
20004 //            return;
20005 //        }
20006 //        
20007 //        this.showPanelNext();
20008 //    },
20009     
20010     
20011     getChildContainer : function()
20012     {
20013         return this.carousel ? this.el.select('.carousel-inner', true).first() : this.el;
20014     },
20015     
20016     /**
20017     * register a Navigation item
20018     * @param {Roo.bootstrap.NavItem} the navitem to add
20019     */
20020     register : function(item)
20021     {
20022         this.tabs.push( item);
20023         item.navId = this.navId; // not really needed..
20024         this.addBullet();
20025     
20026     },
20027     
20028     getActivePanel : function()
20029     {
20030         var r = false;
20031         Roo.each(this.tabs, function(t) {
20032             if (t.active) {
20033                 r = t;
20034                 return false;
20035             }
20036             return null;
20037         });
20038         return r;
20039         
20040     },
20041     getPanelByName : function(n)
20042     {
20043         var r = false;
20044         Roo.each(this.tabs, function(t) {
20045             if (t.tabId == n) {
20046                 r = t;
20047                 return false;
20048             }
20049             return null;
20050         });
20051         return r;
20052     },
20053     indexOfPanel : function(p)
20054     {
20055         var r = false;
20056         Roo.each(this.tabs, function(t,i) {
20057             if (t.tabId == p.tabId) {
20058                 r = i;
20059                 return false;
20060             }
20061             return null;
20062         });
20063         return r;
20064     },
20065     /**
20066      * show a specific panel
20067      * @param {Roo.bootstrap.TabPanel|number|string} panel to change to (use the tabId to specify a specific one)
20068      * @return {boolean} false if panel was not shown (invalid entry or beforedeactivate fails.)
20069      */
20070     showPanel : function (pan)
20071     {
20072         if(this.transition || typeof(pan) == 'undefined'){
20073             Roo.log("waiting for the transitionend");
20074             return false;
20075         }
20076         
20077         if (typeof(pan) == 'number') {
20078             pan = this.tabs[pan];
20079         }
20080         
20081         if (typeof(pan) == 'string') {
20082             pan = this.getPanelByName(pan);
20083         }
20084         
20085         var cur = this.getActivePanel();
20086         
20087         if(!pan || !cur){
20088             Roo.log('pan or acitve pan is undefined');
20089             return false;
20090         }
20091         
20092         if (pan.tabId == this.getActivePanel().tabId) {
20093             return true;
20094         }
20095         
20096         if (false === cur.fireEvent('beforedeactivate')) {
20097             return false;
20098         }
20099         
20100         if(this.bullets > 0 && !Roo.isTouch){
20101             this.setActiveBullet(this.indexOfPanel(pan));
20102         }
20103         
20104         if (this.carousel && typeof(Roo.get(document.body).dom.style.transition) != 'undefined') {
20105             
20106             //class="carousel-item carousel-item-next carousel-item-left"
20107             
20108             this.transition = true;
20109             var dir = this.indexOfPanel(pan) > this.indexOfPanel(cur)  ? 'next' : 'prev';
20110             var lr = dir == 'next' ? 'left' : 'right';
20111             pan.el.addClass(dir); // or prev
20112             pan.el.addClass('carousel-item-' + dir); // or prev
20113             pan.el.dom.offsetWidth; // find the offset with - causing a reflow?
20114             cur.el.addClass(lr); // or right
20115             pan.el.addClass(lr);
20116             cur.el.addClass('carousel-item-' +lr); // or right
20117             pan.el.addClass('carousel-item-' +lr);
20118             
20119             
20120             var _this = this;
20121             cur.el.on('transitionend', function() {
20122                 Roo.log("trans end?");
20123                 
20124                 pan.el.removeClass([lr,dir, 'carousel-item-' + lr, 'carousel-item-' + dir]);
20125                 pan.setActive(true);
20126                 
20127                 cur.el.removeClass([lr, 'carousel-item-' + lr]);
20128                 cur.setActive(false);
20129                 
20130                 _this.transition = false;
20131                 
20132             }, this, { single:  true } );
20133             
20134             return true;
20135         }
20136         
20137         cur.setActive(false);
20138         pan.setActive(true);
20139         
20140         return true;
20141         
20142     },
20143     showPanelNext : function()
20144     {
20145         var i = this.indexOfPanel(this.getActivePanel());
20146         
20147         if (i >= this.tabs.length - 1 && !this.autoslide) {
20148             return;
20149         }
20150         
20151         if (i >= this.tabs.length - 1 && this.autoslide) {
20152             i = -1;
20153         }
20154         
20155         this.showPanel(this.tabs[i+1]);
20156     },
20157     
20158     showPanelPrev : function()
20159     {
20160         var i = this.indexOfPanel(this.getActivePanel());
20161         
20162         if (i  < 1 && !this.autoslide) {
20163             return;
20164         }
20165         
20166         if (i < 1 && this.autoslide) {
20167             i = this.tabs.length;
20168         }
20169         
20170         this.showPanel(this.tabs[i-1]);
20171     },
20172     
20173     
20174     addBullet: function()
20175     {
20176         if(!this.bullets || Roo.isTouch){
20177             return;
20178         }
20179         var ctr = this.el.select('.carousel-bullets',true).first();
20180         var i = this.el.select('.carousel-bullets .bullet',true).getCount() ;
20181         var bullet = ctr.createChild({
20182             cls : 'bullet bullet-' + i
20183         },ctr.dom.lastChild);
20184         
20185         
20186         var _this = this;
20187         
20188         bullet.on('click', (function(e, el, o, ii, t){
20189
20190             e.preventDefault();
20191
20192             this.showPanel(ii);
20193
20194             if(this.autoslide && this.slideFn){
20195                 clearInterval(this.slideFn);
20196                 this.slideFn = window.setInterval(function() {
20197                     _this.showPanelNext();
20198                 }, this.timer);
20199             }
20200
20201         }).createDelegate(this, [i, bullet], true));
20202                 
20203         
20204     },
20205      
20206     setActiveBullet : function(i)
20207     {
20208         if(Roo.isTouch){
20209             return;
20210         }
20211         
20212         Roo.each(this.el.select('.bullet', true).elements, function(el){
20213             el.removeClass('selected');
20214         });
20215
20216         var bullet = this.el.select('.bullet-' + i, true).first();
20217         
20218         if(!bullet){
20219             return;
20220         }
20221         
20222         bullet.addClass('selected');
20223     }
20224     
20225     
20226   
20227 });
20228
20229  
20230
20231  
20232  
20233 Roo.apply(Roo.bootstrap.TabGroup, {
20234     
20235     groups: {},
20236      /**
20237     * register a Navigation Group
20238     * @param {Roo.bootstrap.NavGroup} the navgroup to add
20239     */
20240     register : function(navgrp)
20241     {
20242         this.groups[navgrp.navId] = navgrp;
20243         
20244     },
20245     /**
20246     * fetch a Navigation Group based on the navigation ID
20247     * if one does not exist , it will get created.
20248     * @param {string} the navgroup to add
20249     * @returns {Roo.bootstrap.NavGroup} the navgroup 
20250     */
20251     get: function(navId) {
20252         if (typeof(this.groups[navId]) == 'undefined') {
20253             this.register(new Roo.bootstrap.TabGroup({ navId : navId }));
20254         }
20255         return this.groups[navId] ;
20256     }
20257     
20258     
20259     
20260 });
20261
20262  /*
20263  * - LGPL
20264  *
20265  * TabPanel
20266  * 
20267  */
20268
20269 /**
20270  * @class Roo.bootstrap.TabPanel
20271  * @extends Roo.bootstrap.Component
20272  * Bootstrap TabPanel class
20273  * @cfg {Boolean} active panel active
20274  * @cfg {String} html panel content
20275  * @cfg {String} tabId  unique tab ID (will be autogenerated if not set. - used to match TabItem to Panel)
20276  * @cfg {String} navId The Roo.bootstrap.NavGroup which triggers show hide ()
20277  * @cfg {String} href click to link..
20278  * @cfg {Boolean} touchSlide if swiping slides tab to next panel (default off)
20279  * 
20280  * 
20281  * @constructor
20282  * Create a new TabPanel
20283  * @param {Object} config The config object
20284  */
20285
20286 Roo.bootstrap.TabPanel = function(config){
20287     Roo.bootstrap.TabPanel.superclass.constructor.call(this, config);
20288     this.addEvents({
20289         /**
20290              * @event changed
20291              * Fires when the active status changes
20292              * @param {Roo.bootstrap.TabPanel} this
20293              * @param {Boolean} state the new state
20294             
20295          */
20296         'changed': true,
20297         /**
20298              * @event beforedeactivate
20299              * Fires before a tab is de-activated - can be used to do validation on a form.
20300              * @param {Roo.bootstrap.TabPanel} this
20301              * @return {Boolean} false if there is an error
20302             
20303          */
20304         'beforedeactivate': true
20305      });
20306     
20307     this.tabId = this.tabId || Roo.id();
20308   
20309 };
20310
20311 Roo.extend(Roo.bootstrap.TabPanel, Roo.bootstrap.Component,  {
20312     
20313     active: false,
20314     html: false,
20315     tabId: false,
20316     navId : false,
20317     href : '',
20318     touchSlide : false,
20319     getAutoCreate : function(){
20320         
20321         
20322         var cfg = {
20323             tag: 'div',
20324             // item is needed for carousel - not sure if it has any effect otherwise
20325             cls: 'carousel-item tab-pane item' + ((this.href.length) ? ' clickable ' : ''),
20326             html: this.html || ''
20327         };
20328         
20329         if(this.active){
20330             cfg.cls += ' active';
20331         }
20332         
20333         if(this.tabId){
20334             cfg.tabId = this.tabId;
20335         }
20336         
20337         
20338         
20339         return cfg;
20340     },
20341     
20342     initEvents:  function()
20343     {
20344         var p = this.parent();
20345         
20346         this.navId = this.navId || p.navId;
20347         
20348         if (typeof(this.navId) != 'undefined') {
20349             // not really needed.. but just in case.. parent should be a NavGroup.
20350             var tg = Roo.bootstrap.TabGroup.get(this.navId);
20351             
20352             tg.register(this);
20353             
20354             var i = tg.tabs.length - 1;
20355             
20356             if(this.active && tg.bullets > 0 && i < tg.bullets){
20357                 tg.setActiveBullet(i);
20358             }
20359         }
20360         
20361         this.el.on('click', this.onClick, this);
20362         
20363         if(Roo.isTouch && this.touchSlide){
20364             this.el.on("touchstart", this.onTouchStart, this);
20365             this.el.on("touchmove", this.onTouchMove, this);
20366             this.el.on("touchend", this.onTouchEnd, this);
20367         }
20368         
20369     },
20370     
20371     onRender : function(ct, position)
20372     {
20373         Roo.bootstrap.TabPanel.superclass.onRender.call(this, ct, position);
20374     },
20375     
20376     setActive : function(state)
20377     {
20378         Roo.log("panel - set active " + this.tabId + "=" + state);
20379         
20380         this.active = state;
20381         if (!state) {
20382             this.el.removeClass('active');
20383             
20384         } else  if (!this.el.hasClass('active')) {
20385             this.el.addClass('active');
20386         }
20387         
20388         this.fireEvent('changed', this, state);
20389     },
20390     
20391     onClick : function(e)
20392     {
20393         e.preventDefault();
20394         
20395         if(!this.href.length){
20396             return;
20397         }
20398         
20399         window.location.href = this.href;
20400     },
20401     
20402     startX : 0,
20403     startY : 0,
20404     endX : 0,
20405     endY : 0,
20406     swiping : false,
20407     
20408     onTouchStart : function(e)
20409     {
20410         this.swiping = false;
20411         
20412         this.startX = e.browserEvent.touches[0].clientX;
20413         this.startY = e.browserEvent.touches[0].clientY;
20414     },
20415     
20416     onTouchMove : function(e)
20417     {
20418         this.swiping = true;
20419         
20420         this.endX = e.browserEvent.touches[0].clientX;
20421         this.endY = e.browserEvent.touches[0].clientY;
20422     },
20423     
20424     onTouchEnd : function(e)
20425     {
20426         if(!this.swiping){
20427             this.onClick(e);
20428             return;
20429         }
20430         
20431         var tabGroup = this.parent();
20432         
20433         if(this.endX > this.startX){ // swiping right
20434             tabGroup.showPanelPrev();
20435             return;
20436         }
20437         
20438         if(this.startX > this.endX){ // swiping left
20439             tabGroup.showPanelNext();
20440             return;
20441         }
20442     }
20443     
20444     
20445 });
20446  
20447
20448  
20449
20450  /*
20451  * - LGPL
20452  *
20453  * DateField
20454  * 
20455  */
20456
20457 /**
20458  * @class Roo.bootstrap.DateField
20459  * @extends Roo.bootstrap.Input
20460  * Bootstrap DateField class
20461  * @cfg {Number} weekStart default 0
20462  * @cfg {String} viewMode default empty, (months|years)
20463  * @cfg {String} minViewMode default empty, (months|years)
20464  * @cfg {Number} startDate default -Infinity
20465  * @cfg {Number} endDate default Infinity
20466  * @cfg {Boolean} todayHighlight default false
20467  * @cfg {Boolean} todayBtn default false
20468  * @cfg {Boolean} calendarWeeks default false
20469  * @cfg {Object} daysOfWeekDisabled default empty
20470  * @cfg {Boolean} singleMode default false (true | false)
20471  * 
20472  * @cfg {Boolean} keyboardNavigation default true
20473  * @cfg {String} language default en
20474  * 
20475  * @constructor
20476  * Create a new DateField
20477  * @param {Object} config The config object
20478  */
20479
20480 Roo.bootstrap.DateField = function(config){
20481     Roo.bootstrap.DateField.superclass.constructor.call(this, config);
20482      this.addEvents({
20483             /**
20484              * @event show
20485              * Fires when this field show.
20486              * @param {Roo.bootstrap.DateField} this
20487              * @param {Mixed} date The date value
20488              */
20489             show : true,
20490             /**
20491              * @event show
20492              * Fires when this field hide.
20493              * @param {Roo.bootstrap.DateField} this
20494              * @param {Mixed} date The date value
20495              */
20496             hide : true,
20497             /**
20498              * @event select
20499              * Fires when select a date.
20500              * @param {Roo.bootstrap.DateField} this
20501              * @param {Mixed} date The date value
20502              */
20503             select : true,
20504             /**
20505              * @event beforeselect
20506              * Fires when before select a date.
20507              * @param {Roo.bootstrap.DateField} this
20508              * @param {Mixed} date The date value
20509              */
20510             beforeselect : true
20511         });
20512 };
20513
20514 Roo.extend(Roo.bootstrap.DateField, Roo.bootstrap.Input,  {
20515     
20516     /**
20517      * @cfg {String} format
20518      * The default date format string which can be overriden for localization support.  The format must be
20519      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
20520      */
20521     format : "m/d/y",
20522     /**
20523      * @cfg {String} altFormats
20524      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
20525      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
20526      */
20527     altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
20528     
20529     weekStart : 0,
20530     
20531     viewMode : '',
20532     
20533     minViewMode : '',
20534     
20535     todayHighlight : false,
20536     
20537     todayBtn: false,
20538     
20539     language: 'en',
20540     
20541     keyboardNavigation: true,
20542     
20543     calendarWeeks: false,
20544     
20545     startDate: -Infinity,
20546     
20547     endDate: Infinity,
20548     
20549     daysOfWeekDisabled: [],
20550     
20551     _events: [],
20552     
20553     singleMode : false,
20554     
20555     UTCDate: function()
20556     {
20557         return new Date(Date.UTC.apply(Date, arguments));
20558     },
20559     
20560     UTCToday: function()
20561     {
20562         var today = new Date();
20563         return this.UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
20564     },
20565     
20566     getDate: function() {
20567             var d = this.getUTCDate();
20568             return new Date(d.getTime() + (d.getTimezoneOffset()*60000));
20569     },
20570     
20571     getUTCDate: function() {
20572             return this.date;
20573     },
20574     
20575     setDate: function(d) {
20576             this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000)));
20577     },
20578     
20579     setUTCDate: function(d) {
20580             this.date = d;
20581             this.setValue(this.formatDate(this.date));
20582     },
20583         
20584     onRender: function(ct, position)
20585     {
20586         
20587         Roo.bootstrap.DateField.superclass.onRender.call(this, ct, position);
20588         
20589         this.language = this.language || 'en';
20590         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : this.language.split('-')[0];
20591         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : "en";
20592         
20593         this.isRTL = Roo.bootstrap.DateField.dates[this.language].rtl || false;
20594         this.format = this.format || 'm/d/y';
20595         this.isInline = false;
20596         this.isInput = true;
20597         this.component = this.el.select('.add-on', true).first() || false;
20598         this.component = (this.component && this.component.length === 0) ? false : this.component;
20599         this.hasInput = this.component && this.inputEl().length;
20600         
20601         if (typeof(this.minViewMode === 'string')) {
20602             switch (this.minViewMode) {
20603                 case 'months':
20604                     this.minViewMode = 1;
20605                     break;
20606                 case 'years':
20607                     this.minViewMode = 2;
20608                     break;
20609                 default:
20610                     this.minViewMode = 0;
20611                     break;
20612             }
20613         }
20614         
20615         if (typeof(this.viewMode === 'string')) {
20616             switch (this.viewMode) {
20617                 case 'months':
20618                     this.viewMode = 1;
20619                     break;
20620                 case 'years':
20621                     this.viewMode = 2;
20622                     break;
20623                 default:
20624                     this.viewMode = 0;
20625                     break;
20626             }
20627         }
20628                 
20629         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.DateField.template);
20630         
20631 //        this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.DateField.template);
20632         
20633         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
20634         
20635         this.picker().on('mousedown', this.onMousedown, this);
20636         this.picker().on('click', this.onClick, this);
20637         
20638         this.picker().addClass('datepicker-dropdown');
20639         
20640         this.startViewMode = this.viewMode;
20641         
20642         if(this.singleMode){
20643             Roo.each(this.picker().select('thead > tr > th', true).elements, function(v){
20644                 v.setVisibilityMode(Roo.Element.DISPLAY);
20645                 v.hide();
20646             });
20647             
20648             Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
20649                 v.setStyle('width', '189px');
20650             });
20651         }
20652         
20653         Roo.each(this.picker().select('tfoot th.today', true).elements, function(v){
20654             if(!this.calendarWeeks){
20655                 v.remove();
20656                 return;
20657             }
20658             
20659             v.dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
20660             v.attr('colspan', function(i, val){
20661                 return parseInt(val) + 1;
20662             });
20663         });
20664                         
20665         
20666         this.weekEnd = this.weekStart === 0 ? 6 : this.weekStart - 1;
20667         
20668         this.setStartDate(this.startDate);
20669         this.setEndDate(this.endDate);
20670         
20671         this.setDaysOfWeekDisabled(this.daysOfWeekDisabled);
20672         
20673         this.fillDow();
20674         this.fillMonths();
20675         this.update();
20676         this.showMode();
20677         
20678         if(this.isInline) {
20679             this.showPopup();
20680         }
20681     },
20682     
20683     picker : function()
20684     {
20685         return this.pickerEl;
20686 //        return this.el.select('.datepicker', true).first();
20687     },
20688     
20689     fillDow: function()
20690     {
20691         var dowCnt = this.weekStart;
20692         
20693         var dow = {
20694             tag: 'tr',
20695             cn: [
20696                 
20697             ]
20698         };
20699         
20700         if(this.calendarWeeks){
20701             dow.cn.push({
20702                 tag: 'th',
20703                 cls: 'cw',
20704                 html: '&nbsp;'
20705             })
20706         }
20707         
20708         while (dowCnt < this.weekStart + 7) {
20709             dow.cn.push({
20710                 tag: 'th',
20711                 cls: 'dow',
20712                 html: Roo.bootstrap.DateField.dates[this.language].daysMin[(dowCnt++)%7]
20713             });
20714         }
20715         
20716         this.picker().select('>.datepicker-days thead', true).first().createChild(dow);
20717     },
20718     
20719     fillMonths: function()
20720     {    
20721         var i = 0;
20722         var months = this.picker().select('>.datepicker-months td', true).first();
20723         
20724         months.dom.innerHTML = '';
20725         
20726         while (i < 12) {
20727             var month = {
20728                 tag: 'span',
20729                 cls: 'month',
20730                 html: Roo.bootstrap.DateField.dates[this.language].monthsShort[i++]
20731             };
20732             
20733             months.createChild(month);
20734         }
20735         
20736     },
20737     
20738     update: function()
20739     {
20740         this.date = (typeof(this.date) === 'undefined' || ((typeof(this.date) === 'string') && !this.date.length)) ? this.UTCToday() : (typeof(this.date) === 'string') ? this.parseDate(this.date) : this.date;
20741         
20742         if (this.date < this.startDate) {
20743             this.viewDate = new Date(this.startDate);
20744         } else if (this.date > this.endDate) {
20745             this.viewDate = new Date(this.endDate);
20746         } else {
20747             this.viewDate = new Date(this.date);
20748         }
20749         
20750         this.fill();
20751     },
20752     
20753     fill: function() 
20754     {
20755         var d = new Date(this.viewDate),
20756                 year = d.getUTCFullYear(),
20757                 month = d.getUTCMonth(),
20758                 startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity,
20759                 startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
20760                 endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
20761                 endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
20762                 currentDate = this.date && this.date.valueOf(),
20763                 today = this.UTCToday();
20764         
20765         this.picker().select('>.datepicker-days thead th.switch', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].months[month]+' '+year;
20766         
20767 //        this.picker().select('>tfoot th.today', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
20768         
20769 //        this.picker.select('>tfoot th.today').
20770 //                                              .text(dates[this.language].today)
20771 //                                              .toggle(this.todayBtn !== false);
20772     
20773         this.updateNavArrows();
20774         this.fillMonths();
20775                                                 
20776         var prevMonth = this.UTCDate(year, month-1, 28,0,0,0,0),
20777         
20778         day = prevMonth.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
20779          
20780         prevMonth.setUTCDate(day);
20781         
20782         prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7);
20783         
20784         var nextMonth = new Date(prevMonth);
20785         
20786         nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
20787         
20788         nextMonth = nextMonth.valueOf();
20789         
20790         var fillMonths = false;
20791         
20792         this.picker().select('>.datepicker-days tbody',true).first().dom.innerHTML = '';
20793         
20794         while(prevMonth.valueOf() <= nextMonth) {
20795             var clsName = '';
20796             
20797             if (prevMonth.getUTCDay() === this.weekStart) {
20798                 if(fillMonths){
20799                     this.picker().select('>.datepicker-days tbody',true).first().createChild(fillMonths);
20800                 }
20801                     
20802                 fillMonths = {
20803                     tag: 'tr',
20804                     cn: []
20805                 };
20806                 
20807                 if(this.calendarWeeks){
20808                     // ISO 8601: First week contains first thursday.
20809                     // ISO also states week starts on Monday, but we can be more abstract here.
20810                     var
20811                     // Start of current week: based on weekstart/current date
20812                     ws = new Date(+prevMonth + (this.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5),
20813                     // Thursday of this week
20814                     th = new Date(+ws + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
20815                     // First Thursday of year, year from thursday
20816                     yth = new Date(+(yth = this.UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5),
20817                     // Calendar week: ms between thursdays, div ms per day, div 7 days
20818                     calWeek =  (th - yth) / 864e5 / 7 + 1;
20819                     
20820                     fillMonths.cn.push({
20821                         tag: 'td',
20822                         cls: 'cw',
20823                         html: calWeek
20824                     });
20825                 }
20826             }
20827             
20828             if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
20829                 clsName += ' old';
20830             } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
20831                 clsName += ' new';
20832             }
20833             if (this.todayHighlight &&
20834                 prevMonth.getUTCFullYear() == today.getFullYear() &&
20835                 prevMonth.getUTCMonth() == today.getMonth() &&
20836                 prevMonth.getUTCDate() == today.getDate()) {
20837                 clsName += ' today';
20838             }
20839             
20840             if (currentDate && prevMonth.valueOf() === currentDate) {
20841                 clsName += ' active';
20842             }
20843             
20844             if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate ||
20845                     this.daysOfWeekDisabled.indexOf(prevMonth.getUTCDay()) !== -1) {
20846                     clsName += ' disabled';
20847             }
20848             
20849             fillMonths.cn.push({
20850                 tag: 'td',
20851                 cls: 'day ' + clsName,
20852                 html: prevMonth.getDate()
20853             });
20854             
20855             prevMonth.setDate(prevMonth.getDate()+1);
20856         }
20857           
20858         var currentYear = this.date && this.date.getUTCFullYear();
20859         var currentMonth = this.date && this.date.getUTCMonth();
20860         
20861         this.picker().select('>.datepicker-months th.switch',true).first().dom.innerHTML = year;
20862         
20863         Roo.each(this.picker().select('>.datepicker-months tbody span',true).elements, function(v,k){
20864             v.removeClass('active');
20865             
20866             if(currentYear === year && k === currentMonth){
20867                 v.addClass('active');
20868             }
20869             
20870             if (year < startYear || year > endYear || (year == startYear && k < startMonth) || (year == endYear && k > endMonth)) {
20871                 v.addClass('disabled');
20872             }
20873             
20874         });
20875         
20876         
20877         year = parseInt(year/10, 10) * 10;
20878         
20879         this.picker().select('>.datepicker-years th.switch', true).first().dom.innerHTML = year + '-' + (year + 9);
20880         
20881         this.picker().select('>.datepicker-years tbody td',true).first().dom.innerHTML = '';
20882         
20883         year -= 1;
20884         for (var i = -1; i < 11; i++) {
20885             this.picker().select('>.datepicker-years tbody td',true).first().createChild({
20886                 tag: 'span',
20887                 cls: 'year' + (i === -1 || i === 10 ? ' old' : '') + (currentYear === year ? ' active' : '') + (year < startYear || year > endYear ? ' disabled' : ''),
20888                 html: year
20889             });
20890             
20891             year += 1;
20892         }
20893     },
20894     
20895     showMode: function(dir) 
20896     {
20897         if (dir) {
20898             this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir));
20899         }
20900         
20901         Roo.each(this.picker().select('>div',true).elements, function(v){
20902             v.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
20903             v.hide();
20904         });
20905         this.picker().select('>.datepicker-'+Roo.bootstrap.DateField.modes[this.viewMode].clsName, true).first().show();
20906     },
20907     
20908     place: function()
20909     {
20910         if(this.isInline) {
20911             return;
20912         }
20913         
20914         this.picker().removeClass(['bottom', 'top']);
20915         
20916         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
20917             /*
20918              * place to the top of element!
20919              *
20920              */
20921             
20922             this.picker().addClass('top');
20923             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
20924             
20925             return;
20926         }
20927         
20928         this.picker().addClass('bottom');
20929         
20930         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
20931     },
20932     
20933     parseDate : function(value)
20934     {
20935         if(!value || value instanceof Date){
20936             return value;
20937         }
20938         var v = Date.parseDate(value, this.format);
20939         if (!v && (this.useIso || value.match(/^(\d{4})-0?(\d+)-0?(\d+)/))) {
20940             v = Date.parseDate(value, 'Y-m-d');
20941         }
20942         if(!v && this.altFormats){
20943             if(!this.altFormatsArray){
20944                 this.altFormatsArray = this.altFormats.split("|");
20945             }
20946             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
20947                 v = Date.parseDate(value, this.altFormatsArray[i]);
20948             }
20949         }
20950         return v;
20951     },
20952     
20953     formatDate : function(date, fmt)
20954     {   
20955         return (!date || !(date instanceof Date)) ?
20956         date : date.dateFormat(fmt || this.format);
20957     },
20958     
20959     onFocus : function()
20960     {
20961         Roo.bootstrap.DateField.superclass.onFocus.call(this);
20962         this.showPopup();
20963     },
20964     
20965     onBlur : function()
20966     {
20967         Roo.bootstrap.DateField.superclass.onBlur.call(this);
20968         
20969         var d = this.inputEl().getValue();
20970         
20971         this.setValue(d);
20972                 
20973         this.hidePopup();
20974     },
20975     
20976     showPopup : function()
20977     {
20978         this.picker().show();
20979         this.update();
20980         this.place();
20981         
20982         this.fireEvent('showpopup', this, this.date);
20983     },
20984     
20985     hidePopup : function()
20986     {
20987         if(this.isInline) {
20988             return;
20989         }
20990         this.picker().hide();
20991         this.viewMode = this.startViewMode;
20992         this.showMode();
20993         
20994         this.fireEvent('hidepopup', this, this.date);
20995         
20996     },
20997     
20998     onMousedown: function(e)
20999     {
21000         e.stopPropagation();
21001         e.preventDefault();
21002     },
21003     
21004     keyup: function(e)
21005     {
21006         Roo.bootstrap.DateField.superclass.keyup.call(this);
21007         this.update();
21008     },
21009
21010     setValue: function(v)
21011     {
21012         if(this.fireEvent('beforeselect', this, v) !== false){
21013             var d = new Date(this.parseDate(v) ).clearTime();
21014         
21015             if(isNaN(d.getTime())){
21016                 this.date = this.viewDate = '';
21017                 Roo.bootstrap.DateField.superclass.setValue.call(this, '');
21018                 return;
21019             }
21020
21021             v = this.formatDate(d);
21022
21023             Roo.bootstrap.DateField.superclass.setValue.call(this, v);
21024
21025             this.date = new Date(d.getTime() - d.getTimezoneOffset()*60000);
21026
21027             this.update();
21028
21029             this.fireEvent('select', this, this.date);
21030         }
21031     },
21032     
21033     getValue: function()
21034     {
21035         return this.formatDate(this.date);
21036     },
21037     
21038     fireKey: function(e)
21039     {
21040         if (!this.picker().isVisible()){
21041             if (e.keyCode == 27) { // allow escape to hide and re-show picker
21042                 this.showPopup();
21043             }
21044             return;
21045         }
21046         
21047         var dateChanged = false,
21048         dir, day, month,
21049         newDate, newViewDate;
21050         
21051         switch(e.keyCode){
21052             case 27: // escape
21053                 this.hidePopup();
21054                 e.preventDefault();
21055                 break;
21056             case 37: // left
21057             case 39: // right
21058                 if (!this.keyboardNavigation) {
21059                     break;
21060                 }
21061                 dir = e.keyCode == 37 ? -1 : 1;
21062                 
21063                 if (e.ctrlKey){
21064                     newDate = this.moveYear(this.date, dir);
21065                     newViewDate = this.moveYear(this.viewDate, dir);
21066                 } else if (e.shiftKey){
21067                     newDate = this.moveMonth(this.date, dir);
21068                     newViewDate = this.moveMonth(this.viewDate, dir);
21069                 } else {
21070                     newDate = new Date(this.date);
21071                     newDate.setUTCDate(this.date.getUTCDate() + dir);
21072                     newViewDate = new Date(this.viewDate);
21073                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
21074                 }
21075                 if (this.dateWithinRange(newDate)){
21076                     this.date = newDate;
21077                     this.viewDate = newViewDate;
21078                     this.setValue(this.formatDate(this.date));
21079 //                    this.update();
21080                     e.preventDefault();
21081                     dateChanged = true;
21082                 }
21083                 break;
21084             case 38: // up
21085             case 40: // down
21086                 if (!this.keyboardNavigation) {
21087                     break;
21088                 }
21089                 dir = e.keyCode == 38 ? -1 : 1;
21090                 if (e.ctrlKey){
21091                     newDate = this.moveYear(this.date, dir);
21092                     newViewDate = this.moveYear(this.viewDate, dir);
21093                 } else if (e.shiftKey){
21094                     newDate = this.moveMonth(this.date, dir);
21095                     newViewDate = this.moveMonth(this.viewDate, dir);
21096                 } else {
21097                     newDate = new Date(this.date);
21098                     newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
21099                     newViewDate = new Date(this.viewDate);
21100                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
21101                 }
21102                 if (this.dateWithinRange(newDate)){
21103                     this.date = newDate;
21104                     this.viewDate = newViewDate;
21105                     this.setValue(this.formatDate(this.date));
21106 //                    this.update();
21107                     e.preventDefault();
21108                     dateChanged = true;
21109                 }
21110                 break;
21111             case 13: // enter
21112                 this.setValue(this.formatDate(this.date));
21113                 this.hidePopup();
21114                 e.preventDefault();
21115                 break;
21116             case 9: // tab
21117                 this.setValue(this.formatDate(this.date));
21118                 this.hidePopup();
21119                 break;
21120             case 16: // shift
21121             case 17: // ctrl
21122             case 18: // alt
21123                 break;
21124             default :
21125                 this.hidePopup();
21126                 
21127         }
21128     },
21129     
21130     
21131     onClick: function(e) 
21132     {
21133         e.stopPropagation();
21134         e.preventDefault();
21135         
21136         var target = e.getTarget();
21137         
21138         if(target.nodeName.toLowerCase() === 'i'){
21139             target = Roo.get(target).dom.parentNode;
21140         }
21141         
21142         var nodeName = target.nodeName;
21143         var className = target.className;
21144         var html = target.innerHTML;
21145         //Roo.log(nodeName);
21146         
21147         switch(nodeName.toLowerCase()) {
21148             case 'th':
21149                 switch(className) {
21150                     case 'switch':
21151                         this.showMode(1);
21152                         break;
21153                     case 'prev':
21154                     case 'next':
21155                         var dir = Roo.bootstrap.DateField.modes[this.viewMode].navStep * (className == 'prev' ? -1 : 1);
21156                         switch(this.viewMode){
21157                                 case 0:
21158                                         this.viewDate = this.moveMonth(this.viewDate, dir);
21159                                         break;
21160                                 case 1:
21161                                 case 2:
21162                                         this.viewDate = this.moveYear(this.viewDate, dir);
21163                                         break;
21164                         }
21165                         this.fill();
21166                         break;
21167                     case 'today':
21168                         var date = new Date();
21169                         this.date = this.UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
21170 //                        this.fill()
21171                         this.setValue(this.formatDate(this.date));
21172                         
21173                         this.hidePopup();
21174                         break;
21175                 }
21176                 break;
21177             case 'span':
21178                 if (className.indexOf('disabled') < 0) {
21179                     this.viewDate.setUTCDate(1);
21180                     if (className.indexOf('month') > -1) {
21181                         this.viewDate.setUTCMonth(Roo.bootstrap.DateField.dates[this.language].monthsShort.indexOf(html));
21182                     } else {
21183                         var year = parseInt(html, 10) || 0;
21184                         this.viewDate.setUTCFullYear(year);
21185                         
21186                     }
21187                     
21188                     if(this.singleMode){
21189                         this.setValue(this.formatDate(this.viewDate));
21190                         this.hidePopup();
21191                         return;
21192                     }
21193                     
21194                     this.showMode(-1);
21195                     this.fill();
21196                 }
21197                 break;
21198                 
21199             case 'td':
21200                 //Roo.log(className);
21201                 if (className.indexOf('day') > -1 && className.indexOf('disabled') < 0 ){
21202                     var day = parseInt(html, 10) || 1;
21203                     var year = this.viewDate.getUTCFullYear(),
21204                         month = this.viewDate.getUTCMonth();
21205
21206                     if (className.indexOf('old') > -1) {
21207                         if(month === 0 ){
21208                             month = 11;
21209                             year -= 1;
21210                         }else{
21211                             month -= 1;
21212                         }
21213                     } else if (className.indexOf('new') > -1) {
21214                         if (month == 11) {
21215                             month = 0;
21216                             year += 1;
21217                         } else {
21218                             month += 1;
21219                         }
21220                     }
21221                     //Roo.log([year,month,day]);
21222                     this.date = this.UTCDate(year, month, day,0,0,0,0);
21223                     this.viewDate = this.UTCDate(year, month, Math.min(28, day),0,0,0,0);
21224 //                    this.fill();
21225                     //Roo.log(this.formatDate(this.date));
21226                     this.setValue(this.formatDate(this.date));
21227                     this.hidePopup();
21228                 }
21229                 break;
21230         }
21231     },
21232     
21233     setStartDate: function(startDate)
21234     {
21235         this.startDate = startDate || -Infinity;
21236         if (this.startDate !== -Infinity) {
21237             this.startDate = this.parseDate(this.startDate);
21238         }
21239         this.update();
21240         this.updateNavArrows();
21241     },
21242
21243     setEndDate: function(endDate)
21244     {
21245         this.endDate = endDate || Infinity;
21246         if (this.endDate !== Infinity) {
21247             this.endDate = this.parseDate(this.endDate);
21248         }
21249         this.update();
21250         this.updateNavArrows();
21251     },
21252     
21253     setDaysOfWeekDisabled: function(daysOfWeekDisabled)
21254     {
21255         this.daysOfWeekDisabled = daysOfWeekDisabled || [];
21256         if (typeof(this.daysOfWeekDisabled) !== 'object') {
21257             this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
21258         }
21259         this.daysOfWeekDisabled = this.daysOfWeekDisabled.map(function (d) {
21260             return parseInt(d, 10);
21261         });
21262         this.update();
21263         this.updateNavArrows();
21264     },
21265     
21266     updateNavArrows: function() 
21267     {
21268         if(this.singleMode){
21269             return;
21270         }
21271         
21272         var d = new Date(this.viewDate),
21273         year = d.getUTCFullYear(),
21274         month = d.getUTCMonth();
21275         
21276         Roo.each(this.picker().select('.prev', true).elements, function(v){
21277             v.show();
21278             switch (this.viewMode) {
21279                 case 0:
21280
21281                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
21282                         v.hide();
21283                     }
21284                     break;
21285                 case 1:
21286                 case 2:
21287                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
21288                         v.hide();
21289                     }
21290                     break;
21291             }
21292         });
21293         
21294         Roo.each(this.picker().select('.next', true).elements, function(v){
21295             v.show();
21296             switch (this.viewMode) {
21297                 case 0:
21298
21299                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
21300                         v.hide();
21301                     }
21302                     break;
21303                 case 1:
21304                 case 2:
21305                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
21306                         v.hide();
21307                     }
21308                     break;
21309             }
21310         })
21311     },
21312     
21313     moveMonth: function(date, dir)
21314     {
21315         if (!dir) {
21316             return date;
21317         }
21318         var new_date = new Date(date.valueOf()),
21319         day = new_date.getUTCDate(),
21320         month = new_date.getUTCMonth(),
21321         mag = Math.abs(dir),
21322         new_month, test;
21323         dir = dir > 0 ? 1 : -1;
21324         if (mag == 1){
21325             test = dir == -1
21326             // If going back one month, make sure month is not current month
21327             // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
21328             ? function(){
21329                 return new_date.getUTCMonth() == month;
21330             }
21331             // If going forward one month, make sure month is as expected
21332             // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
21333             : function(){
21334                 return new_date.getUTCMonth() != new_month;
21335             };
21336             new_month = month + dir;
21337             new_date.setUTCMonth(new_month);
21338             // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
21339             if (new_month < 0 || new_month > 11) {
21340                 new_month = (new_month + 12) % 12;
21341             }
21342         } else {
21343             // For magnitudes >1, move one month at a time...
21344             for (var i=0; i<mag; i++) {
21345                 // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
21346                 new_date = this.moveMonth(new_date, dir);
21347             }
21348             // ...then reset the day, keeping it in the new month
21349             new_month = new_date.getUTCMonth();
21350             new_date.setUTCDate(day);
21351             test = function(){
21352                 return new_month != new_date.getUTCMonth();
21353             };
21354         }
21355         // Common date-resetting loop -- if date is beyond end of month, make it
21356         // end of month
21357         while (test()){
21358             new_date.setUTCDate(--day);
21359             new_date.setUTCMonth(new_month);
21360         }
21361         return new_date;
21362     },
21363
21364     moveYear: function(date, dir)
21365     {
21366         return this.moveMonth(date, dir*12);
21367     },
21368
21369     dateWithinRange: function(date)
21370     {
21371         return date >= this.startDate && date <= this.endDate;
21372     },
21373
21374     
21375     remove: function() 
21376     {
21377         this.picker().remove();
21378     },
21379     
21380     validateValue : function(value)
21381     {
21382         if(this.getVisibilityEl().hasClass('hidden')){
21383             return true;
21384         }
21385         
21386         if(value.length < 1)  {
21387             if(this.allowBlank){
21388                 return true;
21389             }
21390             return false;
21391         }
21392         
21393         if(value.length < this.minLength){
21394             return false;
21395         }
21396         if(value.length > this.maxLength){
21397             return false;
21398         }
21399         if(this.vtype){
21400             var vt = Roo.form.VTypes;
21401             if(!vt[this.vtype](value, this)){
21402                 return false;
21403             }
21404         }
21405         if(typeof this.validator == "function"){
21406             var msg = this.validator(value);
21407             if(msg !== true){
21408                 return false;
21409             }
21410         }
21411         
21412         if(this.regex && !this.regex.test(value)){
21413             return false;
21414         }
21415         
21416         if(typeof(this.parseDate(value)) == 'undefined'){
21417             return false;
21418         }
21419         
21420         if (this.endDate !== Infinity && this.parseDate(value).getTime() > this.endDate.getTime()) {
21421             return false;
21422         }      
21423         
21424         if (this.startDate !== -Infinity && this.parseDate(value).getTime() < this.startDate.getTime()) {
21425             return false;
21426         } 
21427         
21428         
21429         return true;
21430     },
21431     
21432     reset : function()
21433     {
21434         this.date = this.viewDate = '';
21435         
21436         Roo.bootstrap.DateField.superclass.setValue.call(this, '');
21437     }
21438    
21439 });
21440
21441 Roo.apply(Roo.bootstrap.DateField,  {
21442     
21443     head : {
21444         tag: 'thead',
21445         cn: [
21446         {
21447             tag: 'tr',
21448             cn: [
21449             {
21450                 tag: 'th',
21451                 cls: 'prev',
21452                 html: '<i class="fa fa-arrow-left"/>'
21453             },
21454             {
21455                 tag: 'th',
21456                 cls: 'switch',
21457                 colspan: '5'
21458             },
21459             {
21460                 tag: 'th',
21461                 cls: 'next',
21462                 html: '<i class="fa fa-arrow-right"/>'
21463             }
21464
21465             ]
21466         }
21467         ]
21468     },
21469     
21470     content : {
21471         tag: 'tbody',
21472         cn: [
21473         {
21474             tag: 'tr',
21475             cn: [
21476             {
21477                 tag: 'td',
21478                 colspan: '7'
21479             }
21480             ]
21481         }
21482         ]
21483     },
21484     
21485     footer : {
21486         tag: 'tfoot',
21487         cn: [
21488         {
21489             tag: 'tr',
21490             cn: [
21491             {
21492                 tag: 'th',
21493                 colspan: '7',
21494                 cls: 'today'
21495             }
21496                     
21497             ]
21498         }
21499         ]
21500     },
21501     
21502     dates:{
21503         en: {
21504             days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
21505             daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
21506             daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
21507             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
21508             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
21509             today: "Today"
21510         }
21511     },
21512     
21513     modes: [
21514     {
21515         clsName: 'days',
21516         navFnc: 'Month',
21517         navStep: 1
21518     },
21519     {
21520         clsName: 'months',
21521         navFnc: 'FullYear',
21522         navStep: 1
21523     },
21524     {
21525         clsName: 'years',
21526         navFnc: 'FullYear',
21527         navStep: 10
21528     }]
21529 });
21530
21531 Roo.apply(Roo.bootstrap.DateField,  {
21532   
21533     template : {
21534         tag: 'div',
21535         cls: 'datepicker dropdown-menu roo-dynamic',
21536         cn: [
21537         {
21538             tag: 'div',
21539             cls: 'datepicker-days',
21540             cn: [
21541             {
21542                 tag: 'table',
21543                 cls: 'table-condensed',
21544                 cn:[
21545                 Roo.bootstrap.DateField.head,
21546                 {
21547                     tag: 'tbody'
21548                 },
21549                 Roo.bootstrap.DateField.footer
21550                 ]
21551             }
21552             ]
21553         },
21554         {
21555             tag: 'div',
21556             cls: 'datepicker-months',
21557             cn: [
21558             {
21559                 tag: 'table',
21560                 cls: 'table-condensed',
21561                 cn:[
21562                 Roo.bootstrap.DateField.head,
21563                 Roo.bootstrap.DateField.content,
21564                 Roo.bootstrap.DateField.footer
21565                 ]
21566             }
21567             ]
21568         },
21569         {
21570             tag: 'div',
21571             cls: 'datepicker-years',
21572             cn: [
21573             {
21574                 tag: 'table',
21575                 cls: 'table-condensed',
21576                 cn:[
21577                 Roo.bootstrap.DateField.head,
21578                 Roo.bootstrap.DateField.content,
21579                 Roo.bootstrap.DateField.footer
21580                 ]
21581             }
21582             ]
21583         }
21584         ]
21585     }
21586 });
21587
21588  
21589
21590  /*
21591  * - LGPL
21592  *
21593  * TimeField
21594  * 
21595  */
21596
21597 /**
21598  * @class Roo.bootstrap.TimeField
21599  * @extends Roo.bootstrap.Input
21600  * Bootstrap DateField class
21601  * 
21602  * 
21603  * @constructor
21604  * Create a new TimeField
21605  * @param {Object} config The config object
21606  */
21607
21608 Roo.bootstrap.TimeField = function(config){
21609     Roo.bootstrap.TimeField.superclass.constructor.call(this, config);
21610     this.addEvents({
21611             /**
21612              * @event show
21613              * Fires when this field show.
21614              * @param {Roo.bootstrap.DateField} thisthis
21615              * @param {Mixed} date The date value
21616              */
21617             show : true,
21618             /**
21619              * @event show
21620              * Fires when this field hide.
21621              * @param {Roo.bootstrap.DateField} this
21622              * @param {Mixed} date The date value
21623              */
21624             hide : true,
21625             /**
21626              * @event select
21627              * Fires when select a date.
21628              * @param {Roo.bootstrap.DateField} this
21629              * @param {Mixed} date The date value
21630              */
21631             select : true
21632         });
21633 };
21634
21635 Roo.extend(Roo.bootstrap.TimeField, Roo.bootstrap.Input,  {
21636     
21637     /**
21638      * @cfg {String} format
21639      * The default time format string which can be overriden for localization support.  The format must be
21640      * valid according to {@link Date#parseDate} (defaults to 'H:i').
21641      */
21642     format : "H:i",
21643        
21644     onRender: function(ct, position)
21645     {
21646         
21647         Roo.bootstrap.TimeField.superclass.onRender.call(this, ct, position);
21648                 
21649         this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.TimeField.template);
21650         
21651         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
21652         
21653         this.pop = this.picker().select('>.datepicker-time',true).first();
21654         this.pop.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
21655         
21656         this.picker().on('mousedown', this.onMousedown, this);
21657         this.picker().on('click', this.onClick, this);
21658         
21659         this.picker().addClass('datepicker-dropdown');
21660     
21661         this.fillTime();
21662         this.update();
21663             
21664         this.pop.select('span.hours-up', true).first().on('click', this.onIncrementHours, this);
21665         this.pop.select('span.hours-down', true).first().on('click', this.onDecrementHours, this);
21666         this.pop.select('span.minutes-up', true).first().on('click', this.onIncrementMinutes, this);
21667         this.pop.select('span.minutes-down', true).first().on('click', this.onDecrementMinutes, this);
21668         this.pop.select('button.period', true).first().on('click', this.onTogglePeriod, this);
21669         this.pop.select('button.ok', true).first().on('click', this.setTime, this);
21670
21671     },
21672     
21673     fireKey: function(e){
21674         if (!this.picker().isVisible()){
21675             if (e.keyCode == 27) { // allow escape to hide and re-show picker
21676                 this.show();
21677             }
21678             return;
21679         }
21680
21681         e.preventDefault();
21682         
21683         switch(e.keyCode){
21684             case 27: // escape
21685                 this.hide();
21686                 break;
21687             case 37: // left
21688             case 39: // right
21689                 this.onTogglePeriod();
21690                 break;
21691             case 38: // up
21692                 this.onIncrementMinutes();
21693                 break;
21694             case 40: // down
21695                 this.onDecrementMinutes();
21696                 break;
21697             case 13: // enter
21698             case 9: // tab
21699                 this.setTime();
21700                 break;
21701         }
21702     },
21703     
21704     onClick: function(e) {
21705         e.stopPropagation();
21706         e.preventDefault();
21707     },
21708     
21709     picker : function()
21710     {
21711         return this.el.select('.datepicker', true).first();
21712     },
21713     
21714     fillTime: function()
21715     {    
21716         var time = this.pop.select('tbody', true).first();
21717         
21718         time.dom.innerHTML = '';
21719         
21720         time.createChild({
21721             tag: 'tr',
21722             cn: [
21723                 {
21724                     tag: 'td',
21725                     cn: [
21726                         {
21727                             tag: 'a',
21728                             href: '#',
21729                             cls: 'btn',
21730                             cn: [
21731                                 {
21732                                     tag: 'span',
21733                                     cls: 'hours-up glyphicon glyphicon-chevron-up'
21734                                 }
21735                             ]
21736                         } 
21737                     ]
21738                 },
21739                 {
21740                     tag: 'td',
21741                     cls: 'separator'
21742                 },
21743                 {
21744                     tag: 'td',
21745                     cn: [
21746                         {
21747                             tag: 'a',
21748                             href: '#',
21749                             cls: 'btn',
21750                             cn: [
21751                                 {
21752                                     tag: 'span',
21753                                     cls: 'minutes-up glyphicon glyphicon-chevron-up'
21754                                 }
21755                             ]
21756                         }
21757                     ]
21758                 },
21759                 {
21760                     tag: 'td',
21761                     cls: 'separator'
21762                 }
21763             ]
21764         });
21765         
21766         time.createChild({
21767             tag: 'tr',
21768             cn: [
21769                 {
21770                     tag: 'td',
21771                     cn: [
21772                         {
21773                             tag: 'span',
21774                             cls: 'timepicker-hour',
21775                             html: '00'
21776                         }  
21777                     ]
21778                 },
21779                 {
21780                     tag: 'td',
21781                     cls: 'separator',
21782                     html: ':'
21783                 },
21784                 {
21785                     tag: 'td',
21786                     cn: [
21787                         {
21788                             tag: 'span',
21789                             cls: 'timepicker-minute',
21790                             html: '00'
21791                         }  
21792                     ]
21793                 },
21794                 {
21795                     tag: 'td',
21796                     cls: 'separator'
21797                 },
21798                 {
21799                     tag: 'td',
21800                     cn: [
21801                         {
21802                             tag: 'button',
21803                             type: 'button',
21804                             cls: 'btn btn-primary period',
21805                             html: 'AM'
21806                             
21807                         }
21808                     ]
21809                 }
21810             ]
21811         });
21812         
21813         time.createChild({
21814             tag: 'tr',
21815             cn: [
21816                 {
21817                     tag: 'td',
21818                     cn: [
21819                         {
21820                             tag: 'a',
21821                             href: '#',
21822                             cls: 'btn',
21823                             cn: [
21824                                 {
21825                                     tag: 'span',
21826                                     cls: 'hours-down glyphicon glyphicon-chevron-down'
21827                                 }
21828                             ]
21829                         }
21830                     ]
21831                 },
21832                 {
21833                     tag: 'td',
21834                     cls: 'separator'
21835                 },
21836                 {
21837                     tag: 'td',
21838                     cn: [
21839                         {
21840                             tag: 'a',
21841                             href: '#',
21842                             cls: 'btn',
21843                             cn: [
21844                                 {
21845                                     tag: 'span',
21846                                     cls: 'minutes-down glyphicon glyphicon-chevron-down'
21847                                 }
21848                             ]
21849                         }
21850                     ]
21851                 },
21852                 {
21853                     tag: 'td',
21854                     cls: 'separator'
21855                 }
21856             ]
21857         });
21858         
21859     },
21860     
21861     update: function()
21862     {
21863         
21864         this.time = (typeof(this.time) === 'undefined') ? new Date() : this.time;
21865         
21866         this.fill();
21867     },
21868     
21869     fill: function() 
21870     {
21871         var hours = this.time.getHours();
21872         var minutes = this.time.getMinutes();
21873         var period = 'AM';
21874         
21875         if(hours > 11){
21876             period = 'PM';
21877         }
21878         
21879         if(hours == 0){
21880             hours = 12;
21881         }
21882         
21883         
21884         if(hours > 12){
21885             hours = hours - 12;
21886         }
21887         
21888         if(hours < 10){
21889             hours = '0' + hours;
21890         }
21891         
21892         if(minutes < 10){
21893             minutes = '0' + minutes;
21894         }
21895         
21896         this.pop.select('.timepicker-hour', true).first().dom.innerHTML = hours;
21897         this.pop.select('.timepicker-minute', true).first().dom.innerHTML = minutes;
21898         this.pop.select('button', true).first().dom.innerHTML = period;
21899         
21900     },
21901     
21902     place: function()
21903     {   
21904         this.picker().removeClass(['bottom-left', 'bottom-right', 'top-left', 'top-right']);
21905         
21906         var cls = ['bottom'];
21907         
21908         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){ // top
21909             cls.pop();
21910             cls.push('top');
21911         }
21912         
21913         cls.push('right');
21914         
21915         if((Roo.lib.Dom.getViewWidth() + Roo.get(document.body).getScroll().left) - (this.inputEl().getLeft() + this.picker().getWidth()) < 0){ // left
21916             cls.pop();
21917             cls.push('left');
21918         }
21919         
21920         this.picker().addClass(cls.join('-'));
21921         
21922         var _this = this;
21923         
21924         Roo.each(cls, function(c){
21925             if(c == 'bottom'){
21926                 _this.picker().setTop(_this.inputEl().getHeight());
21927                 return;
21928             }
21929             if(c == 'top'){
21930                 _this.picker().setTop(0 - _this.picker().getHeight());
21931                 return;
21932             }
21933             
21934             if(c == 'left'){
21935                 _this.picker().setLeft(_this.inputEl().getLeft() + _this.inputEl().getWidth() - _this.el.getLeft() - _this.picker().getWidth());
21936                 return;
21937             }
21938             if(c == 'right'){
21939                 _this.picker().setLeft(_this.inputEl().getLeft() - _this.el.getLeft());
21940                 return;
21941             }
21942         });
21943         
21944     },
21945   
21946     onFocus : function()
21947     {
21948         Roo.bootstrap.TimeField.superclass.onFocus.call(this);
21949         this.show();
21950     },
21951     
21952     onBlur : function()
21953     {
21954         Roo.bootstrap.TimeField.superclass.onBlur.call(this);
21955         this.hide();
21956     },
21957     
21958     show : function()
21959     {
21960         this.picker().show();
21961         this.pop.show();
21962         this.update();
21963         this.place();
21964         
21965         this.fireEvent('show', this, this.date);
21966     },
21967     
21968     hide : function()
21969     {
21970         this.picker().hide();
21971         this.pop.hide();
21972         
21973         this.fireEvent('hide', this, this.date);
21974     },
21975     
21976     setTime : function()
21977     {
21978         this.hide();
21979         this.setValue(this.time.format(this.format));
21980         
21981         this.fireEvent('select', this, this.date);
21982         
21983         
21984     },
21985     
21986     onMousedown: function(e){
21987         e.stopPropagation();
21988         e.preventDefault();
21989     },
21990     
21991     onIncrementHours: function()
21992     {
21993         Roo.log('onIncrementHours');
21994         this.time = this.time.add(Date.HOUR, 1);
21995         this.update();
21996         
21997     },
21998     
21999     onDecrementHours: function()
22000     {
22001         Roo.log('onDecrementHours');
22002         this.time = this.time.add(Date.HOUR, -1);
22003         this.update();
22004     },
22005     
22006     onIncrementMinutes: function()
22007     {
22008         Roo.log('onIncrementMinutes');
22009         this.time = this.time.add(Date.MINUTE, 1);
22010         this.update();
22011     },
22012     
22013     onDecrementMinutes: function()
22014     {
22015         Roo.log('onDecrementMinutes');
22016         this.time = this.time.add(Date.MINUTE, -1);
22017         this.update();
22018     },
22019     
22020     onTogglePeriod: function()
22021     {
22022         Roo.log('onTogglePeriod');
22023         this.time = this.time.add(Date.HOUR, 12);
22024         this.update();
22025     }
22026     
22027    
22028 });
22029
22030 Roo.apply(Roo.bootstrap.TimeField,  {
22031     
22032     content : {
22033         tag: 'tbody',
22034         cn: [
22035             {
22036                 tag: 'tr',
22037                 cn: [
22038                 {
22039                     tag: 'td',
22040                     colspan: '7'
22041                 }
22042                 ]
22043             }
22044         ]
22045     },
22046     
22047     footer : {
22048         tag: 'tfoot',
22049         cn: [
22050             {
22051                 tag: 'tr',
22052                 cn: [
22053                 {
22054                     tag: 'th',
22055                     colspan: '7',
22056                     cls: '',
22057                     cn: [
22058                         {
22059                             tag: 'button',
22060                             cls: 'btn btn-info ok',
22061                             html: 'OK'
22062                         }
22063                     ]
22064                 }
22065
22066                 ]
22067             }
22068         ]
22069     }
22070 });
22071
22072 Roo.apply(Roo.bootstrap.TimeField,  {
22073   
22074     template : {
22075         tag: 'div',
22076         cls: 'datepicker dropdown-menu',
22077         cn: [
22078             {
22079                 tag: 'div',
22080                 cls: 'datepicker-time',
22081                 cn: [
22082                 {
22083                     tag: 'table',
22084                     cls: 'table-condensed',
22085                     cn:[
22086                     Roo.bootstrap.TimeField.content,
22087                     Roo.bootstrap.TimeField.footer
22088                     ]
22089                 }
22090                 ]
22091             }
22092         ]
22093     }
22094 });
22095
22096  
22097
22098  /*
22099  * - LGPL
22100  *
22101  * MonthField
22102  * 
22103  */
22104
22105 /**
22106  * @class Roo.bootstrap.MonthField
22107  * @extends Roo.bootstrap.Input
22108  * Bootstrap MonthField class
22109  * 
22110  * @cfg {String} language default en
22111  * 
22112  * @constructor
22113  * Create a new MonthField
22114  * @param {Object} config The config object
22115  */
22116
22117 Roo.bootstrap.MonthField = function(config){
22118     Roo.bootstrap.MonthField.superclass.constructor.call(this, config);
22119     
22120     this.addEvents({
22121         /**
22122          * @event show
22123          * Fires when this field show.
22124          * @param {Roo.bootstrap.MonthField} this
22125          * @param {Mixed} date The date value
22126          */
22127         show : true,
22128         /**
22129          * @event show
22130          * Fires when this field hide.
22131          * @param {Roo.bootstrap.MonthField} this
22132          * @param {Mixed} date The date value
22133          */
22134         hide : true,
22135         /**
22136          * @event select
22137          * Fires when select a date.
22138          * @param {Roo.bootstrap.MonthField} this
22139          * @param {String} oldvalue The old value
22140          * @param {String} newvalue The new value
22141          */
22142         select : true
22143     });
22144 };
22145
22146 Roo.extend(Roo.bootstrap.MonthField, Roo.bootstrap.Input,  {
22147     
22148     onRender: function(ct, position)
22149     {
22150         
22151         Roo.bootstrap.MonthField.superclass.onRender.call(this, ct, position);
22152         
22153         this.language = this.language || 'en';
22154         this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : this.language.split('-')[0];
22155         this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : "en";
22156         
22157         this.isRTL = Roo.bootstrap.MonthField.dates[this.language].rtl || false;
22158         this.isInline = false;
22159         this.isInput = true;
22160         this.component = this.el.select('.add-on', true).first() || false;
22161         this.component = (this.component && this.component.length === 0) ? false : this.component;
22162         this.hasInput = this.component && this.inputEL().length;
22163         
22164         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.MonthField.template);
22165         
22166         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
22167         
22168         this.picker().on('mousedown', this.onMousedown, this);
22169         this.picker().on('click', this.onClick, this);
22170         
22171         this.picker().addClass('datepicker-dropdown');
22172         
22173         Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
22174             v.setStyle('width', '189px');
22175         });
22176         
22177         this.fillMonths();
22178         
22179         this.update();
22180         
22181         if(this.isInline) {
22182             this.show();
22183         }
22184         
22185     },
22186     
22187     setValue: function(v, suppressEvent)
22188     {   
22189         var o = this.getValue();
22190         
22191         Roo.bootstrap.MonthField.superclass.setValue.call(this, v);
22192         
22193         this.update();
22194
22195         if(suppressEvent !== true){
22196             this.fireEvent('select', this, o, v);
22197         }
22198         
22199     },
22200     
22201     getValue: function()
22202     {
22203         return this.value;
22204     },
22205     
22206     onClick: function(e) 
22207     {
22208         e.stopPropagation();
22209         e.preventDefault();
22210         
22211         var target = e.getTarget();
22212         
22213         if(target.nodeName.toLowerCase() === 'i'){
22214             target = Roo.get(target).dom.parentNode;
22215         }
22216         
22217         var nodeName = target.nodeName;
22218         var className = target.className;
22219         var html = target.innerHTML;
22220         
22221         if(nodeName.toLowerCase() != 'span' || className.indexOf('disabled') > -1 || className.indexOf('month') == -1){
22222             return;
22223         }
22224         
22225         this.vIndex = Roo.bootstrap.MonthField.dates[this.language].monthsShort.indexOf(html);
22226         
22227         this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
22228         
22229         this.hide();
22230                         
22231     },
22232     
22233     picker : function()
22234     {
22235         return this.pickerEl;
22236     },
22237     
22238     fillMonths: function()
22239     {    
22240         var i = 0;
22241         var months = this.picker().select('>.datepicker-months td', true).first();
22242         
22243         months.dom.innerHTML = '';
22244         
22245         while (i < 12) {
22246             var month = {
22247                 tag: 'span',
22248                 cls: 'month',
22249                 html: Roo.bootstrap.MonthField.dates[this.language].monthsShort[i++]
22250             };
22251             
22252             months.createChild(month);
22253         }
22254         
22255     },
22256     
22257     update: function()
22258     {
22259         var _this = this;
22260         
22261         if(typeof(this.vIndex) == 'undefined' && this.value.length){
22262             this.vIndex = Roo.bootstrap.MonthField.dates[this.language].months.indexOf(this.value);
22263         }
22264         
22265         Roo.each(this.pickerEl.select('> .datepicker-months tbody > tr > td > span', true).elements, function(e, k){
22266             e.removeClass('active');
22267             
22268             if(typeof(_this.vIndex) != 'undefined' && k == _this.vIndex){
22269                 e.addClass('active');
22270             }
22271         })
22272     },
22273     
22274     place: function()
22275     {
22276         if(this.isInline) {
22277             return;
22278         }
22279         
22280         this.picker().removeClass(['bottom', 'top']);
22281         
22282         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
22283             /*
22284              * place to the top of element!
22285              *
22286              */
22287             
22288             this.picker().addClass('top');
22289             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
22290             
22291             return;
22292         }
22293         
22294         this.picker().addClass('bottom');
22295         
22296         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
22297     },
22298     
22299     onFocus : function()
22300     {
22301         Roo.bootstrap.MonthField.superclass.onFocus.call(this);
22302         this.show();
22303     },
22304     
22305     onBlur : function()
22306     {
22307         Roo.bootstrap.MonthField.superclass.onBlur.call(this);
22308         
22309         var d = this.inputEl().getValue();
22310         
22311         this.setValue(d);
22312                 
22313         this.hide();
22314     },
22315     
22316     show : function()
22317     {
22318         this.picker().show();
22319         this.picker().select('>.datepicker-months', true).first().show();
22320         this.update();
22321         this.place();
22322         
22323         this.fireEvent('show', this, this.date);
22324     },
22325     
22326     hide : function()
22327     {
22328         if(this.isInline) {
22329             return;
22330         }
22331         this.picker().hide();
22332         this.fireEvent('hide', this, this.date);
22333         
22334     },
22335     
22336     onMousedown: function(e)
22337     {
22338         e.stopPropagation();
22339         e.preventDefault();
22340     },
22341     
22342     keyup: function(e)
22343     {
22344         Roo.bootstrap.MonthField.superclass.keyup.call(this);
22345         this.update();
22346     },
22347
22348     fireKey: function(e)
22349     {
22350         if (!this.picker().isVisible()){
22351             if (e.keyCode == 27)   {// allow escape to hide and re-show picker
22352                 this.show();
22353             }
22354             return;
22355         }
22356         
22357         var dir;
22358         
22359         switch(e.keyCode){
22360             case 27: // escape
22361                 this.hide();
22362                 e.preventDefault();
22363                 break;
22364             case 37: // left
22365             case 39: // right
22366                 dir = e.keyCode == 37 ? -1 : 1;
22367                 
22368                 this.vIndex = this.vIndex + dir;
22369                 
22370                 if(this.vIndex < 0){
22371                     this.vIndex = 0;
22372                 }
22373                 
22374                 if(this.vIndex > 11){
22375                     this.vIndex = 11;
22376                 }
22377                 
22378                 if(isNaN(this.vIndex)){
22379                     this.vIndex = 0;
22380                 }
22381                 
22382                 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
22383                 
22384                 break;
22385             case 38: // up
22386             case 40: // down
22387                 
22388                 dir = e.keyCode == 38 ? -1 : 1;
22389                 
22390                 this.vIndex = this.vIndex + dir * 4;
22391                 
22392                 if(this.vIndex < 0){
22393                     this.vIndex = 0;
22394                 }
22395                 
22396                 if(this.vIndex > 11){
22397                     this.vIndex = 11;
22398                 }
22399                 
22400                 if(isNaN(this.vIndex)){
22401                     this.vIndex = 0;
22402                 }
22403                 
22404                 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
22405                 break;
22406                 
22407             case 13: // enter
22408                 
22409                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
22410                     this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
22411                 }
22412                 
22413                 this.hide();
22414                 e.preventDefault();
22415                 break;
22416             case 9: // tab
22417                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
22418                     this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
22419                 }
22420                 this.hide();
22421                 break;
22422             case 16: // shift
22423             case 17: // ctrl
22424             case 18: // alt
22425                 break;
22426             default :
22427                 this.hide();
22428                 
22429         }
22430     },
22431     
22432     remove: function() 
22433     {
22434         this.picker().remove();
22435     }
22436    
22437 });
22438
22439 Roo.apply(Roo.bootstrap.MonthField,  {
22440     
22441     content : {
22442         tag: 'tbody',
22443         cn: [
22444         {
22445             tag: 'tr',
22446             cn: [
22447             {
22448                 tag: 'td',
22449                 colspan: '7'
22450             }
22451             ]
22452         }
22453         ]
22454     },
22455     
22456     dates:{
22457         en: {
22458             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
22459             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
22460         }
22461     }
22462 });
22463
22464 Roo.apply(Roo.bootstrap.MonthField,  {
22465   
22466     template : {
22467         tag: 'div',
22468         cls: 'datepicker dropdown-menu roo-dynamic',
22469         cn: [
22470             {
22471                 tag: 'div',
22472                 cls: 'datepicker-months',
22473                 cn: [
22474                 {
22475                     tag: 'table',
22476                     cls: 'table-condensed',
22477                     cn:[
22478                         Roo.bootstrap.DateField.content
22479                     ]
22480                 }
22481                 ]
22482             }
22483         ]
22484     }
22485 });
22486
22487  
22488
22489  
22490  /*
22491  * - LGPL
22492  *
22493  * CheckBox
22494  * 
22495  */
22496
22497 /**
22498  * @class Roo.bootstrap.CheckBox
22499  * @extends Roo.bootstrap.Input
22500  * Bootstrap CheckBox class
22501  * 
22502  * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
22503  * @cfg {String} inputValue The value that should go into the generated input element's value when checked.
22504  * @cfg {String} boxLabel The text that appears beside the checkbox
22505  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the checkbox
22506  * @cfg {Boolean} checked initnal the element
22507  * @cfg {Boolean} inline inline the element (default false)
22508  * @cfg {String} groupId the checkbox group id // normal just use for checkbox
22509  * @cfg {String} tooltip label tooltip
22510  * 
22511  * @constructor
22512  * Create a new CheckBox
22513  * @param {Object} config The config object
22514  */
22515
22516 Roo.bootstrap.CheckBox = function(config){
22517     Roo.bootstrap.CheckBox.superclass.constructor.call(this, config);
22518    
22519     this.addEvents({
22520         /**
22521         * @event check
22522         * Fires when the element is checked or unchecked.
22523         * @param {Roo.bootstrap.CheckBox} this This input
22524         * @param {Boolean} checked The new checked value
22525         */
22526        check : true,
22527        /**
22528         * @event click
22529         * Fires when the element is click.
22530         * @param {Roo.bootstrap.CheckBox} this This input
22531         */
22532        click : true
22533     });
22534     
22535 };
22536
22537 Roo.extend(Roo.bootstrap.CheckBox, Roo.bootstrap.Input,  {
22538   
22539     inputType: 'checkbox',
22540     inputValue: 1,
22541     valueOff: 0,
22542     boxLabel: false,
22543     checked: false,
22544     weight : false,
22545     inline: false,
22546     tooltip : '',
22547     
22548     // checkbox success does not make any sense really.. 
22549     invalidClass : "",
22550     validClass : "",
22551     
22552     
22553     getAutoCreate : function()
22554     {
22555         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
22556         
22557         var id = Roo.id();
22558         
22559         var cfg = {};
22560         
22561         cfg.cls = 'form-group form-check ' + this.inputType; //input-group
22562         
22563         if(this.inline){
22564             cfg.cls += ' ' + this.inputType + '-inline  form-check-inline';
22565         }
22566         
22567         var input =  {
22568             tag: 'input',
22569             id : id,
22570             type : this.inputType,
22571             value : this.inputValue,
22572             cls : 'roo-' + this.inputType, //'form-box',
22573             placeholder : this.placeholder || ''
22574             
22575         };
22576         
22577         if(this.inputType != 'radio'){
22578             var hidden =  {
22579                 tag: 'input',
22580                 type : 'hidden',
22581                 cls : 'roo-hidden-value',
22582                 value : this.checked ? this.inputValue : this.valueOff
22583             };
22584         }
22585         
22586             
22587         if (this.weight) { // Validity check?
22588             cfg.cls += " " + this.inputType + "-" + this.weight;
22589         }
22590         
22591         if (this.disabled) {
22592             input.disabled=true;
22593         }
22594         
22595         if(this.checked){
22596             input.checked = this.checked;
22597         }
22598         
22599         if (this.name) {
22600             
22601             input.name = this.name;
22602             
22603             if(this.inputType != 'radio'){
22604                 hidden.name = this.name;
22605                 input.name = '_hidden_' + this.name;
22606             }
22607         }
22608         
22609         if (this.size) {
22610             input.cls += ' input-' + this.size;
22611         }
22612         
22613         var settings=this;
22614         
22615         ['xs','sm','md','lg'].map(function(size){
22616             if (settings[size]) {
22617                 cfg.cls += ' col-' + size + '-' + settings[size];
22618             }
22619         });
22620         
22621         var inputblock = input;
22622          
22623         if (this.before || this.after) {
22624             
22625             inputblock = {
22626                 cls : 'input-group',
22627                 cn :  [] 
22628             };
22629             
22630             if (this.before) {
22631                 inputblock.cn.push({
22632                     tag :'span',
22633                     cls : 'input-group-addon',
22634                     html : this.before
22635                 });
22636             }
22637             
22638             inputblock.cn.push(input);
22639             
22640             if(this.inputType != 'radio'){
22641                 inputblock.cn.push(hidden);
22642             }
22643             
22644             if (this.after) {
22645                 inputblock.cn.push({
22646                     tag :'span',
22647                     cls : 'input-group-addon',
22648                     html : this.after
22649                 });
22650             }
22651             
22652         }
22653         var boxLabelCfg = false;
22654         
22655         if(this.boxLabel){
22656            
22657             boxLabelCfg = {
22658                 tag: 'label',
22659                 //'for': id, // box label is handled by onclick - so no for...
22660                 cls: 'box-label',
22661                 html: this.boxLabel
22662             };
22663             if(this.tooltip){
22664                 boxLabelCfg.tooltip = this.tooltip;
22665             }
22666              
22667         }
22668         
22669         
22670         if (align ==='left' && this.fieldLabel.length) {
22671 //                Roo.log("left and has label");
22672             cfg.cn = [
22673                 {
22674                     tag: 'label',
22675                     'for' :  id,
22676                     cls : 'control-label',
22677                     html : this.fieldLabel
22678                 },
22679                 {
22680                     cls : "", 
22681                     cn: [
22682                         inputblock
22683                     ]
22684                 }
22685             ];
22686             
22687             if (boxLabelCfg) {
22688                 cfg.cn[1].cn.push(boxLabelCfg);
22689             }
22690             
22691             if(this.labelWidth > 12){
22692                 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
22693             }
22694             
22695             if(this.labelWidth < 13 && this.labelmd == 0){
22696                 this.labelmd = this.labelWidth;
22697             }
22698             
22699             if(this.labellg > 0){
22700                 cfg.cn[0].cls += ' col-lg-' + this.labellg;
22701                 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
22702             }
22703             
22704             if(this.labelmd > 0){
22705                 cfg.cn[0].cls += ' col-md-' + this.labelmd;
22706                 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
22707             }
22708             
22709             if(this.labelsm > 0){
22710                 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
22711                 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
22712             }
22713             
22714             if(this.labelxs > 0){
22715                 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
22716                 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
22717             }
22718             
22719         } else if ( this.fieldLabel.length) {
22720 //                Roo.log(" label");
22721                 cfg.cn = [
22722                    
22723                     {
22724                         tag: this.boxLabel ? 'span' : 'label',
22725                         'for': id,
22726                         cls: 'control-label box-input-label',
22727                         //cls : 'input-group-addon',
22728                         html : this.fieldLabel
22729                     },
22730                     
22731                     inputblock
22732                     
22733                 ];
22734                 if (boxLabelCfg) {
22735                     cfg.cn.push(boxLabelCfg);
22736                 }
22737
22738         } else {
22739             
22740 //                Roo.log(" no label && no align");
22741                 cfg.cn = [  inputblock ] ;
22742                 if (boxLabelCfg) {
22743                     cfg.cn.push(boxLabelCfg);
22744                 }
22745
22746                 
22747         }
22748         
22749        
22750         
22751         if(this.inputType != 'radio'){
22752             cfg.cn.push(hidden);
22753         }
22754         
22755         return cfg;
22756         
22757     },
22758     
22759     /**
22760      * return the real input element.
22761      */
22762     inputEl: function ()
22763     {
22764         return this.el.select('input.roo-' + this.inputType,true).first();
22765     },
22766     hiddenEl: function ()
22767     {
22768         return this.el.select('input.roo-hidden-value',true).first();
22769     },
22770     
22771     labelEl: function()
22772     {
22773         return this.el.select('label.control-label',true).first();
22774     },
22775     /* depricated... */
22776     
22777     label: function()
22778     {
22779         return this.labelEl();
22780     },
22781     
22782     boxLabelEl: function()
22783     {
22784         return this.el.select('label.box-label',true).first();
22785     },
22786     
22787     initEvents : function()
22788     {
22789 //        Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
22790         
22791         this.inputEl().on('click', this.onClick,  this);
22792         
22793         if (this.boxLabel) { 
22794             this.el.select('label.box-label',true).first().on('click', this.onClick,  this);
22795         }
22796         
22797         this.startValue = this.getValue();
22798         
22799         if(this.groupId){
22800             Roo.bootstrap.CheckBox.register(this);
22801         }
22802     },
22803     
22804     onClick : function(e)
22805     {   
22806         if(this.fireEvent('click', this, e) !== false){
22807             this.setChecked(!this.checked);
22808         }
22809         
22810     },
22811     
22812     setChecked : function(state,suppressEvent)
22813     {
22814         this.startValue = this.getValue();
22815
22816         if(this.inputType == 'radio'){
22817             
22818             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
22819                 e.dom.checked = false;
22820             });
22821             
22822             this.inputEl().dom.checked = true;
22823             
22824             this.inputEl().dom.value = this.inputValue;
22825             
22826             if(suppressEvent !== true){
22827                 this.fireEvent('check', this, true);
22828             }
22829             
22830             this.validate();
22831             
22832             return;
22833         }
22834         
22835         this.checked = state;
22836         
22837         this.inputEl().dom.checked = state;
22838         
22839         
22840         this.hiddenEl().dom.value = state ? this.inputValue : this.valueOff;
22841         
22842         if(suppressEvent !== true){
22843             this.fireEvent('check', this, state);
22844         }
22845         
22846         this.validate();
22847     },
22848     
22849     getValue : function()
22850     {
22851         if(this.inputType == 'radio'){
22852             return this.getGroupValue();
22853         }
22854         
22855         return this.hiddenEl().dom.value;
22856         
22857     },
22858     
22859     getGroupValue : function()
22860     {
22861         if(typeof(this.el.up('form').child('input[name='+this.name+']:checked', true)) == 'undefined'){
22862             return '';
22863         }
22864         
22865         return this.el.up('form').child('input[name='+this.name+']:checked', true).value;
22866     },
22867     
22868     setValue : function(v,suppressEvent)
22869     {
22870         if(this.inputType == 'radio'){
22871             this.setGroupValue(v, suppressEvent);
22872             return;
22873         }
22874         
22875         this.setChecked(((typeof(v) == 'undefined') ? this.checked : (String(v) === String(this.inputValue))), suppressEvent);
22876         
22877         this.validate();
22878     },
22879     
22880     setGroupValue : function(v, suppressEvent)
22881     {
22882         this.startValue = this.getValue();
22883         
22884         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
22885             e.dom.checked = false;
22886             
22887             if(e.dom.value == v){
22888                 e.dom.checked = true;
22889             }
22890         });
22891         
22892         if(suppressEvent !== true){
22893             this.fireEvent('check', this, true);
22894         }
22895
22896         this.validate();
22897         
22898         return;
22899     },
22900     
22901     validate : function()
22902     {
22903         if(this.getVisibilityEl().hasClass('hidden')){
22904             return true;
22905         }
22906         
22907         if(
22908                 this.disabled || 
22909                 (this.inputType == 'radio' && this.validateRadio()) ||
22910                 (this.inputType == 'checkbox' && this.validateCheckbox())
22911         ){
22912             this.markValid();
22913             return true;
22914         }
22915         
22916         this.markInvalid();
22917         return false;
22918     },
22919     
22920     validateRadio : function()
22921     {
22922         if(this.getVisibilityEl().hasClass('hidden')){
22923             return true;
22924         }
22925         
22926         if(this.allowBlank){
22927             return true;
22928         }
22929         
22930         var valid = false;
22931         
22932         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
22933             if(!e.dom.checked){
22934                 return;
22935             }
22936             
22937             valid = true;
22938             
22939             return false;
22940         });
22941         
22942         return valid;
22943     },
22944     
22945     validateCheckbox : function()
22946     {
22947         if(!this.groupId){
22948             return (this.getValue() == this.inputValue || this.allowBlank) ? true : false;
22949             //return (this.getValue() == this.inputValue) ? true : false;
22950         }
22951         
22952         var group = Roo.bootstrap.CheckBox.get(this.groupId);
22953         
22954         if(!group){
22955             return false;
22956         }
22957         
22958         var r = false;
22959         
22960         for(var i in group){
22961             if(group[i].el.isVisible(true)){
22962                 r = false;
22963                 break;
22964             }
22965             
22966             r = true;
22967         }
22968         
22969         for(var i in group){
22970             if(r){
22971                 break;
22972             }
22973             
22974             r = (group[i].getValue() == group[i].inputValue) ? true : false;
22975         }
22976         
22977         return r;
22978     },
22979     
22980     /**
22981      * Mark this field as valid
22982      */
22983     markValid : function()
22984     {
22985         var _this = this;
22986         
22987         this.fireEvent('valid', this);
22988         
22989         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
22990         
22991         if(this.groupId){
22992             label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
22993         }
22994         
22995         if(label){
22996             label.markValid();
22997         }
22998
22999         if(this.inputType == 'radio'){
23000             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23001                 var fg = e.findParent('.form-group', false, true);
23002                 if (Roo.bootstrap.version == 3) {
23003                     fg.removeClass([_this.invalidClass, _this.validClass]);
23004                     fg.addClass(_this.validClass);
23005                 } else {
23006                     fg.removeClass(['is-valid', 'is-invalid']);
23007                     fg.addClass('is-valid');
23008                 }
23009             });
23010             
23011             return;
23012         }
23013
23014         if(!this.groupId){
23015             var fg = this.el.findParent('.form-group', false, true);
23016             if (Roo.bootstrap.version == 3) {
23017                 fg.removeClass([this.invalidClass, this.validClass]);
23018                 fg.addClass(this.validClass);
23019             } else {
23020                 fg.removeClass(['is-valid', 'is-invalid']);
23021                 fg.addClass('is-valid');
23022             }
23023             return;
23024         }
23025         
23026         var group = Roo.bootstrap.CheckBox.get(this.groupId);
23027         
23028         if(!group){
23029             return;
23030         }
23031         
23032         for(var i in group){
23033             var fg = group[i].el.findParent('.form-group', false, true);
23034             if (Roo.bootstrap.version == 3) {
23035                 fg.removeClass([this.invalidClass, this.validClass]);
23036                 fg.addClass(this.validClass);
23037             } else {
23038                 fg.removeClass(['is-valid', 'is-invalid']);
23039                 fg.addClass('is-valid');
23040             }
23041         }
23042     },
23043     
23044      /**
23045      * Mark this field as invalid
23046      * @param {String} msg The validation message
23047      */
23048     markInvalid : function(msg)
23049     {
23050         if(this.allowBlank){
23051             return;
23052         }
23053         
23054         var _this = this;
23055         
23056         this.fireEvent('invalid', this, msg);
23057         
23058         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
23059         
23060         if(this.groupId){
23061             label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
23062         }
23063         
23064         if(label){
23065             label.markInvalid();
23066         }
23067             
23068         if(this.inputType == 'radio'){
23069             
23070             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23071                 var fg = e.findParent('.form-group', false, true);
23072                 if (Roo.bootstrap.version == 3) {
23073                     fg.removeClass([_this.invalidClass, _this.validClass]);
23074                     fg.addClass(_this.invalidClass);
23075                 } else {
23076                     fg.removeClass(['is-invalid', 'is-valid']);
23077                     fg.addClass('is-invalid');
23078                 }
23079             });
23080             
23081             return;
23082         }
23083         
23084         if(!this.groupId){
23085             var fg = this.el.findParent('.form-group', false, true);
23086             if (Roo.bootstrap.version == 3) {
23087                 fg.removeClass([_this.invalidClass, _this.validClass]);
23088                 fg.addClass(_this.invalidClass);
23089             } else {
23090                 fg.removeClass(['is-invalid', 'is-valid']);
23091                 fg.addClass('is-invalid');
23092             }
23093             return;
23094         }
23095         
23096         var group = Roo.bootstrap.CheckBox.get(this.groupId);
23097         
23098         if(!group){
23099             return;
23100         }
23101         
23102         for(var i in group){
23103             var fg = group[i].el.findParent('.form-group', false, true);
23104             if (Roo.bootstrap.version == 3) {
23105                 fg.removeClass([_this.invalidClass, _this.validClass]);
23106                 fg.addClass(_this.invalidClass);
23107             } else {
23108                 fg.removeClass(['is-invalid', 'is-valid']);
23109                 fg.addClass('is-invalid');
23110             }
23111         }
23112         
23113     },
23114     
23115     clearInvalid : function()
23116     {
23117         Roo.bootstrap.Input.prototype.clearInvalid.call(this);
23118         
23119         // this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
23120         
23121         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
23122         
23123         if (label && label.iconEl) {
23124             label.iconEl.removeClass([ label.validClass, label.invalidClass ]);
23125             label.iconEl.removeClass(['is-invalid', 'is-valid']);
23126         }
23127     },
23128     
23129     disable : function()
23130     {
23131         if(this.inputType != 'radio'){
23132             Roo.bootstrap.CheckBox.superclass.disable.call(this);
23133             return;
23134         }
23135         
23136         var _this = this;
23137         
23138         if(this.rendered){
23139             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23140                 _this.getActionEl().addClass(this.disabledClass);
23141                 e.dom.disabled = true;
23142             });
23143         }
23144         
23145         this.disabled = true;
23146         this.fireEvent("disable", this);
23147         return this;
23148     },
23149
23150     enable : function()
23151     {
23152         if(this.inputType != 'radio'){
23153             Roo.bootstrap.CheckBox.superclass.enable.call(this);
23154             return;
23155         }
23156         
23157         var _this = this;
23158         
23159         if(this.rendered){
23160             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23161                 _this.getActionEl().removeClass(this.disabledClass);
23162                 e.dom.disabled = false;
23163             });
23164         }
23165         
23166         this.disabled = false;
23167         this.fireEvent("enable", this);
23168         return this;
23169     },
23170     
23171     setBoxLabel : function(v)
23172     {
23173         this.boxLabel = v;
23174         
23175         if(this.rendered){
23176             this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
23177         }
23178     }
23179
23180 });
23181
23182 Roo.apply(Roo.bootstrap.CheckBox, {
23183     
23184     groups: {},
23185     
23186      /**
23187     * register a CheckBox Group
23188     * @param {Roo.bootstrap.CheckBox} the CheckBox to add
23189     */
23190     register : function(checkbox)
23191     {
23192         if(typeof(this.groups[checkbox.groupId]) == 'undefined'){
23193             this.groups[checkbox.groupId] = {};
23194         }
23195         
23196         if(this.groups[checkbox.groupId].hasOwnProperty(checkbox.name)){
23197             return;
23198         }
23199         
23200         this.groups[checkbox.groupId][checkbox.name] = checkbox;
23201         
23202     },
23203     /**
23204     * fetch a CheckBox Group based on the group ID
23205     * @param {string} the group ID
23206     * @returns {Roo.bootstrap.CheckBox} the CheckBox group
23207     */
23208     get: function(groupId) {
23209         if (typeof(this.groups[groupId]) == 'undefined') {
23210             return false;
23211         }
23212         
23213         return this.groups[groupId] ;
23214     }
23215     
23216     
23217 });
23218 /*
23219  * - LGPL
23220  *
23221  * RadioItem
23222  * 
23223  */
23224
23225 /**
23226  * @class Roo.bootstrap.Radio
23227  * @extends Roo.bootstrap.Component
23228  * Bootstrap Radio class
23229  * @cfg {String} boxLabel - the label associated
23230  * @cfg {String} value - the value of radio
23231  * 
23232  * @constructor
23233  * Create a new Radio
23234  * @param {Object} config The config object
23235  */
23236 Roo.bootstrap.Radio = function(config){
23237     Roo.bootstrap.Radio.superclass.constructor.call(this, config);
23238     
23239 };
23240
23241 Roo.extend(Roo.bootstrap.Radio, Roo.bootstrap.Component, {
23242     
23243     boxLabel : '',
23244     
23245     value : '',
23246     
23247     getAutoCreate : function()
23248     {
23249         var cfg = {
23250             tag : 'div',
23251             cls : 'form-group radio',
23252             cn : [
23253                 {
23254                     tag : 'label',
23255                     cls : 'box-label',
23256                     html : this.boxLabel
23257                 }
23258             ]
23259         };
23260         
23261         return cfg;
23262     },
23263     
23264     initEvents : function() 
23265     {
23266         this.parent().register(this);
23267         
23268         this.el.on('click', this.onClick, this);
23269         
23270     },
23271     
23272     onClick : function(e)
23273     {
23274         if(this.parent().fireEvent('click', this.parent(), this, e) !== false){
23275             this.setChecked(true);
23276         }
23277     },
23278     
23279     setChecked : function(state, suppressEvent)
23280     {
23281         this.parent().setValue(this.value, suppressEvent);
23282         
23283     },
23284     
23285     setBoxLabel : function(v)
23286     {
23287         this.boxLabel = v;
23288         
23289         if(this.rendered){
23290             this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
23291         }
23292     }
23293     
23294 });
23295  
23296
23297  /*
23298  * - LGPL
23299  *
23300  * Input
23301  * 
23302  */
23303
23304 /**
23305  * @class Roo.bootstrap.SecurePass
23306  * @extends Roo.bootstrap.Input
23307  * Bootstrap SecurePass class
23308  *
23309  * 
23310  * @constructor
23311  * Create a new SecurePass
23312  * @param {Object} config The config object
23313  */
23314  
23315 Roo.bootstrap.SecurePass = function (config) {
23316     // these go here, so the translation tool can replace them..
23317     this.errors = {
23318         PwdEmpty: "Please type a password, and then retype it to confirm.",
23319         PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
23320         PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
23321         PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
23322         IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
23323         FNInPwd: "Your password can't contain your first name. Please type a different password.",
23324         LNInPwd: "Your password can't contain your last name. Please type a different password.",
23325         TooWeak: "Your password is Too Weak."
23326     },
23327     this.meterLabel = "Password strength:";
23328     this.pwdStrengths = ["Too Weak", "Weak", "Medium", "Strong"];
23329     this.meterClass = [
23330         "roo-password-meter-tooweak", 
23331         "roo-password-meter-weak", 
23332         "roo-password-meter-medium", 
23333         "roo-password-meter-strong", 
23334         "roo-password-meter-grey"
23335     ];
23336     
23337     this.errors = {};
23338     
23339     Roo.bootstrap.SecurePass.superclass.constructor.call(this, config);
23340 }
23341
23342 Roo.extend(Roo.bootstrap.SecurePass, Roo.bootstrap.Input, {
23343     /**
23344      * @cfg {String/Object} errors A Error spec, or true for a default spec (defaults to
23345      * {
23346      *  PwdEmpty: "Please type a password, and then retype it to confirm.",
23347      *  PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
23348      *  PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
23349      *  PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
23350      *  IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
23351      *  FNInPwd: "Your password can't contain your first name. Please type a different password.",
23352      *  LNInPwd: "Your password can't contain your last name. Please type a different password."
23353      * })
23354      */
23355     // private
23356     
23357     meterWidth: 300,
23358     errorMsg :'',    
23359     errors: false,
23360     imageRoot: '/',
23361     /**
23362      * @cfg {String/Object} Label for the strength meter (defaults to
23363      * 'Password strength:')
23364      */
23365     // private
23366     meterLabel: '',
23367     /**
23368      * @cfg {String/Object} pwdStrengths A pwdStrengths spec, or true for a default spec (defaults to
23369      * ['Weak', 'Medium', 'Strong'])
23370      */
23371     // private    
23372     pwdStrengths: false,    
23373     // private
23374     strength: 0,
23375     // private
23376     _lastPwd: null,
23377     // private
23378     kCapitalLetter: 0,
23379     kSmallLetter: 1,
23380     kDigit: 2,
23381     kPunctuation: 3,
23382     
23383     insecure: false,
23384     // private
23385     initEvents: function ()
23386     {
23387         Roo.bootstrap.SecurePass.superclass.initEvents.call(this);
23388
23389         if (this.el.is('input[type=password]') && Roo.isSafari) {
23390             this.el.on('keydown', this.SafariOnKeyDown, this);
23391         }
23392
23393         this.el.on('keyup', this.checkStrength, this, {buffer: 50});
23394     },
23395     // private
23396     onRender: function (ct, position)
23397     {
23398         Roo.bootstrap.SecurePass.superclass.onRender.call(this, ct, position);
23399         this.wrap = this.el.wrap({cls: 'x-form-field-wrap'});
23400         this.trigger = this.wrap.createChild({tag: 'div', cls: 'StrengthMeter ' + this.triggerClass});
23401
23402         this.trigger.createChild({
23403                    cn: [
23404                     {
23405                     //id: 'PwdMeter',
23406                     tag: 'div',
23407                     cls: 'roo-password-meter-grey col-xs-12',
23408                     style: {
23409                         //width: 0,
23410                         //width: this.meterWidth + 'px'                                                
23411                         }
23412                     },
23413                     {                            
23414                          cls: 'roo-password-meter-text'                          
23415                     }
23416                 ]            
23417         });
23418
23419          
23420         if (this.hideTrigger) {
23421             this.trigger.setDisplayed(false);
23422         }
23423         this.setSize(this.width || '', this.height || '');
23424     },
23425     // private
23426     onDestroy: function ()
23427     {
23428         if (this.trigger) {
23429             this.trigger.removeAllListeners();
23430             this.trigger.remove();
23431         }
23432         if (this.wrap) {
23433             this.wrap.remove();
23434         }
23435         Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
23436     },
23437     // private
23438     checkStrength: function ()
23439     {
23440         var pwd = this.inputEl().getValue();
23441         if (pwd == this._lastPwd) {
23442             return;
23443         }
23444
23445         var strength;
23446         if (this.ClientSideStrongPassword(pwd)) {
23447             strength = 3;
23448         } else if (this.ClientSideMediumPassword(pwd)) {
23449             strength = 2;
23450         } else if (this.ClientSideWeakPassword(pwd)) {
23451             strength = 1;
23452         } else {
23453             strength = 0;
23454         }
23455         
23456         Roo.log('strength1: ' + strength);
23457         
23458         //var pm = this.trigger.child('div/div/div').dom;
23459         var pm = this.trigger.child('div/div');
23460         pm.removeClass(this.meterClass);
23461         pm.addClass(this.meterClass[strength]);
23462                 
23463         
23464         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
23465                 
23466         pt.innerHTML = this.meterLabel + '&nbsp;' + this.pwdStrengths[strength];
23467         
23468         this._lastPwd = pwd;
23469     },
23470     reset: function ()
23471     {
23472         Roo.bootstrap.SecurePass.superclass.reset.call(this);
23473         
23474         this._lastPwd = '';
23475         
23476         var pm = this.trigger.child('div/div');
23477         pm.removeClass(this.meterClass);
23478         pm.addClass('roo-password-meter-grey');        
23479         
23480         
23481         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
23482         
23483         pt.innerHTML = '';
23484         this.inputEl().dom.type='password';
23485     },
23486     // private
23487     validateValue: function (value)
23488     {
23489         if (!Roo.bootstrap.SecurePass.superclass.validateValue.call(this, value)) {
23490             return false;
23491         }
23492         if (value.length == 0) {
23493             if (this.allowBlank) {
23494                 this.clearInvalid();
23495                 return true;
23496             }
23497
23498             this.markInvalid(this.errors.PwdEmpty);
23499             this.errorMsg = this.errors.PwdEmpty;
23500             return false;
23501         }
23502         
23503         if(this.insecure){
23504             return true;
23505         }
23506         
23507         if (!value.match(/[\x21-\x7e]*/)) {
23508             this.markInvalid(this.errors.PwdBadChar);
23509             this.errorMsg = this.errors.PwdBadChar;
23510             return false;
23511         }
23512         if (value.length < 6) {
23513             this.markInvalid(this.errors.PwdShort);
23514             this.errorMsg = this.errors.PwdShort;
23515             return false;
23516         }
23517         if (value.length > 16) {
23518             this.markInvalid(this.errors.PwdLong);
23519             this.errorMsg = this.errors.PwdLong;
23520             return false;
23521         }
23522         var strength;
23523         if (this.ClientSideStrongPassword(value)) {
23524             strength = 3;
23525         } else if (this.ClientSideMediumPassword(value)) {
23526             strength = 2;
23527         } else if (this.ClientSideWeakPassword(value)) {
23528             strength = 1;
23529         } else {
23530             strength = 0;
23531         }
23532
23533         
23534         if (strength < 2) {
23535             //this.markInvalid(this.errors.TooWeak);
23536             this.errorMsg = this.errors.TooWeak;
23537             //return false;
23538         }
23539         
23540         
23541         console.log('strength2: ' + strength);
23542         
23543         //var pm = this.trigger.child('div/div/div').dom;
23544         
23545         var pm = this.trigger.child('div/div');
23546         pm.removeClass(this.meterClass);
23547         pm.addClass(this.meterClass[strength]);
23548                 
23549         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
23550                 
23551         pt.innerHTML = this.meterLabel + '&nbsp;' + this.pwdStrengths[strength];
23552         
23553         this.errorMsg = ''; 
23554         return true;
23555     },
23556     // private
23557     CharacterSetChecks: function (type)
23558     {
23559         this.type = type;
23560         this.fResult = false;
23561     },
23562     // private
23563     isctype: function (character, type)
23564     {
23565         switch (type) {  
23566             case this.kCapitalLetter:
23567                 if (character >= 'A' && character <= 'Z') {
23568                     return true;
23569                 }
23570                 break;
23571             
23572             case this.kSmallLetter:
23573                 if (character >= 'a' && character <= 'z') {
23574                     return true;
23575                 }
23576                 break;
23577             
23578             case this.kDigit:
23579                 if (character >= '0' && character <= '9') {
23580                     return true;
23581                 }
23582                 break;
23583             
23584             case this.kPunctuation:
23585                 if ('!@#$%^&*()_+-=\'";:[{]}|.>,</?`~'.indexOf(character) >= 0) {
23586                     return true;
23587                 }
23588                 break;
23589             
23590             default:
23591                 return false;
23592         }
23593
23594     },
23595     // private
23596     IsLongEnough: function (pwd, size)
23597     {
23598         return !(pwd == null || isNaN(size) || pwd.length < size);
23599     },
23600     // private
23601     SpansEnoughCharacterSets: function (word, nb)
23602     {
23603         if (!this.IsLongEnough(word, nb))
23604         {
23605             return false;
23606         }
23607
23608         var characterSetChecks = new Array(
23609             new this.CharacterSetChecks(this.kCapitalLetter), new this.CharacterSetChecks(this.kSmallLetter),
23610             new this.CharacterSetChecks(this.kDigit), new this.CharacterSetChecks(this.kPunctuation)
23611         );
23612         
23613         for (var index = 0; index < word.length; ++index) {
23614             for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
23615                 if (!characterSetChecks[nCharSet].fResult && this.isctype(word.charAt(index), characterSetChecks[nCharSet].type)) {
23616                     characterSetChecks[nCharSet].fResult = true;
23617                     break;
23618                 }
23619             }
23620         }
23621
23622         var nCharSets = 0;
23623         for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
23624             if (characterSetChecks[nCharSet].fResult) {
23625                 ++nCharSets;
23626             }
23627         }
23628
23629         if (nCharSets < nb) {
23630             return false;
23631         }
23632         return true;
23633     },
23634     // private
23635     ClientSideStrongPassword: function (pwd)
23636     {
23637         return this.IsLongEnough(pwd, 8) && this.SpansEnoughCharacterSets(pwd, 3);
23638     },
23639     // private
23640     ClientSideMediumPassword: function (pwd)
23641     {
23642         return this.IsLongEnough(pwd, 7) && this.SpansEnoughCharacterSets(pwd, 2);
23643     },
23644     // private
23645     ClientSideWeakPassword: function (pwd)
23646     {
23647         return this.IsLongEnough(pwd, 6) || !this.IsLongEnough(pwd, 0);
23648     }
23649           
23650 })//<script type="text/javascript">
23651
23652 /*
23653  * Based  Ext JS Library 1.1.1
23654  * Copyright(c) 2006-2007, Ext JS, LLC.
23655  * LGPL
23656  *
23657  */
23658  
23659 /**
23660  * @class Roo.HtmlEditorCore
23661  * @extends Roo.Component
23662  * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
23663  *
23664  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
23665  */
23666
23667 Roo.HtmlEditorCore = function(config){
23668     
23669     
23670     Roo.HtmlEditorCore.superclass.constructor.call(this, config);
23671     
23672     
23673     this.addEvents({
23674         /**
23675          * @event initialize
23676          * Fires when the editor is fully initialized (including the iframe)
23677          * @param {Roo.HtmlEditorCore} this
23678          */
23679         initialize: true,
23680         /**
23681          * @event activate
23682          * Fires when the editor is first receives the focus. Any insertion must wait
23683          * until after this event.
23684          * @param {Roo.HtmlEditorCore} this
23685          */
23686         activate: true,
23687          /**
23688          * @event beforesync
23689          * Fires before the textarea is updated with content from the editor iframe. Return false
23690          * to cancel the sync.
23691          * @param {Roo.HtmlEditorCore} this
23692          * @param {String} html
23693          */
23694         beforesync: true,
23695          /**
23696          * @event beforepush
23697          * Fires before the iframe editor is updated with content from the textarea. Return false
23698          * to cancel the push.
23699          * @param {Roo.HtmlEditorCore} this
23700          * @param {String} html
23701          */
23702         beforepush: true,
23703          /**
23704          * @event sync
23705          * Fires when the textarea is updated with content from the editor iframe.
23706          * @param {Roo.HtmlEditorCore} this
23707          * @param {String} html
23708          */
23709         sync: true,
23710          /**
23711          * @event push
23712          * Fires when the iframe editor is updated with content from the textarea.
23713          * @param {Roo.HtmlEditorCore} this
23714          * @param {String} html
23715          */
23716         push: true,
23717         
23718         /**
23719          * @event editorevent
23720          * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
23721          * @param {Roo.HtmlEditorCore} this
23722          */
23723         editorevent: true
23724         
23725     });
23726     
23727     // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
23728     
23729     // defaults : white / black...
23730     this.applyBlacklists();
23731     
23732     
23733     
23734 };
23735
23736
23737 Roo.extend(Roo.HtmlEditorCore, Roo.Component,  {
23738
23739
23740      /**
23741      * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field 
23742      */
23743     
23744     owner : false,
23745     
23746      /**
23747      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
23748      *                        Roo.resizable.
23749      */
23750     resizable : false,
23751      /**
23752      * @cfg {Number} height (in pixels)
23753      */   
23754     height: 300,
23755    /**
23756      * @cfg {Number} width (in pixels)
23757      */   
23758     width: 500,
23759     
23760     /**
23761      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
23762      * 
23763      */
23764     stylesheets: false,
23765     
23766     // id of frame..
23767     frameId: false,
23768     
23769     // private properties
23770     validationEvent : false,
23771     deferHeight: true,
23772     initialized : false,
23773     activated : false,
23774     sourceEditMode : false,
23775     onFocus : Roo.emptyFn,
23776     iframePad:3,
23777     hideMode:'offsets',
23778     
23779     clearUp: true,
23780     
23781     // blacklist + whitelisted elements..
23782     black: false,
23783     white: false,
23784      
23785     bodyCls : '',
23786
23787     /**
23788      * Protected method that will not generally be called directly. It
23789      * is called when the editor initializes the iframe with HTML contents. Override this method if you
23790      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
23791      */
23792     getDocMarkup : function(){
23793         // body styles..
23794         var st = '';
23795         
23796         // inherit styels from page...?? 
23797         if (this.stylesheets === false) {
23798             
23799             Roo.get(document.head).select('style').each(function(node) {
23800                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
23801             });
23802             
23803             Roo.get(document.head).select('link').each(function(node) { 
23804                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
23805             });
23806             
23807         } else if (!this.stylesheets.length) {
23808                 // simple..
23809                 st = '<style type="text/css">' +
23810                     'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
23811                    '</style>';
23812         } else {
23813             for (var i in this.stylesheets) { 
23814                 st += '<link rel="stylesheet" href="' + this.stylesheets[i] +'" type="text/css">';
23815             }
23816             
23817         }
23818         
23819         st +=  '<style type="text/css">' +
23820             'IMG { cursor: pointer } ' +
23821         '</style>';
23822
23823         var cls = 'roo-htmleditor-body';
23824         
23825         if(this.bodyCls.length){
23826             cls += ' ' + this.bodyCls;
23827         }
23828         
23829         return '<html><head>' + st  +
23830             //<style type="text/css">' +
23831             //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
23832             //'</style>' +
23833             ' </head><body contenteditable="true" data-enable-grammerly="true" class="' +  cls + '"></body></html>';
23834     },
23835
23836     // private
23837     onRender : function(ct, position)
23838     {
23839         var _t = this;
23840         //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
23841         this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
23842         
23843         
23844         this.el.dom.style.border = '0 none';
23845         this.el.dom.setAttribute('tabIndex', -1);
23846         this.el.addClass('x-hidden hide');
23847         
23848         
23849         
23850         if(Roo.isIE){ // fix IE 1px bogus margin
23851             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
23852         }
23853        
23854         
23855         this.frameId = Roo.id();
23856         
23857          
23858         
23859         var iframe = this.owner.wrap.createChild({
23860             tag: 'iframe',
23861             cls: 'form-control', // bootstrap..
23862             id: this.frameId,
23863             name: this.frameId,
23864             frameBorder : 'no',
23865             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
23866         }, this.el
23867         );
23868         
23869         
23870         this.iframe = iframe.dom;
23871
23872          this.assignDocWin();
23873         
23874         this.doc.designMode = 'on';
23875        
23876         this.doc.open();
23877         this.doc.write(this.getDocMarkup());
23878         this.doc.close();
23879
23880         
23881         var task = { // must defer to wait for browser to be ready
23882             run : function(){
23883                 //console.log("run task?" + this.doc.readyState);
23884                 this.assignDocWin();
23885                 if(this.doc.body || this.doc.readyState == 'complete'){
23886                     try {
23887                         this.doc.designMode="on";
23888                     } catch (e) {
23889                         return;
23890                     }
23891                     Roo.TaskMgr.stop(task);
23892                     this.initEditor.defer(10, this);
23893                 }
23894             },
23895             interval : 10,
23896             duration: 10000,
23897             scope: this
23898         };
23899         Roo.TaskMgr.start(task);
23900
23901     },
23902
23903     // private
23904     onResize : function(w, h)
23905     {
23906          Roo.log('resize: ' +w + ',' + h );
23907         //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
23908         if(!this.iframe){
23909             return;
23910         }
23911         if(typeof w == 'number'){
23912             
23913             this.iframe.style.width = w + 'px';
23914         }
23915         if(typeof h == 'number'){
23916             
23917             this.iframe.style.height = h + 'px';
23918             if(this.doc){
23919                 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
23920             }
23921         }
23922         
23923     },
23924
23925     /**
23926      * Toggles the editor between standard and source edit mode.
23927      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
23928      */
23929     toggleSourceEdit : function(sourceEditMode){
23930         
23931         this.sourceEditMode = sourceEditMode === true;
23932         
23933         if(this.sourceEditMode){
23934  
23935             Roo.get(this.iframe).addClass(['x-hidden','hide']);     //FIXME - what's the BS styles for these
23936             
23937         }else{
23938             Roo.get(this.iframe).removeClass(['x-hidden','hide']);
23939             //this.iframe.className = '';
23940             this.deferFocus();
23941         }
23942         //this.setSize(this.owner.wrap.getSize());
23943         //this.fireEvent('editmodechange', this, this.sourceEditMode);
23944     },
23945
23946     
23947   
23948
23949     /**
23950      * Protected method that will not generally be called directly. If you need/want
23951      * custom HTML cleanup, this is the method you should override.
23952      * @param {String} html The HTML to be cleaned
23953      * return {String} The cleaned HTML
23954      */
23955     cleanHtml : function(html){
23956         html = String(html);
23957         if(html.length > 5){
23958             if(Roo.isSafari){ // strip safari nonsense
23959                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
23960             }
23961         }
23962         if(html == '&nbsp;'){
23963             html = '';
23964         }
23965         return html;
23966     },
23967
23968     /**
23969      * HTML Editor -> Textarea
23970      * Protected method that will not generally be called directly. Syncs the contents
23971      * of the editor iframe with the textarea.
23972      */
23973     syncValue : function(){
23974         if(this.initialized){
23975             var bd = (this.doc.body || this.doc.documentElement);
23976             //this.cleanUpPaste(); -- this is done else where and causes havoc..
23977             var html = bd.innerHTML;
23978             if(Roo.isSafari){
23979                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
23980                 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
23981                 if(m && m[1]){
23982                     html = '<div style="'+m[0]+'">' + html + '</div>';
23983                 }
23984             }
23985             html = this.cleanHtml(html);
23986             // fix up the special chars.. normaly like back quotes in word...
23987             // however we do not want to do this with chinese..
23988             html = html.replace(/[\uD800-\uDBFF][\uDC00-\uDFFF]|[\u0080-\uFFFF]/g, function(match) {
23989                 
23990                 var cc = match.charCodeAt();
23991
23992                 // Get the character value, handling surrogate pairs
23993                 if (match.length == 2) {
23994                     // It's a surrogate pair, calculate the Unicode code point
23995                     var high = match.charCodeAt(0) - 0xD800;
23996                     var low  = match.charCodeAt(1) - 0xDC00;
23997                     cc = (high * 0x400) + low + 0x10000;
23998                 }  else if (
23999                     (cc >= 0x4E00 && cc < 0xA000 ) ||
24000                     (cc >= 0x3400 && cc < 0x4E00 ) ||
24001                     (cc >= 0xf900 && cc < 0xfb00 )
24002                 ) {
24003                         return match;
24004                 }  
24005          
24006                 // No, use a numeric entity. Here we brazenly (and possibly mistakenly)
24007                 return "&#" + cc + ";";
24008                 
24009                 
24010             });
24011             
24012             
24013              
24014             if(this.owner.fireEvent('beforesync', this, html) !== false){
24015                 this.el.dom.value = html;
24016                 this.owner.fireEvent('sync', this, html);
24017             }
24018         }
24019     },
24020
24021     /**
24022      * Protected method that will not generally be called directly. Pushes the value of the textarea
24023      * into the iframe editor.
24024      */
24025     pushValue : function(){
24026         if(this.initialized){
24027             var v = this.el.dom.value.trim();
24028             
24029 //            if(v.length < 1){
24030 //                v = '&#160;';
24031 //            }
24032             
24033             if(this.owner.fireEvent('beforepush', this, v) !== false){
24034                 var d = (this.doc.body || this.doc.documentElement);
24035                 d.innerHTML = v;
24036                 this.cleanUpPaste();
24037                 this.el.dom.value = d.innerHTML;
24038                 this.owner.fireEvent('push', this, v);
24039             }
24040         }
24041     },
24042
24043     // private
24044     deferFocus : function(){
24045         this.focus.defer(10, this);
24046     },
24047
24048     // doc'ed in Field
24049     focus : function(){
24050         if(this.win && !this.sourceEditMode){
24051             this.win.focus();
24052         }else{
24053             this.el.focus();
24054         }
24055     },
24056     
24057     assignDocWin: function()
24058     {
24059         var iframe = this.iframe;
24060         
24061          if(Roo.isIE){
24062             this.doc = iframe.contentWindow.document;
24063             this.win = iframe.contentWindow;
24064         } else {
24065 //            if (!Roo.get(this.frameId)) {
24066 //                return;
24067 //            }
24068 //            this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
24069 //            this.win = Roo.get(this.frameId).dom.contentWindow;
24070             
24071             if (!Roo.get(this.frameId) && !iframe.contentDocument) {
24072                 return;
24073             }
24074             
24075             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
24076             this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
24077         }
24078     },
24079     
24080     // private
24081     initEditor : function(){
24082         //console.log("INIT EDITOR");
24083         this.assignDocWin();
24084         
24085         
24086         
24087         this.doc.designMode="on";
24088         this.doc.open();
24089         this.doc.write(this.getDocMarkup());
24090         this.doc.close();
24091         
24092         var dbody = (this.doc.body || this.doc.documentElement);
24093         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
24094         // this copies styles from the containing element into thsi one..
24095         // not sure why we need all of this..
24096         //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
24097         
24098         //var ss = this.el.getStyles( 'background-image', 'background-repeat');
24099         //ss['background-attachment'] = 'fixed'; // w3c
24100         dbody.bgProperties = 'fixed'; // ie
24101         //Roo.DomHelper.applyStyles(dbody, ss);
24102         Roo.EventManager.on(this.doc, {
24103             //'mousedown': this.onEditorEvent,
24104             'mouseup': this.onEditorEvent,
24105             'dblclick': this.onEditorEvent,
24106             'click': this.onEditorEvent,
24107             'keyup': this.onEditorEvent,
24108             buffer:100,
24109             scope: this
24110         });
24111         if(Roo.isGecko){
24112             Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
24113         }
24114         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
24115             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
24116         }
24117         this.initialized = true;
24118
24119         this.owner.fireEvent('initialize', this);
24120         this.pushValue();
24121     },
24122
24123     // private
24124     onDestroy : function(){
24125         
24126         
24127         
24128         if(this.rendered){
24129             
24130             //for (var i =0; i < this.toolbars.length;i++) {
24131             //    // fixme - ask toolbars for heights?
24132             //    this.toolbars[i].onDestroy();
24133            // }
24134             
24135             //this.wrap.dom.innerHTML = '';
24136             //this.wrap.remove();
24137         }
24138     },
24139
24140     // private
24141     onFirstFocus : function(){
24142         
24143         this.assignDocWin();
24144         
24145         
24146         this.activated = true;
24147          
24148     
24149         if(Roo.isGecko){ // prevent silly gecko errors
24150             this.win.focus();
24151             var s = this.win.getSelection();
24152             if(!s.focusNode || s.focusNode.nodeType != 3){
24153                 var r = s.getRangeAt(0);
24154                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
24155                 r.collapse(true);
24156                 this.deferFocus();
24157             }
24158             try{
24159                 this.execCmd('useCSS', true);
24160                 this.execCmd('styleWithCSS', false);
24161             }catch(e){}
24162         }
24163         this.owner.fireEvent('activate', this);
24164     },
24165
24166     // private
24167     adjustFont: function(btn){
24168         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
24169         //if(Roo.isSafari){ // safari
24170         //    adjust *= 2;
24171        // }
24172         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
24173         if(Roo.isSafari){ // safari
24174             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
24175             v =  (v < 10) ? 10 : v;
24176             v =  (v > 48) ? 48 : v;
24177             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
24178             
24179         }
24180         
24181         
24182         v = Math.max(1, v+adjust);
24183         
24184         this.execCmd('FontSize', v  );
24185     },
24186
24187     onEditorEvent : function(e)
24188     {
24189         this.owner.fireEvent('editorevent', this, e);
24190       //  this.updateToolbar();
24191         this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
24192     },
24193
24194     insertTag : function(tg)
24195     {
24196         // could be a bit smarter... -> wrap the current selected tRoo..
24197         if (tg.toLowerCase() == 'span' ||
24198             tg.toLowerCase() == 'code' ||
24199             tg.toLowerCase() == 'sup' ||
24200             tg.toLowerCase() == 'sub' 
24201             ) {
24202             
24203             range = this.createRange(this.getSelection());
24204             var wrappingNode = this.doc.createElement(tg.toLowerCase());
24205             wrappingNode.appendChild(range.extractContents());
24206             range.insertNode(wrappingNode);
24207
24208             return;
24209             
24210             
24211             
24212         }
24213         this.execCmd("formatblock",   tg);
24214         
24215     },
24216     
24217     insertText : function(txt)
24218     {
24219         
24220         
24221         var range = this.createRange();
24222         range.deleteContents();
24223                //alert(Sender.getAttribute('label'));
24224                
24225         range.insertNode(this.doc.createTextNode(txt));
24226     } ,
24227     
24228      
24229
24230     /**
24231      * Executes a Midas editor command on the editor document and performs necessary focus and
24232      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
24233      * @param {String} cmd The Midas command
24234      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
24235      */
24236     relayCmd : function(cmd, value){
24237         this.win.focus();
24238         this.execCmd(cmd, value);
24239         this.owner.fireEvent('editorevent', this);
24240         //this.updateToolbar();
24241         this.owner.deferFocus();
24242     },
24243
24244     /**
24245      * Executes a Midas editor command directly on the editor document.
24246      * For visual commands, you should use {@link #relayCmd} instead.
24247      * <b>This should only be called after the editor is initialized.</b>
24248      * @param {String} cmd The Midas command
24249      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
24250      */
24251     execCmd : function(cmd, value){
24252         this.doc.execCommand(cmd, false, value === undefined ? null : value);
24253         this.syncValue();
24254     },
24255  
24256  
24257    
24258     /**
24259      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
24260      * to insert tRoo.
24261      * @param {String} text | dom node.. 
24262      */
24263     insertAtCursor : function(text)
24264     {
24265         
24266         if(!this.activated){
24267             return;
24268         }
24269         /*
24270         if(Roo.isIE){
24271             this.win.focus();
24272             var r = this.doc.selection.createRange();
24273             if(r){
24274                 r.collapse(true);
24275                 r.pasteHTML(text);
24276                 this.syncValue();
24277                 this.deferFocus();
24278             
24279             }
24280             return;
24281         }
24282         */
24283         if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
24284             this.win.focus();
24285             
24286             
24287             // from jquery ui (MIT licenced)
24288             var range, node;
24289             var win = this.win;
24290             
24291             if (win.getSelection && win.getSelection().getRangeAt) {
24292                 range = win.getSelection().getRangeAt(0);
24293                 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
24294                 range.insertNode(node);
24295             } else if (win.document.selection && win.document.selection.createRange) {
24296                 // no firefox support
24297                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
24298                 win.document.selection.createRange().pasteHTML(txt);
24299             } else {
24300                 // no firefox support
24301                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
24302                 this.execCmd('InsertHTML', txt);
24303             } 
24304             
24305             this.syncValue();
24306             
24307             this.deferFocus();
24308         }
24309     },
24310  // private
24311     mozKeyPress : function(e){
24312         if(e.ctrlKey){
24313             var c = e.getCharCode(), cmd;
24314           
24315             if(c > 0){
24316                 c = String.fromCharCode(c).toLowerCase();
24317                 switch(c){
24318                     case 'b':
24319                         cmd = 'bold';
24320                         break;
24321                     case 'i':
24322                         cmd = 'italic';
24323                         break;
24324                     
24325                     case 'u':
24326                         cmd = 'underline';
24327                         break;
24328                     
24329                     case 'v':
24330                         this.cleanUpPaste.defer(100, this);
24331                         return;
24332                         
24333                 }
24334                 if(cmd){
24335                     this.win.focus();
24336                     this.execCmd(cmd);
24337                     this.deferFocus();
24338                     e.preventDefault();
24339                 }
24340                 
24341             }
24342         }
24343     },
24344
24345     // private
24346     fixKeys : function(){ // load time branching for fastest keydown performance
24347         if(Roo.isIE){
24348             return function(e){
24349                 var k = e.getKey(), r;
24350                 if(k == e.TAB){
24351                     e.stopEvent();
24352                     r = this.doc.selection.createRange();
24353                     if(r){
24354                         r.collapse(true);
24355                         r.pasteHTML('&#160;&#160;&#160;&#160;');
24356                         this.deferFocus();
24357                     }
24358                     return;
24359                 }
24360                 
24361                 if(k == e.ENTER){
24362                     r = this.doc.selection.createRange();
24363                     if(r){
24364                         var target = r.parentElement();
24365                         if(!target || target.tagName.toLowerCase() != 'li'){
24366                             e.stopEvent();
24367                             r.pasteHTML('<br />');
24368                             r.collapse(false);
24369                             r.select();
24370                         }
24371                     }
24372                 }
24373                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
24374                     this.cleanUpPaste.defer(100, this);
24375                     return;
24376                 }
24377                 
24378                 
24379             };
24380         }else if(Roo.isOpera){
24381             return function(e){
24382                 var k = e.getKey();
24383                 if(k == e.TAB){
24384                     e.stopEvent();
24385                     this.win.focus();
24386                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
24387                     this.deferFocus();
24388                 }
24389                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
24390                     this.cleanUpPaste.defer(100, this);
24391                     return;
24392                 }
24393                 
24394             };
24395         }else if(Roo.isSafari){
24396             return function(e){
24397                 var k = e.getKey();
24398                 
24399                 if(k == e.TAB){
24400                     e.stopEvent();
24401                     this.execCmd('InsertText','\t');
24402                     this.deferFocus();
24403                     return;
24404                 }
24405                if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
24406                     this.cleanUpPaste.defer(100, this);
24407                     return;
24408                 }
24409                 
24410              };
24411         }
24412     }(),
24413     
24414     getAllAncestors: function()
24415     {
24416         var p = this.getSelectedNode();
24417         var a = [];
24418         if (!p) {
24419             a.push(p); // push blank onto stack..
24420             p = this.getParentElement();
24421         }
24422         
24423         
24424         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
24425             a.push(p);
24426             p = p.parentNode;
24427         }
24428         a.push(this.doc.body);
24429         return a;
24430     },
24431     lastSel : false,
24432     lastSelNode : false,
24433     
24434     
24435     getSelection : function() 
24436     {
24437         this.assignDocWin();
24438         return Roo.isIE ? this.doc.selection : this.win.getSelection();
24439     },
24440     
24441     getSelectedNode: function() 
24442     {
24443         // this may only work on Gecko!!!
24444         
24445         // should we cache this!!!!
24446         
24447         
24448         
24449          
24450         var range = this.createRange(this.getSelection()).cloneRange();
24451         
24452         if (Roo.isIE) {
24453             var parent = range.parentElement();
24454             while (true) {
24455                 var testRange = range.duplicate();
24456                 testRange.moveToElementText(parent);
24457                 if (testRange.inRange(range)) {
24458                     break;
24459                 }
24460                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
24461                     break;
24462                 }
24463                 parent = parent.parentElement;
24464             }
24465             return parent;
24466         }
24467         
24468         // is ancestor a text element.
24469         var ac =  range.commonAncestorContainer;
24470         if (ac.nodeType == 3) {
24471             ac = ac.parentNode;
24472         }
24473         
24474         var ar = ac.childNodes;
24475          
24476         var nodes = [];
24477         var other_nodes = [];
24478         var has_other_nodes = false;
24479         for (var i=0;i<ar.length;i++) {
24480             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
24481                 continue;
24482             }
24483             // fullly contained node.
24484             
24485             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
24486                 nodes.push(ar[i]);
24487                 continue;
24488             }
24489             
24490             // probably selected..
24491             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
24492                 other_nodes.push(ar[i]);
24493                 continue;
24494             }
24495             // outer..
24496             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
24497                 continue;
24498             }
24499             
24500             
24501             has_other_nodes = true;
24502         }
24503         if (!nodes.length && other_nodes.length) {
24504             nodes= other_nodes;
24505         }
24506         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
24507             return false;
24508         }
24509         
24510         return nodes[0];
24511     },
24512     createRange: function(sel)
24513     {
24514         // this has strange effects when using with 
24515         // top toolbar - not sure if it's a great idea.
24516         //this.editor.contentWindow.focus();
24517         if (typeof sel != "undefined") {
24518             try {
24519                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
24520             } catch(e) {
24521                 return this.doc.createRange();
24522             }
24523         } else {
24524             return this.doc.createRange();
24525         }
24526     },
24527     getParentElement: function()
24528     {
24529         
24530         this.assignDocWin();
24531         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
24532         
24533         var range = this.createRange(sel);
24534          
24535         try {
24536             var p = range.commonAncestorContainer;
24537             while (p.nodeType == 3) { // text node
24538                 p = p.parentNode;
24539             }
24540             return p;
24541         } catch (e) {
24542             return null;
24543         }
24544     
24545     },
24546     /***
24547      *
24548      * Range intersection.. the hard stuff...
24549      *  '-1' = before
24550      *  '0' = hits..
24551      *  '1' = after.
24552      *         [ -- selected range --- ]
24553      *   [fail]                        [fail]
24554      *
24555      *    basically..
24556      *      if end is before start or  hits it. fail.
24557      *      if start is after end or hits it fail.
24558      *
24559      *   if either hits (but other is outside. - then it's not 
24560      *   
24561      *    
24562      **/
24563     
24564     
24565     // @see http://www.thismuchiknow.co.uk/?p=64.
24566     rangeIntersectsNode : function(range, node)
24567     {
24568         var nodeRange = node.ownerDocument.createRange();
24569         try {
24570             nodeRange.selectNode(node);
24571         } catch (e) {
24572             nodeRange.selectNodeContents(node);
24573         }
24574     
24575         var rangeStartRange = range.cloneRange();
24576         rangeStartRange.collapse(true);
24577     
24578         var rangeEndRange = range.cloneRange();
24579         rangeEndRange.collapse(false);
24580     
24581         var nodeStartRange = nodeRange.cloneRange();
24582         nodeStartRange.collapse(true);
24583     
24584         var nodeEndRange = nodeRange.cloneRange();
24585         nodeEndRange.collapse(false);
24586     
24587         return rangeStartRange.compareBoundaryPoints(
24588                  Range.START_TO_START, nodeEndRange) == -1 &&
24589                rangeEndRange.compareBoundaryPoints(
24590                  Range.START_TO_START, nodeStartRange) == 1;
24591         
24592          
24593     },
24594     rangeCompareNode : function(range, node)
24595     {
24596         var nodeRange = node.ownerDocument.createRange();
24597         try {
24598             nodeRange.selectNode(node);
24599         } catch (e) {
24600             nodeRange.selectNodeContents(node);
24601         }
24602         
24603         
24604         range.collapse(true);
24605     
24606         nodeRange.collapse(true);
24607      
24608         var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
24609         var ee = range.compareBoundaryPoints(  Range.END_TO_END, nodeRange);
24610          
24611         //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
24612         
24613         var nodeIsBefore   =  ss == 1;
24614         var nodeIsAfter    = ee == -1;
24615         
24616         if (nodeIsBefore && nodeIsAfter) {
24617             return 0; // outer
24618         }
24619         if (!nodeIsBefore && nodeIsAfter) {
24620             return 1; //right trailed.
24621         }
24622         
24623         if (nodeIsBefore && !nodeIsAfter) {
24624             return 2;  // left trailed.
24625         }
24626         // fully contined.
24627         return 3;
24628     },
24629
24630     // private? - in a new class?
24631     cleanUpPaste :  function()
24632     {
24633         // cleans up the whole document..
24634         Roo.log('cleanuppaste');
24635         
24636         this.cleanUpChildren(this.doc.body);
24637         var clean = this.cleanWordChars(this.doc.body.innerHTML);
24638         if (clean != this.doc.body.innerHTML) {
24639             this.doc.body.innerHTML = clean;
24640         }
24641         
24642     },
24643     
24644     cleanWordChars : function(input) {// change the chars to hex code
24645         var he = Roo.HtmlEditorCore;
24646         
24647         var output = input;
24648         Roo.each(he.swapCodes, function(sw) { 
24649             var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
24650             
24651             output = output.replace(swapper, sw[1]);
24652         });
24653         
24654         return output;
24655     },
24656     
24657     
24658     cleanUpChildren : function (n)
24659     {
24660         if (!n.childNodes.length) {
24661             return;
24662         }
24663         for (var i = n.childNodes.length-1; i > -1 ; i--) {
24664            this.cleanUpChild(n.childNodes[i]);
24665         }
24666     },
24667     
24668     
24669         
24670     
24671     cleanUpChild : function (node)
24672     {
24673         var ed = this;
24674         //console.log(node);
24675         if (node.nodeName == "#text") {
24676             // clean up silly Windows -- stuff?
24677             return; 
24678         }
24679         if (node.nodeName == "#comment") {
24680             node.parentNode.removeChild(node);
24681             // clean up silly Windows -- stuff?
24682             return; 
24683         }
24684         var lcname = node.tagName.toLowerCase();
24685         // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
24686         // whitelist of tags..
24687         
24688         if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
24689             // remove node.
24690             node.parentNode.removeChild(node);
24691             return;
24692             
24693         }
24694         
24695         var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
24696         
24697         // spans with no attributes - just remove them..
24698         if ((!node.attributes || !node.attributes.length) && lcname == 'span') { 
24699             remove_keep_children = true;
24700         }
24701         
24702         // remove <a name=....> as rendering on yahoo mailer is borked with this.
24703         // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
24704         
24705         //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
24706         //    remove_keep_children = true;
24707         //}
24708         
24709         if (remove_keep_children) {
24710             this.cleanUpChildren(node);
24711             // inserts everything just before this node...
24712             while (node.childNodes.length) {
24713                 var cn = node.childNodes[0];
24714                 node.removeChild(cn);
24715                 node.parentNode.insertBefore(cn, node);
24716             }
24717             node.parentNode.removeChild(node);
24718             return;
24719         }
24720         
24721         if (!node.attributes || !node.attributes.length) {
24722             
24723           
24724             
24725             
24726             this.cleanUpChildren(node);
24727             return;
24728         }
24729         
24730         function cleanAttr(n,v)
24731         {
24732             
24733             if (v.match(/^\./) || v.match(/^\//)) {
24734                 return;
24735             }
24736             if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/) || v.match(/^ftp:/)) {
24737                 return;
24738             }
24739             if (v.match(/^#/)) {
24740                 return;
24741             }
24742             if (v.match(/^\{/)) { // allow template editing.
24743                 return;
24744             }
24745 //            Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
24746             node.removeAttribute(n);
24747             
24748         }
24749         
24750         var cwhite = this.cwhite;
24751         var cblack = this.cblack;
24752             
24753         function cleanStyle(n,v)
24754         {
24755             if (v.match(/expression/)) { //XSS?? should we even bother..
24756                 node.removeAttribute(n);
24757                 return;
24758             }
24759             
24760             var parts = v.split(/;/);
24761             var clean = [];
24762             
24763             Roo.each(parts, function(p) {
24764                 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
24765                 if (!p.length) {
24766                     return true;
24767                 }
24768                 var l = p.split(':').shift().replace(/\s+/g,'');
24769                 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
24770                 
24771                 if ( cwhite.length && cblack.indexOf(l) > -1) {
24772 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
24773                     //node.removeAttribute(n);
24774                     return true;
24775                 }
24776                 //Roo.log()
24777                 // only allow 'c whitelisted system attributes'
24778                 if ( cwhite.length &&  cwhite.indexOf(l) < 0) {
24779 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
24780                     //node.removeAttribute(n);
24781                     return true;
24782                 }
24783                 
24784                 
24785                  
24786                 
24787                 clean.push(p);
24788                 return true;
24789             });
24790             if (clean.length) { 
24791                 node.setAttribute(n, clean.join(';'));
24792             } else {
24793                 node.removeAttribute(n);
24794             }
24795             
24796         }
24797         
24798         
24799         for (var i = node.attributes.length-1; i > -1 ; i--) {
24800             var a = node.attributes[i];
24801             //console.log(a);
24802             
24803             if (a.name.toLowerCase().substr(0,2)=='on')  {
24804                 node.removeAttribute(a.name);
24805                 continue;
24806             }
24807             if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
24808                 node.removeAttribute(a.name);
24809                 continue;
24810             }
24811             if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
24812                 cleanAttr(a.name,a.value); // fixme..
24813                 continue;
24814             }
24815             if (a.name == 'style') {
24816                 cleanStyle(a.name,a.value);
24817                 continue;
24818             }
24819             /// clean up MS crap..
24820             // tecnically this should be a list of valid class'es..
24821             
24822             
24823             if (a.name == 'class') {
24824                 if (a.value.match(/^Mso/)) {
24825                     node.removeAttribute('class');
24826                 }
24827                 
24828                 if (a.value.match(/^body$/)) {
24829                     node.removeAttribute('class');
24830                 }
24831                 continue;
24832             }
24833             
24834             // style cleanup!?
24835             // class cleanup?
24836             
24837         }
24838         
24839         
24840         this.cleanUpChildren(node);
24841         
24842         
24843     },
24844     
24845     /**
24846      * Clean up MS wordisms...
24847      */
24848     cleanWord : function(node)
24849     {
24850         if (!node) {
24851             this.cleanWord(this.doc.body);
24852             return;
24853         }
24854         
24855         if(
24856                 node.nodeName == 'SPAN' &&
24857                 !node.hasAttributes() &&
24858                 node.childNodes.length == 1 &&
24859                 node.firstChild.nodeName == "#text"  
24860         ) {
24861             var textNode = node.firstChild;
24862             node.removeChild(textNode);
24863             if (node.getAttribute('lang') != 'zh-CN') {   // do not space pad on chinese characters..
24864                 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" "), node);
24865             }
24866             node.parentNode.insertBefore(textNode, node);
24867             if (node.getAttribute('lang') != 'zh-CN') {   // do not space pad on chinese characters..
24868                 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" ") , node);
24869             }
24870             node.parentNode.removeChild(node);
24871         }
24872         
24873         if (node.nodeName == "#text") {
24874             // clean up silly Windows -- stuff?
24875             return; 
24876         }
24877         if (node.nodeName == "#comment") {
24878             node.parentNode.removeChild(node);
24879             // clean up silly Windows -- stuff?
24880             return; 
24881         }
24882         
24883         if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
24884             node.parentNode.removeChild(node);
24885             return;
24886         }
24887         //Roo.log(node.tagName);
24888         // remove - but keep children..
24889         if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|v:|font)/)) {
24890             //Roo.log('-- removed');
24891             while (node.childNodes.length) {
24892                 var cn = node.childNodes[0];
24893                 node.removeChild(cn);
24894                 node.parentNode.insertBefore(cn, node);
24895                 // move node to parent - and clean it..
24896                 this.cleanWord(cn);
24897             }
24898             node.parentNode.removeChild(node);
24899             /// no need to iterate chidlren = it's got none..
24900             //this.iterateChildren(node, this.cleanWord);
24901             return;
24902         }
24903         // clean styles
24904         if (node.className.length) {
24905             
24906             var cn = node.className.split(/\W+/);
24907             var cna = [];
24908             Roo.each(cn, function(cls) {
24909                 if (cls.match(/Mso[a-zA-Z]+/)) {
24910                     return;
24911                 }
24912                 cna.push(cls);
24913             });
24914             node.className = cna.length ? cna.join(' ') : '';
24915             if (!cna.length) {
24916                 node.removeAttribute("class");
24917             }
24918         }
24919         
24920         if (node.hasAttribute("lang")) {
24921             node.removeAttribute("lang");
24922         }
24923         
24924         if (node.hasAttribute("style")) {
24925             
24926             var styles = node.getAttribute("style").split(";");
24927             var nstyle = [];
24928             Roo.each(styles, function(s) {
24929                 if (!s.match(/:/)) {
24930                     return;
24931                 }
24932                 var kv = s.split(":");
24933                 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
24934                     return;
24935                 }
24936                 // what ever is left... we allow.
24937                 nstyle.push(s);
24938             });
24939             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
24940             if (!nstyle.length) {
24941                 node.removeAttribute('style');
24942             }
24943         }
24944         this.iterateChildren(node, this.cleanWord);
24945         
24946         
24947         
24948     },
24949     /**
24950      * iterateChildren of a Node, calling fn each time, using this as the scole..
24951      * @param {DomNode} node node to iterate children of.
24952      * @param {Function} fn method of this class to call on each item.
24953      */
24954     iterateChildren : function(node, fn)
24955     {
24956         if (!node.childNodes.length) {
24957                 return;
24958         }
24959         for (var i = node.childNodes.length-1; i > -1 ; i--) {
24960            fn.call(this, node.childNodes[i])
24961         }
24962     },
24963     
24964     
24965     /**
24966      * cleanTableWidths.
24967      *
24968      * Quite often pasting from word etc.. results in tables with column and widths.
24969      * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
24970      *
24971      */
24972     cleanTableWidths : function(node)
24973     {
24974          
24975          
24976         if (!node) {
24977             this.cleanTableWidths(this.doc.body);
24978             return;
24979         }
24980         
24981         // ignore list...
24982         if (node.nodeName == "#text" || node.nodeName == "#comment") {
24983             return; 
24984         }
24985         Roo.log(node.tagName);
24986         if (!node.tagName.toLowerCase().match(/^(table|td|tr)$/)) {
24987             this.iterateChildren(node, this.cleanTableWidths);
24988             return;
24989         }
24990         if (node.hasAttribute('width')) {
24991             node.removeAttribute('width');
24992         }
24993         
24994          
24995         if (node.hasAttribute("style")) {
24996             // pretty basic...
24997             
24998             var styles = node.getAttribute("style").split(";");
24999             var nstyle = [];
25000             Roo.each(styles, function(s) {
25001                 if (!s.match(/:/)) {
25002                     return;
25003                 }
25004                 var kv = s.split(":");
25005                 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
25006                     return;
25007                 }
25008                 // what ever is left... we allow.
25009                 nstyle.push(s);
25010             });
25011             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
25012             if (!nstyle.length) {
25013                 node.removeAttribute('style');
25014             }
25015         }
25016         
25017         this.iterateChildren(node, this.cleanTableWidths);
25018         
25019         
25020     },
25021     
25022     
25023     
25024     
25025     domToHTML : function(currentElement, depth, nopadtext) {
25026         
25027         depth = depth || 0;
25028         nopadtext = nopadtext || false;
25029     
25030         if (!currentElement) {
25031             return this.domToHTML(this.doc.body);
25032         }
25033         
25034         //Roo.log(currentElement);
25035         var j;
25036         var allText = false;
25037         var nodeName = currentElement.nodeName;
25038         var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
25039         
25040         if  (nodeName == '#text') {
25041             
25042             return nopadtext ? currentElement.nodeValue : currentElement.nodeValue.trim();
25043         }
25044         
25045         
25046         var ret = '';
25047         if (nodeName != 'BODY') {
25048              
25049             var i = 0;
25050             // Prints the node tagName, such as <A>, <IMG>, etc
25051             if (tagName) {
25052                 var attr = [];
25053                 for(i = 0; i < currentElement.attributes.length;i++) {
25054                     // quoting?
25055                     var aname = currentElement.attributes.item(i).name;
25056                     if (!currentElement.attributes.item(i).value.length) {
25057                         continue;
25058                     }
25059                     attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
25060                 }
25061                 
25062                 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
25063             } 
25064             else {
25065                 
25066                 // eack
25067             }
25068         } else {
25069             tagName = false;
25070         }
25071         if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
25072             return ret;
25073         }
25074         if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
25075             nopadtext = true;
25076         }
25077         
25078         
25079         // Traverse the tree
25080         i = 0;
25081         var currentElementChild = currentElement.childNodes.item(i);
25082         var allText = true;
25083         var innerHTML  = '';
25084         lastnode = '';
25085         while (currentElementChild) {
25086             // Formatting code (indent the tree so it looks nice on the screen)
25087             var nopad = nopadtext;
25088             if (lastnode == 'SPAN') {
25089                 nopad  = true;
25090             }
25091             // text
25092             if  (currentElementChild.nodeName == '#text') {
25093                 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
25094                 toadd = nopadtext ? toadd : toadd.trim();
25095                 if (!nopad && toadd.length > 80) {
25096                     innerHTML  += "\n" + (new Array( depth + 1 )).join( "  "  );
25097                 }
25098                 innerHTML  += toadd;
25099                 
25100                 i++;
25101                 currentElementChild = currentElement.childNodes.item(i);
25102                 lastNode = '';
25103                 continue;
25104             }
25105             allText = false;
25106             
25107             innerHTML  += nopad ? '' : "\n" + (new Array( depth + 1 )).join( "  "  );
25108                 
25109             // Recursively traverse the tree structure of the child node
25110             innerHTML   += this.domToHTML(currentElementChild, depth+1, nopadtext);
25111             lastnode = currentElementChild.nodeName;
25112             i++;
25113             currentElementChild=currentElement.childNodes.item(i);
25114         }
25115         
25116         ret += innerHTML;
25117         
25118         if (!allText) {
25119                 // The remaining code is mostly for formatting the tree
25120             ret+= nopadtext ? '' : "\n" + (new Array( depth  )).join( "  "  );
25121         }
25122         
25123         
25124         if (tagName) {
25125             ret+= "</"+tagName+">";
25126         }
25127         return ret;
25128         
25129     },
25130         
25131     applyBlacklists : function()
25132     {
25133         var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white  : [];
25134         var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black :  [];
25135         
25136         this.white = [];
25137         this.black = [];
25138         Roo.each(Roo.HtmlEditorCore.white, function(tag) {
25139             if (b.indexOf(tag) > -1) {
25140                 return;
25141             }
25142             this.white.push(tag);
25143             
25144         }, this);
25145         
25146         Roo.each(w, function(tag) {
25147             if (b.indexOf(tag) > -1) {
25148                 return;
25149             }
25150             if (this.white.indexOf(tag) > -1) {
25151                 return;
25152             }
25153             this.white.push(tag);
25154             
25155         }, this);
25156         
25157         
25158         Roo.each(Roo.HtmlEditorCore.black, function(tag) {
25159             if (w.indexOf(tag) > -1) {
25160                 return;
25161             }
25162             this.black.push(tag);
25163             
25164         }, this);
25165         
25166         Roo.each(b, function(tag) {
25167             if (w.indexOf(tag) > -1) {
25168                 return;
25169             }
25170             if (this.black.indexOf(tag) > -1) {
25171                 return;
25172             }
25173             this.black.push(tag);
25174             
25175         }, this);
25176         
25177         
25178         w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite  : [];
25179         b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack :  [];
25180         
25181         this.cwhite = [];
25182         this.cblack = [];
25183         Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
25184             if (b.indexOf(tag) > -1) {
25185                 return;
25186             }
25187             this.cwhite.push(tag);
25188             
25189         }, this);
25190         
25191         Roo.each(w, function(tag) {
25192             if (b.indexOf(tag) > -1) {
25193                 return;
25194             }
25195             if (this.cwhite.indexOf(tag) > -1) {
25196                 return;
25197             }
25198             this.cwhite.push(tag);
25199             
25200         }, this);
25201         
25202         
25203         Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
25204             if (w.indexOf(tag) > -1) {
25205                 return;
25206             }
25207             this.cblack.push(tag);
25208             
25209         }, this);
25210         
25211         Roo.each(b, function(tag) {
25212             if (w.indexOf(tag) > -1) {
25213                 return;
25214             }
25215             if (this.cblack.indexOf(tag) > -1) {
25216                 return;
25217             }
25218             this.cblack.push(tag);
25219             
25220         }, this);
25221     },
25222     
25223     setStylesheets : function(stylesheets)
25224     {
25225         if(typeof(stylesheets) == 'string'){
25226             Roo.get(this.iframe.contentDocument.head).createChild({
25227                 tag : 'link',
25228                 rel : 'stylesheet',
25229                 type : 'text/css',
25230                 href : stylesheets
25231             });
25232             
25233             return;
25234         }
25235         var _this = this;
25236      
25237         Roo.each(stylesheets, function(s) {
25238             if(!s.length){
25239                 return;
25240             }
25241             
25242             Roo.get(_this.iframe.contentDocument.head).createChild({
25243                 tag : 'link',
25244                 rel : 'stylesheet',
25245                 type : 'text/css',
25246                 href : s
25247             });
25248         });
25249
25250         
25251     },
25252     
25253     removeStylesheets : function()
25254     {
25255         var _this = this;
25256         
25257         Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
25258             s.remove();
25259         });
25260     },
25261     
25262     setStyle : function(style)
25263     {
25264         Roo.get(this.iframe.contentDocument.head).createChild({
25265             tag : 'style',
25266             type : 'text/css',
25267             html : style
25268         });
25269
25270         return;
25271     }
25272     
25273     // hide stuff that is not compatible
25274     /**
25275      * @event blur
25276      * @hide
25277      */
25278     /**
25279      * @event change
25280      * @hide
25281      */
25282     /**
25283      * @event focus
25284      * @hide
25285      */
25286     /**
25287      * @event specialkey
25288      * @hide
25289      */
25290     /**
25291      * @cfg {String} fieldClass @hide
25292      */
25293     /**
25294      * @cfg {String} focusClass @hide
25295      */
25296     /**
25297      * @cfg {String} autoCreate @hide
25298      */
25299     /**
25300      * @cfg {String} inputType @hide
25301      */
25302     /**
25303      * @cfg {String} invalidClass @hide
25304      */
25305     /**
25306      * @cfg {String} invalidText @hide
25307      */
25308     /**
25309      * @cfg {String} msgFx @hide
25310      */
25311     /**
25312      * @cfg {String} validateOnBlur @hide
25313      */
25314 });
25315
25316 Roo.HtmlEditorCore.white = [
25317         'area', 'br', 'img', 'input', 'hr', 'wbr',
25318         
25319        'address', 'blockquote', 'center', 'dd',      'dir',       'div', 
25320        'dl',      'dt',         'h1',     'h2',      'h3',        'h4', 
25321        'h5',      'h6',         'hr',     'isindex', 'listing',   'marquee', 
25322        'menu',    'multicol',   'ol',     'p',       'plaintext', 'pre', 
25323        'table',   'ul',         'xmp', 
25324        
25325        'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th', 
25326       'thead',   'tr', 
25327      
25328       'dir', 'menu', 'ol', 'ul', 'dl',
25329        
25330       'embed',  'object'
25331 ];
25332
25333
25334 Roo.HtmlEditorCore.black = [
25335     //    'embed',  'object', // enable - backend responsiblity to clean thiese
25336         'applet', // 
25337         'base',   'basefont', 'bgsound', 'blink',  'body', 
25338         'frame',  'frameset', 'head',    'html',   'ilayer', 
25339         'iframe', 'layer',  'link',     'meta',    'object',   
25340         'script', 'style' ,'title',  'xml' // clean later..
25341 ];
25342 Roo.HtmlEditorCore.clean = [
25343     'script', 'style', 'title', 'xml'
25344 ];
25345 Roo.HtmlEditorCore.remove = [
25346     'font'
25347 ];
25348 // attributes..
25349
25350 Roo.HtmlEditorCore.ablack = [
25351     'on'
25352 ];
25353     
25354 Roo.HtmlEditorCore.aclean = [ 
25355     'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc' 
25356 ];
25357
25358 // protocols..
25359 Roo.HtmlEditorCore.pwhite= [
25360         'http',  'https',  'mailto'
25361 ];
25362
25363 // white listed style attributes.
25364 Roo.HtmlEditorCore.cwhite= [
25365       //  'text-align', /// default is to allow most things..
25366       
25367          
25368 //        'font-size'//??
25369 ];
25370
25371 // black listed style attributes.
25372 Roo.HtmlEditorCore.cblack= [
25373       //  'font-size' -- this can be set by the project 
25374 ];
25375
25376
25377 Roo.HtmlEditorCore.swapCodes   =[ 
25378     [    8211, "--" ], 
25379     [    8212, "--" ], 
25380     [    8216,  "'" ],  
25381     [    8217, "'" ],  
25382     [    8220, '"' ],  
25383     [    8221, '"' ],  
25384     [    8226, "*" ],  
25385     [    8230, "..." ]
25386 ]; 
25387
25388     /*
25389  * - LGPL
25390  *
25391  * HtmlEditor
25392  * 
25393  */
25394
25395 /**
25396  * @class Roo.bootstrap.HtmlEditor
25397  * @extends Roo.bootstrap.TextArea
25398  * Bootstrap HtmlEditor class
25399
25400  * @constructor
25401  * Create a new HtmlEditor
25402  * @param {Object} config The config object
25403  */
25404
25405 Roo.bootstrap.HtmlEditor = function(config){
25406     Roo.bootstrap.HtmlEditor.superclass.constructor.call(this, config);
25407     if (!this.toolbars) {
25408         this.toolbars = [];
25409     }
25410     
25411     this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
25412     this.addEvents({
25413             /**
25414              * @event initialize
25415              * Fires when the editor is fully initialized (including the iframe)
25416              * @param {HtmlEditor} this
25417              */
25418             initialize: true,
25419             /**
25420              * @event activate
25421              * Fires when the editor is first receives the focus. Any insertion must wait
25422              * until after this event.
25423              * @param {HtmlEditor} this
25424              */
25425             activate: true,
25426              /**
25427              * @event beforesync
25428              * Fires before the textarea is updated with content from the editor iframe. Return false
25429              * to cancel the sync.
25430              * @param {HtmlEditor} this
25431              * @param {String} html
25432              */
25433             beforesync: true,
25434              /**
25435              * @event beforepush
25436              * Fires before the iframe editor is updated with content from the textarea. Return false
25437              * to cancel the push.
25438              * @param {HtmlEditor} this
25439              * @param {String} html
25440              */
25441             beforepush: true,
25442              /**
25443              * @event sync
25444              * Fires when the textarea is updated with content from the editor iframe.
25445              * @param {HtmlEditor} this
25446              * @param {String} html
25447              */
25448             sync: true,
25449              /**
25450              * @event push
25451              * Fires when the iframe editor is updated with content from the textarea.
25452              * @param {HtmlEditor} this
25453              * @param {String} html
25454              */
25455             push: true,
25456              /**
25457              * @event editmodechange
25458              * Fires when the editor switches edit modes
25459              * @param {HtmlEditor} this
25460              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
25461              */
25462             editmodechange: true,
25463             /**
25464              * @event editorevent
25465              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
25466              * @param {HtmlEditor} this
25467              */
25468             editorevent: true,
25469             /**
25470              * @event firstfocus
25471              * Fires when on first focus - needed by toolbars..
25472              * @param {HtmlEditor} this
25473              */
25474             firstfocus: true,
25475             /**
25476              * @event autosave
25477              * Auto save the htmlEditor value as a file into Events
25478              * @param {HtmlEditor} this
25479              */
25480             autosave: true,
25481             /**
25482              * @event savedpreview
25483              * preview the saved version of htmlEditor
25484              * @param {HtmlEditor} this
25485              */
25486             savedpreview: true
25487         });
25488 };
25489
25490
25491 Roo.extend(Roo.bootstrap.HtmlEditor, Roo.bootstrap.TextArea,  {
25492     
25493     
25494       /**
25495      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
25496      */
25497     toolbars : false,
25498     
25499      /**
25500     * @cfg {Array} buttons Array of toolbar's buttons. - defaults to empty
25501     */
25502     btns : [],
25503    
25504      /**
25505      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
25506      *                        Roo.resizable.
25507      */
25508     resizable : false,
25509      /**
25510      * @cfg {Number} height (in pixels)
25511      */   
25512     height: 300,
25513    /**
25514      * @cfg {Number} width (in pixels)
25515      */   
25516     width: false,
25517     
25518     /**
25519      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
25520      * 
25521      */
25522     stylesheets: false,
25523     
25524     // id of frame..
25525     frameId: false,
25526     
25527     // private properties
25528     validationEvent : false,
25529     deferHeight: true,
25530     initialized : false,
25531     activated : false,
25532     
25533     onFocus : Roo.emptyFn,
25534     iframePad:3,
25535     hideMode:'offsets',
25536     
25537     tbContainer : false,
25538     
25539     bodyCls : '',
25540     
25541     toolbarContainer :function() {
25542         return this.wrap.select('.x-html-editor-tb',true).first();
25543     },
25544
25545     /**
25546      * Protected method that will not generally be called directly. It
25547      * is called when the editor creates its toolbar. Override this method if you need to
25548      * add custom toolbar buttons.
25549      * @param {HtmlEditor} editor
25550      */
25551     createToolbar : function(){
25552         Roo.log('renewing');
25553         Roo.log("create toolbars");
25554         
25555         this.toolbars = [ new Roo.bootstrap.htmleditor.ToolbarStandard({editor: this} ) ];
25556         this.toolbars[0].render(this.toolbarContainer());
25557         
25558         return;
25559         
25560 //        if (!editor.toolbars || !editor.toolbars.length) {
25561 //            editor.toolbars = [ new Roo.bootstrap.HtmlEditor.ToolbarStandard() ]; // can be empty?
25562 //        }
25563 //        
25564 //        for (var i =0 ; i < editor.toolbars.length;i++) {
25565 //            editor.toolbars[i] = Roo.factory(
25566 //                    typeof(editor.toolbars[i]) == 'string' ?
25567 //                        { xtype: editor.toolbars[i]} : editor.toolbars[i],
25568 //                Roo.bootstrap.HtmlEditor);
25569 //            editor.toolbars[i].init(editor);
25570 //        }
25571     },
25572
25573      
25574     // private
25575     onRender : function(ct, position)
25576     {
25577        // Roo.log("Call onRender: " + this.xtype);
25578         var _t = this;
25579         Roo.bootstrap.HtmlEditor.superclass.onRender.call(this, ct, position);
25580       
25581         this.wrap = this.inputEl().wrap({
25582             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
25583         });
25584         
25585         this.editorcore.onRender(ct, position);
25586          
25587         if (this.resizable) {
25588             this.resizeEl = new Roo.Resizable(this.wrap, {
25589                 pinned : true,
25590                 wrap: true,
25591                 dynamic : true,
25592                 minHeight : this.height,
25593                 height: this.height,
25594                 handles : this.resizable,
25595                 width: this.width,
25596                 listeners : {
25597                     resize : function(r, w, h) {
25598                         _t.onResize(w,h); // -something
25599                     }
25600                 }
25601             });
25602             
25603         }
25604         this.createToolbar(this);
25605        
25606         
25607         if(!this.width && this.resizable){
25608             this.setSize(this.wrap.getSize());
25609         }
25610         if (this.resizeEl) {
25611             this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
25612             // should trigger onReize..
25613         }
25614         
25615     },
25616
25617     // private
25618     onResize : function(w, h)
25619     {
25620         Roo.log('resize: ' +w + ',' + h );
25621         Roo.bootstrap.HtmlEditor.superclass.onResize.apply(this, arguments);
25622         var ew = false;
25623         var eh = false;
25624         
25625         if(this.inputEl() ){
25626             if(typeof w == 'number'){
25627                 var aw = w - this.wrap.getFrameWidth('lr');
25628                 this.inputEl().setWidth(this.adjustWidth('textarea', aw));
25629                 ew = aw;
25630             }
25631             if(typeof h == 'number'){
25632                  var tbh = -11;  // fixme it needs to tool bar size!
25633                 for (var i =0; i < this.toolbars.length;i++) {
25634                     // fixme - ask toolbars for heights?
25635                     tbh += this.toolbars[i].el.getHeight();
25636                     //if (this.toolbars[i].footer) {
25637                     //    tbh += this.toolbars[i].footer.el.getHeight();
25638                     //}
25639                 }
25640               
25641                 
25642                 
25643                 
25644                 
25645                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
25646                 ah -= 5; // knock a few pixes off for look..
25647                 this.inputEl().setHeight(this.adjustWidth('textarea', ah));
25648                 var eh = ah;
25649             }
25650         }
25651         Roo.log('onResize:' + [w,h,ew,eh].join(',') );
25652         this.editorcore.onResize(ew,eh);
25653         
25654     },
25655
25656     /**
25657      * Toggles the editor between standard and source edit mode.
25658      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
25659      */
25660     toggleSourceEdit : function(sourceEditMode)
25661     {
25662         this.editorcore.toggleSourceEdit(sourceEditMode);
25663         
25664         if(this.editorcore.sourceEditMode){
25665             Roo.log('editor - showing textarea');
25666             
25667 //            Roo.log('in');
25668 //            Roo.log(this.syncValue());
25669             this.syncValue();
25670             this.inputEl().removeClass(['hide', 'x-hidden']);
25671             this.inputEl().dom.removeAttribute('tabIndex');
25672             this.inputEl().focus();
25673         }else{
25674             Roo.log('editor - hiding textarea');
25675 //            Roo.log('out')
25676 //            Roo.log(this.pushValue()); 
25677             this.pushValue();
25678             
25679             this.inputEl().addClass(['hide', 'x-hidden']);
25680             this.inputEl().dom.setAttribute('tabIndex', -1);
25681             //this.deferFocus();
25682         }
25683          
25684         if(this.resizable){
25685             this.setSize(this.wrap.getSize());
25686         }
25687         
25688         this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
25689     },
25690  
25691     // private (for BoxComponent)
25692     adjustSize : Roo.BoxComponent.prototype.adjustSize,
25693
25694     // private (for BoxComponent)
25695     getResizeEl : function(){
25696         return this.wrap;
25697     },
25698
25699     // private (for BoxComponent)
25700     getPositionEl : function(){
25701         return this.wrap;
25702     },
25703
25704     // private
25705     initEvents : function(){
25706         this.originalValue = this.getValue();
25707     },
25708
25709 //    /**
25710 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
25711 //     * @method
25712 //     */
25713 //    markInvalid : Roo.emptyFn,
25714 //    /**
25715 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
25716 //     * @method
25717 //     */
25718 //    clearInvalid : Roo.emptyFn,
25719
25720     setValue : function(v){
25721         Roo.bootstrap.HtmlEditor.superclass.setValue.call(this, v);
25722         this.editorcore.pushValue();
25723     },
25724
25725      
25726     // private
25727     deferFocus : function(){
25728         this.focus.defer(10, this);
25729     },
25730
25731     // doc'ed in Field
25732     focus : function(){
25733         this.editorcore.focus();
25734         
25735     },
25736       
25737
25738     // private
25739     onDestroy : function(){
25740         
25741         
25742         
25743         if(this.rendered){
25744             
25745             for (var i =0; i < this.toolbars.length;i++) {
25746                 // fixme - ask toolbars for heights?
25747                 this.toolbars[i].onDestroy();
25748             }
25749             
25750             this.wrap.dom.innerHTML = '';
25751             this.wrap.remove();
25752         }
25753     },
25754
25755     // private
25756     onFirstFocus : function(){
25757         //Roo.log("onFirstFocus");
25758         this.editorcore.onFirstFocus();
25759          for (var i =0; i < this.toolbars.length;i++) {
25760             this.toolbars[i].onFirstFocus();
25761         }
25762         
25763     },
25764     
25765     // private
25766     syncValue : function()
25767     {   
25768         this.editorcore.syncValue();
25769     },
25770     
25771     pushValue : function()
25772     {   
25773         this.editorcore.pushValue();
25774     }
25775      
25776     
25777     // hide stuff that is not compatible
25778     /**
25779      * @event blur
25780      * @hide
25781      */
25782     /**
25783      * @event change
25784      * @hide
25785      */
25786     /**
25787      * @event focus
25788      * @hide
25789      */
25790     /**
25791      * @event specialkey
25792      * @hide
25793      */
25794     /**
25795      * @cfg {String} fieldClass @hide
25796      */
25797     /**
25798      * @cfg {String} focusClass @hide
25799      */
25800     /**
25801      * @cfg {String} autoCreate @hide
25802      */
25803     /**
25804      * @cfg {String} inputType @hide
25805      */
25806      
25807     /**
25808      * @cfg {String} invalidText @hide
25809      */
25810     /**
25811      * @cfg {String} msgFx @hide
25812      */
25813     /**
25814      * @cfg {String} validateOnBlur @hide
25815      */
25816 });
25817  
25818     
25819    
25820    
25821    
25822       
25823 Roo.namespace('Roo.bootstrap.htmleditor');
25824 /**
25825  * @class Roo.bootstrap.HtmlEditorToolbar1
25826  * Basic Toolbar
25827  * 
25828  * @example
25829  * Usage:
25830  *
25831  new Roo.bootstrap.HtmlEditor({
25832     ....
25833     toolbars : [
25834         new Roo.bootstrap.HtmlEditorToolbar1({
25835             disable : { fonts: 1 , format: 1, ..., ... , ...],
25836             btns : [ .... ]
25837         })
25838     }
25839      
25840  * 
25841  * @cfg {Object} disable List of elements to disable..
25842  * @cfg {Array} btns List of additional buttons.
25843  * 
25844  * 
25845  * NEEDS Extra CSS? 
25846  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
25847  */
25848  
25849 Roo.bootstrap.htmleditor.ToolbarStandard = function(config)
25850 {
25851     
25852     Roo.apply(this, config);
25853     
25854     // default disabled, based on 'good practice'..
25855     this.disable = this.disable || {};
25856     Roo.applyIf(this.disable, {
25857         fontSize : true,
25858         colors : true,
25859         specialElements : true
25860     });
25861     Roo.bootstrap.htmleditor.ToolbarStandard.superclass.constructor.call(this, config);
25862     
25863     this.editor = config.editor;
25864     this.editorcore = config.editor.editorcore;
25865     
25866     this.buttons   = new Roo.util.MixedCollection(false, function(o) { return o.cmd; });
25867     
25868     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
25869     // dont call parent... till later.
25870 }
25871 Roo.extend(Roo.bootstrap.htmleditor.ToolbarStandard, Roo.bootstrap.NavSimplebar,  {
25872      
25873     bar : true,
25874     
25875     editor : false,
25876     editorcore : false,
25877     
25878     
25879     formats : [
25880         "p" ,  
25881         "h1","h2","h3","h4","h5","h6", 
25882         "pre", "code", 
25883         "abbr", "acronym", "address", "cite", "samp", "var",
25884         'div','span'
25885     ],
25886     
25887     onRender : function(ct, position)
25888     {
25889        // Roo.log("Call onRender: " + this.xtype);
25890         
25891        Roo.bootstrap.htmleditor.ToolbarStandard.superclass.onRender.call(this, ct, position);
25892        Roo.log(this.el);
25893        this.el.dom.style.marginBottom = '0';
25894        var _this = this;
25895        var editorcore = this.editorcore;
25896        var editor= this.editor;
25897        
25898        var children = [];
25899        var btn = function(id,cmd , toggle, handler, html){
25900        
25901             var  event = toggle ? 'toggle' : 'click';
25902        
25903             var a = {
25904                 size : 'sm',
25905                 xtype: 'Button',
25906                 xns: Roo.bootstrap,
25907                 //glyphicon : id,
25908                 fa: id,
25909                 cmd : id || cmd,
25910                 enableToggle:toggle !== false,
25911                 html : html || '',
25912                 pressed : toggle ? false : null,
25913                 listeners : {}
25914             };
25915             a.listeners[toggle ? 'toggle' : 'click'] = function() {
25916                 handler ? handler.call(_this,this) :_this.onBtnClick.call(_this, cmd ||  id);
25917             };
25918             children.push(a);
25919             return a;
25920        }
25921        
25922     //    var cb_box = function...
25923         
25924         var style = {
25925                 xtype: 'Button',
25926                 size : 'sm',
25927                 xns: Roo.bootstrap,
25928                 fa : 'font',
25929                 //html : 'submit'
25930                 menu : {
25931                     xtype: 'Menu',
25932                     xns: Roo.bootstrap,
25933                     items:  []
25934                 }
25935         };
25936         Roo.each(this.formats, function(f) {
25937             style.menu.items.push({
25938                 xtype :'MenuItem',
25939                 xns: Roo.bootstrap,
25940                 html : '<'+ f+' style="margin:2px">'+f +'</'+ f+'>',
25941                 tagname : f,
25942                 listeners : {
25943                     click : function()
25944                     {
25945                         editorcore.insertTag(this.tagname);
25946                         editor.focus();
25947                     }
25948                 }
25949                 
25950             });
25951         });
25952         children.push(style);   
25953         
25954         btn('bold',false,true);
25955         btn('italic',false,true);
25956         btn('align-left', 'justifyleft',true);
25957         btn('align-center', 'justifycenter',true);
25958         btn('align-right' , 'justifyright',true);
25959         btn('link', false, false, function(btn) {
25960             //Roo.log("create link?");
25961             var url = prompt(this.createLinkText, this.defaultLinkValue);
25962             if(url && url != 'http:/'+'/'){
25963                 this.editorcore.relayCmd('createlink', url);
25964             }
25965         }),
25966         btn('list','insertunorderedlist',true);
25967         btn('pencil', false,true, function(btn){
25968                 Roo.log(this);
25969                 this.toggleSourceEdit(btn.pressed);
25970         });
25971         
25972         if (this.editor.btns.length > 0) {
25973             for (var i = 0; i<this.editor.btns.length; i++) {
25974                 children.push(this.editor.btns[i]);
25975             }
25976         }
25977         
25978         /*
25979         var cog = {
25980                 xtype: 'Button',
25981                 size : 'sm',
25982                 xns: Roo.bootstrap,
25983                 glyphicon : 'cog',
25984                 //html : 'submit'
25985                 menu : {
25986                     xtype: 'Menu',
25987                     xns: Roo.bootstrap,
25988                     items:  []
25989                 }
25990         };
25991         
25992         cog.menu.items.push({
25993             xtype :'MenuItem',
25994             xns: Roo.bootstrap,
25995             html : Clean styles,
25996             tagname : f,
25997             listeners : {
25998                 click : function()
25999                 {
26000                     editorcore.insertTag(this.tagname);
26001                     editor.focus();
26002                 }
26003             }
26004             
26005         });
26006        */
26007         
26008          
26009        this.xtype = 'NavSimplebar';
26010         
26011         for(var i=0;i< children.length;i++) {
26012             
26013             this.buttons.add(this.addxtypeChild(children[i]));
26014             
26015         }
26016         
26017         editor.on('editorevent', this.updateToolbar, this);
26018     },
26019     onBtnClick : function(id)
26020     {
26021        this.editorcore.relayCmd(id);
26022        this.editorcore.focus();
26023     },
26024     
26025     /**
26026      * Protected method that will not generally be called directly. It triggers
26027      * a toolbar update by reading the markup state of the current selection in the editor.
26028      */
26029     updateToolbar: function(){
26030
26031         if(!this.editorcore.activated){
26032             this.editor.onFirstFocus(); // is this neeed?
26033             return;
26034         }
26035
26036         var btns = this.buttons; 
26037         var doc = this.editorcore.doc;
26038         btns.get('bold').setActive(doc.queryCommandState('bold'));
26039         btns.get('italic').setActive(doc.queryCommandState('italic'));
26040         //btns.get('underline').setActive(doc.queryCommandState('underline'));
26041         
26042         btns.get('align-left').setActive(doc.queryCommandState('justifyleft'));
26043         btns.get('align-center').setActive(doc.queryCommandState('justifycenter'));
26044         btns.get('align-right').setActive(doc.queryCommandState('justifyright'));
26045         
26046         //btns[frameId + '-insertorderedlist').setActive(doc.queryCommandState('insertorderedlist'));
26047         btns.get('list').setActive(doc.queryCommandState('insertunorderedlist'));
26048          /*
26049         
26050         var ans = this.editorcore.getAllAncestors();
26051         if (this.formatCombo) {
26052             
26053             
26054             var store = this.formatCombo.store;
26055             this.formatCombo.setValue("");
26056             for (var i =0; i < ans.length;i++) {
26057                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
26058                     // select it..
26059                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
26060                     break;
26061                 }
26062             }
26063         }
26064         
26065         
26066         
26067         // hides menus... - so this cant be on a menu...
26068         Roo.bootstrap.MenuMgr.hideAll();
26069         */
26070         Roo.bootstrap.MenuMgr.hideAll();
26071         //this.editorsyncValue();
26072     },
26073     onFirstFocus: function() {
26074         this.buttons.each(function(item){
26075            item.enable();
26076         });
26077     },
26078     toggleSourceEdit : function(sourceEditMode){
26079         
26080           
26081         if(sourceEditMode){
26082             Roo.log("disabling buttons");
26083            this.buttons.each( function(item){
26084                 if(item.cmd != 'pencil'){
26085                     item.disable();
26086                 }
26087             });
26088           
26089         }else{
26090             Roo.log("enabling buttons");
26091             if(this.editorcore.initialized){
26092                 this.buttons.each( function(item){
26093                     item.enable();
26094                 });
26095             }
26096             
26097         }
26098         Roo.log("calling toggole on editor");
26099         // tell the editor that it's been pressed..
26100         this.editor.toggleSourceEdit(sourceEditMode);
26101        
26102     }
26103 });
26104
26105
26106
26107
26108  
26109 /*
26110  * - LGPL
26111  */
26112
26113 /**
26114  * @class Roo.bootstrap.Markdown
26115  * @extends Roo.bootstrap.TextArea
26116  * Bootstrap Showdown editable area
26117  * @cfg {string} content
26118  * 
26119  * @constructor
26120  * Create a new Showdown
26121  */
26122
26123 Roo.bootstrap.Markdown = function(config){
26124     Roo.bootstrap.Markdown.superclass.constructor.call(this, config);
26125    
26126 };
26127
26128 Roo.extend(Roo.bootstrap.Markdown, Roo.bootstrap.TextArea,  {
26129     
26130     editing :false,
26131     
26132     initEvents : function()
26133     {
26134         
26135         Roo.bootstrap.TextArea.prototype.initEvents.call(this);
26136         this.markdownEl = this.el.createChild({
26137             cls : 'roo-markdown-area'
26138         });
26139         this.inputEl().addClass('d-none');
26140         this.markdownEl.dom.innerHTML = Roo.Markdown.toHtml(Roo.util.Format.htmlEncode(this.getValue()));
26141         this.markdownEl.on('click', this.toggleTextEdit, this);
26142         this.on('blur', this.toggleTextEdit, this);
26143         this.on('specialkey', this.resizeTextArea, this);
26144     },
26145     
26146     toggleTextEdit : function()
26147     {
26148         var sh = this.markdownEl.getHeight();
26149         this.inputEl().addClass('d-none');
26150         this.markdownEl.addClass('d-none');
26151         if (!this.editing) {
26152             // show editor?
26153             this.inputEl().setHeight(Math.min(500, Math.max(sh,(this.getValue().split("\n").length+1) * 30)));
26154             this.inputEl().removeClass('d-none');
26155             this.inputEl().focus();
26156             this.editing = true;
26157             return;
26158         }
26159         // show showdown...
26160         this.updateMarkdown();
26161         this.markdownEl.removeClass('d-none');
26162         this.editing = false;
26163         return;
26164     },
26165     updateMarkdown : function()
26166     {
26167         if (this.getValue() == '') {
26168             this.markdownEl.dom.innerHTML = String.format('<span class="roo-placeholder">{0}</span>', this.placeholder);
26169             return;
26170         }
26171         this.markdownEl.dom.innerHTML = Roo.Markdown.toHtml(Roo.util.Format.htmlEncode(this.getValue()));
26172     },
26173     
26174     resizeTextArea: function () {
26175         
26176         var sh = 100;
26177         Roo.log([sh, this.getValue().split("\n").length * 30]);
26178         this.inputEl().setHeight(Math.min(500, Math.max(sh, (this.getValue().split("\n").length +1) * 30)));
26179     },
26180     setValue : function(val)
26181     {
26182         Roo.bootstrap.TextArea.prototype.setValue.call(this,val);
26183         if (!this.editing) {
26184             this.updateMarkdown();
26185         }
26186         
26187     },
26188     focus : function()
26189     {
26190         if (!this.editing) {
26191             this.toggleTextEdit();
26192         }
26193         
26194     }
26195
26196
26197 });
26198 /**
26199  * @class Roo.bootstrap.Table.AbstractSelectionModel
26200  * @extends Roo.util.Observable
26201  * Abstract base class for grid SelectionModels.  It provides the interface that should be
26202  * implemented by descendant classes.  This class should not be directly instantiated.
26203  * @constructor
26204  */
26205 Roo.bootstrap.Table.AbstractSelectionModel = function(){
26206     this.locked = false;
26207     Roo.bootstrap.Table.AbstractSelectionModel.superclass.constructor.call(this);
26208 };
26209
26210
26211 Roo.extend(Roo.bootstrap.Table.AbstractSelectionModel, Roo.util.Observable,  {
26212     /** @ignore Called by the grid automatically. Do not call directly. */
26213     init : function(grid){
26214         this.grid = grid;
26215         this.initEvents();
26216     },
26217
26218     /**
26219      * Locks the selections.
26220      */
26221     lock : function(){
26222         this.locked = true;
26223     },
26224
26225     /**
26226      * Unlocks the selections.
26227      */
26228     unlock : function(){
26229         this.locked = false;
26230     },
26231
26232     /**
26233      * Returns true if the selections are locked.
26234      * @return {Boolean}
26235      */
26236     isLocked : function(){
26237         return this.locked;
26238     },
26239     
26240     
26241     initEvents : function ()
26242     {
26243         
26244     }
26245 });
26246 /**
26247  * @extends Roo.bootstrap.Table.AbstractSelectionModel
26248  * @class Roo.bootstrap.Table.RowSelectionModel
26249  * The default SelectionModel used by {@link Roo.bootstrap.Table}.
26250  * It supports multiple selections and keyboard selection/navigation. 
26251  * @constructor
26252  * @param {Object} config
26253  */
26254
26255 Roo.bootstrap.Table.RowSelectionModel = function(config){
26256     Roo.apply(this, config);
26257     this.selections = new Roo.util.MixedCollection(false, function(o){
26258         return o.id;
26259     });
26260
26261     this.last = false;
26262     this.lastActive = false;
26263
26264     this.addEvents({
26265         /**
26266              * @event selectionchange
26267              * Fires when the selection changes
26268              * @param {SelectionModel} this
26269              */
26270             "selectionchange" : true,
26271         /**
26272              * @event afterselectionchange
26273              * Fires after the selection changes (eg. by key press or clicking)
26274              * @param {SelectionModel} this
26275              */
26276             "afterselectionchange" : true,
26277         /**
26278              * @event beforerowselect
26279              * Fires when a row is selected being selected, return false to cancel.
26280              * @param {SelectionModel} this
26281              * @param {Number} rowIndex The selected index
26282              * @param {Boolean} keepExisting False if other selections will be cleared
26283              */
26284             "beforerowselect" : true,
26285         /**
26286              * @event rowselect
26287              * Fires when a row is selected.
26288              * @param {SelectionModel} this
26289              * @param {Number} rowIndex The selected index
26290              * @param {Roo.data.Record} r The record
26291              */
26292             "rowselect" : true,
26293         /**
26294              * @event rowdeselect
26295              * Fires when a row is deselected.
26296              * @param {SelectionModel} this
26297              * @param {Number} rowIndex The selected index
26298              */
26299         "rowdeselect" : true
26300     });
26301     Roo.bootstrap.Table.RowSelectionModel.superclass.constructor.call(this);
26302     this.locked = false;
26303  };
26304
26305 Roo.extend(Roo.bootstrap.Table.RowSelectionModel, Roo.bootstrap.Table.AbstractSelectionModel,  {
26306     /**
26307      * @cfg {Boolean} singleSelect
26308      * True to allow selection of only one row at a time (defaults to false)
26309      */
26310     singleSelect : false,
26311
26312     // private
26313     initEvents : function()
26314     {
26315
26316         //if(!this.grid.enableDragDrop && !this.grid.enableDrag){
26317         //    this.growclickrid.on("mousedown", this.handleMouseDown, this);
26318         //}else{ // allow click to work like normal
26319          //   this.grid.on("rowclick", this.handleDragableRowClick, this);
26320         //}
26321         //this.grid.on("rowdblclick", this.handleMouseDBClick, this);
26322         this.grid.on("rowclick", this.handleMouseDown, this);
26323         
26324         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
26325             "up" : function(e){
26326                 if(!e.shiftKey){
26327                     this.selectPrevious(e.shiftKey);
26328                 }else if(this.last !== false && this.lastActive !== false){
26329                     var last = this.last;
26330                     this.selectRange(this.last,  this.lastActive-1);
26331                     this.grid.getView().focusRow(this.lastActive);
26332                     if(last !== false){
26333                         this.last = last;
26334                     }
26335                 }else{
26336                     this.selectFirstRow();
26337                 }
26338                 this.fireEvent("afterselectionchange", this);
26339             },
26340             "down" : function(e){
26341                 if(!e.shiftKey){
26342                     this.selectNext(e.shiftKey);
26343                 }else if(this.last !== false && this.lastActive !== false){
26344                     var last = this.last;
26345                     this.selectRange(this.last,  this.lastActive+1);
26346                     this.grid.getView().focusRow(this.lastActive);
26347                     if(last !== false){
26348                         this.last = last;
26349                     }
26350                 }else{
26351                     this.selectFirstRow();
26352                 }
26353                 this.fireEvent("afterselectionchange", this);
26354             },
26355             scope: this
26356         });
26357         this.grid.store.on('load', function(){
26358             this.selections.clear();
26359         },this);
26360         /*
26361         var view = this.grid.view;
26362         view.on("refresh", this.onRefresh, this);
26363         view.on("rowupdated", this.onRowUpdated, this);
26364         view.on("rowremoved", this.onRemove, this);
26365         */
26366     },
26367
26368     // private
26369     onRefresh : function()
26370     {
26371         var ds = this.grid.store, i, v = this.grid.view;
26372         var s = this.selections;
26373         s.each(function(r){
26374             if((i = ds.indexOfId(r.id)) != -1){
26375                 v.onRowSelect(i);
26376             }else{
26377                 s.remove(r);
26378             }
26379         });
26380     },
26381
26382     // private
26383     onRemove : function(v, index, r){
26384         this.selections.remove(r);
26385     },
26386
26387     // private
26388     onRowUpdated : function(v, index, r){
26389         if(this.isSelected(r)){
26390             v.onRowSelect(index);
26391         }
26392     },
26393
26394     /**
26395      * Select records.
26396      * @param {Array} records The records to select
26397      * @param {Boolean} keepExisting (optional) True to keep existing selections
26398      */
26399     selectRecords : function(records, keepExisting)
26400     {
26401         if(!keepExisting){
26402             this.clearSelections();
26403         }
26404             var ds = this.grid.store;
26405         for(var i = 0, len = records.length; i < len; i++){
26406             this.selectRow(ds.indexOf(records[i]), true);
26407         }
26408     },
26409
26410     /**
26411      * Gets the number of selected rows.
26412      * @return {Number}
26413      */
26414     getCount : function(){
26415         return this.selections.length;
26416     },
26417
26418     /**
26419      * Selects the first row in the grid.
26420      */
26421     selectFirstRow : function(){
26422         this.selectRow(0);
26423     },
26424
26425     /**
26426      * Select the last row.
26427      * @param {Boolean} keepExisting (optional) True to keep existing selections
26428      */
26429     selectLastRow : function(keepExisting){
26430         //this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
26431         this.selectRow(this.grid.store.getCount() - 1, keepExisting);
26432     },
26433
26434     /**
26435      * Selects the row immediately following the last selected row.
26436      * @param {Boolean} keepExisting (optional) True to keep existing selections
26437      */
26438     selectNext : function(keepExisting)
26439     {
26440             if(this.last !== false && (this.last+1) < this.grid.store.getCount()){
26441             this.selectRow(this.last+1, keepExisting);
26442             this.grid.getView().focusRow(this.last);
26443         }
26444     },
26445
26446     /**
26447      * Selects the row that precedes the last selected row.
26448      * @param {Boolean} keepExisting (optional) True to keep existing selections
26449      */
26450     selectPrevious : function(keepExisting){
26451         if(this.last){
26452             this.selectRow(this.last-1, keepExisting);
26453             this.grid.getView().focusRow(this.last);
26454         }
26455     },
26456
26457     /**
26458      * Returns the selected records
26459      * @return {Array} Array of selected records
26460      */
26461     getSelections : function(){
26462         return [].concat(this.selections.items);
26463     },
26464
26465     /**
26466      * Returns the first selected record.
26467      * @return {Record}
26468      */
26469     getSelected : function(){
26470         return this.selections.itemAt(0);
26471     },
26472
26473
26474     /**
26475      * Clears all selections.
26476      */
26477     clearSelections : function(fast)
26478     {
26479         if(this.locked) {
26480             return;
26481         }
26482         if(fast !== true){
26483                 var ds = this.grid.store;
26484             var s = this.selections;
26485             s.each(function(r){
26486                 this.deselectRow(ds.indexOfId(r.id));
26487             }, this);
26488             s.clear();
26489         }else{
26490             this.selections.clear();
26491         }
26492         this.last = false;
26493     },
26494
26495
26496     /**
26497      * Selects all rows.
26498      */
26499     selectAll : function(){
26500         if(this.locked) {
26501             return;
26502         }
26503         this.selections.clear();
26504         for(var i = 0, len = this.grid.store.getCount(); i < len; i++){
26505             this.selectRow(i, true);
26506         }
26507     },
26508
26509     /**
26510      * Returns True if there is a selection.
26511      * @return {Boolean}
26512      */
26513     hasSelection : function(){
26514         return this.selections.length > 0;
26515     },
26516
26517     /**
26518      * Returns True if the specified row is selected.
26519      * @param {Number/Record} record The record or index of the record to check
26520      * @return {Boolean}
26521      */
26522     isSelected : function(index){
26523             var r = typeof index == "number" ? this.grid.store.getAt(index) : index;
26524         return (r && this.selections.key(r.id) ? true : false);
26525     },
26526
26527     /**
26528      * Returns True if the specified record id is selected.
26529      * @param {String} id The id of record to check
26530      * @return {Boolean}
26531      */
26532     isIdSelected : function(id){
26533         return (this.selections.key(id) ? true : false);
26534     },
26535
26536
26537     // private
26538     handleMouseDBClick : function(e, t){
26539         
26540     },
26541     // private
26542     handleMouseDown : function(e, t)
26543     {
26544             var rowIndex = this.grid.headerShow  ? t.dom.rowIndex - 1 : t.dom.rowIndex ; // first row is header???
26545         if(this.isLocked() || rowIndex < 0 ){
26546             return;
26547         };
26548         if(e.shiftKey && this.last !== false){
26549             var last = this.last;
26550             this.selectRange(last, rowIndex, e.ctrlKey);
26551             this.last = last; // reset the last
26552             t.focus();
26553     
26554         }else{
26555             var isSelected = this.isSelected(rowIndex);
26556             //Roo.log("select row:" + rowIndex);
26557             if(isSelected){
26558                 this.deselectRow(rowIndex);
26559             } else {
26560                         this.selectRow(rowIndex, true);
26561             }
26562     
26563             /*
26564                 if(e.button !== 0 && isSelected){
26565                 alert('rowIndex 2: ' + rowIndex);
26566                     view.focusRow(rowIndex);
26567                 }else if(e.ctrlKey && isSelected){
26568                     this.deselectRow(rowIndex);
26569                 }else if(!isSelected){
26570                     this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
26571                     view.focusRow(rowIndex);
26572                 }
26573             */
26574         }
26575         this.fireEvent("afterselectionchange", this);
26576     },
26577     // private
26578     handleDragableRowClick :  function(grid, rowIndex, e) 
26579     {
26580         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
26581             this.selectRow(rowIndex, false);
26582             grid.view.focusRow(rowIndex);
26583              this.fireEvent("afterselectionchange", this);
26584         }
26585     },
26586     
26587     /**
26588      * Selects multiple rows.
26589      * @param {Array} rows Array of the indexes of the row to select
26590      * @param {Boolean} keepExisting (optional) True to keep existing selections
26591      */
26592     selectRows : function(rows, keepExisting){
26593         if(!keepExisting){
26594             this.clearSelections();
26595         }
26596         for(var i = 0, len = rows.length; i < len; i++){
26597             this.selectRow(rows[i], true);
26598         }
26599     },
26600
26601     /**
26602      * Selects a range of rows. All rows in between startRow and endRow are also selected.
26603      * @param {Number} startRow The index of the first row in the range
26604      * @param {Number} endRow The index of the last row in the range
26605      * @param {Boolean} keepExisting (optional) True to retain existing selections
26606      */
26607     selectRange : function(startRow, endRow, keepExisting){
26608         if(this.locked) {
26609             return;
26610         }
26611         if(!keepExisting){
26612             this.clearSelections();
26613         }
26614         if(startRow <= endRow){
26615             for(var i = startRow; i <= endRow; i++){
26616                 this.selectRow(i, true);
26617             }
26618         }else{
26619             for(var i = startRow; i >= endRow; i--){
26620                 this.selectRow(i, true);
26621             }
26622         }
26623     },
26624
26625     /**
26626      * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
26627      * @param {Number} startRow The index of the first row in the range
26628      * @param {Number} endRow The index of the last row in the range
26629      */
26630     deselectRange : function(startRow, endRow, preventViewNotify){
26631         if(this.locked) {
26632             return;
26633         }
26634         for(var i = startRow; i <= endRow; i++){
26635             this.deselectRow(i, preventViewNotify);
26636         }
26637     },
26638
26639     /**
26640      * Selects a row.
26641      * @param {Number} row The index of the row to select
26642      * @param {Boolean} keepExisting (optional) True to keep existing selections
26643      */
26644     selectRow : function(index, keepExisting, preventViewNotify)
26645     {
26646             if(this.locked || (index < 0 || index > this.grid.store.getCount())) {
26647             return;
26648         }
26649         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
26650             if(!keepExisting || this.singleSelect){
26651                 this.clearSelections();
26652             }
26653             
26654             var r = this.grid.store.getAt(index);
26655             //console.log('selectRow - record id :' + r.id);
26656             
26657             this.selections.add(r);
26658             this.last = this.lastActive = index;
26659             if(!preventViewNotify){
26660                 var proxy = new Roo.Element(
26661                                 this.grid.getRowDom(index)
26662                 );
26663                 proxy.addClass('bg-info info');
26664             }
26665             this.fireEvent("rowselect", this, index, r);
26666             this.fireEvent("selectionchange", this);
26667         }
26668     },
26669
26670     /**
26671      * Deselects a row.
26672      * @param {Number} row The index of the row to deselect
26673      */
26674     deselectRow : function(index, preventViewNotify)
26675     {
26676         if(this.locked) {
26677             return;
26678         }
26679         if(this.last == index){
26680             this.last = false;
26681         }
26682         if(this.lastActive == index){
26683             this.lastActive = false;
26684         }
26685         
26686         var r = this.grid.store.getAt(index);
26687         if (!r) {
26688             return;
26689         }
26690         
26691         this.selections.remove(r);
26692         //.console.log('deselectRow - record id :' + r.id);
26693         if(!preventViewNotify){
26694         
26695             var proxy = new Roo.Element(
26696                 this.grid.getRowDom(index)
26697             );
26698             proxy.removeClass('bg-info info');
26699         }
26700         this.fireEvent("rowdeselect", this, index);
26701         this.fireEvent("selectionchange", this);
26702     },
26703
26704     // private
26705     restoreLast : function(){
26706         if(this._last){
26707             this.last = this._last;
26708         }
26709     },
26710
26711     // private
26712     acceptsNav : function(row, col, cm){
26713         return !cm.isHidden(col) && cm.isCellEditable(col, row);
26714     },
26715
26716     // private
26717     onEditorKey : function(field, e){
26718         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
26719         if(k == e.TAB){
26720             e.stopEvent();
26721             ed.completeEdit();
26722             if(e.shiftKey){
26723                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
26724             }else{
26725                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
26726             }
26727         }else if(k == e.ENTER && !e.ctrlKey){
26728             e.stopEvent();
26729             ed.completeEdit();
26730             if(e.shiftKey){
26731                 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
26732             }else{
26733                 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
26734             }
26735         }else if(k == e.ESC){
26736             ed.cancelEdit();
26737         }
26738         if(newCell){
26739             g.startEditing(newCell[0], newCell[1]);
26740         }
26741     }
26742 });
26743 /*
26744  * Based on:
26745  * Ext JS Library 1.1.1
26746  * Copyright(c) 2006-2007, Ext JS, LLC.
26747  *
26748  * Originally Released Under LGPL - original licence link has changed is not relivant.
26749  *
26750  * Fork - LGPL
26751  * <script type="text/javascript">
26752  */
26753  
26754 /**
26755  * @class Roo.bootstrap.PagingToolbar
26756  * @extends Roo.bootstrap.NavSimplebar
26757  * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
26758  * @constructor
26759  * Create a new PagingToolbar
26760  * @param {Object} config The config object
26761  * @param {Roo.data.Store} store
26762  */
26763 Roo.bootstrap.PagingToolbar = function(config)
26764 {
26765     // old args format still supported... - xtype is prefered..
26766         // created from xtype...
26767     
26768     this.ds = config.dataSource;
26769     
26770     if (config.store && !this.ds) {
26771         this.store= Roo.factory(config.store, Roo.data);
26772         this.ds = this.store;
26773         this.ds.xmodule = this.xmodule || false;
26774     }
26775     
26776     this.toolbarItems = [];
26777     if (config.items) {
26778         this.toolbarItems = config.items;
26779     }
26780     
26781     Roo.bootstrap.PagingToolbar.superclass.constructor.call(this, config);
26782     
26783     this.cursor = 0;
26784     
26785     if (this.ds) { 
26786         this.bind(this.ds);
26787     }
26788     
26789     if (Roo.bootstrap.version == 4) {
26790         this.navgroup = new Roo.bootstrap.ButtonGroup({ cls: 'pagination' });
26791     } else {
26792         this.navgroup = new Roo.bootstrap.NavGroup({ cls: 'pagination' });
26793     }
26794     
26795 };
26796
26797 Roo.extend(Roo.bootstrap.PagingToolbar, Roo.bootstrap.NavSimplebar, {
26798     /**
26799      * @cfg {Roo.data.Store} dataSource
26800      * The underlying data store providing the paged data
26801      */
26802     /**
26803      * @cfg {String/HTMLElement/Element} container
26804      * container The id or element that will contain the toolbar
26805      */
26806     /**
26807      * @cfg {Boolean} displayInfo
26808      * True to display the displayMsg (defaults to false)
26809      */
26810     /**
26811      * @cfg {Number} pageSize
26812      * The number of records to display per page (defaults to 20)
26813      */
26814     pageSize: 20,
26815     /**
26816      * @cfg {String} displayMsg
26817      * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
26818      */
26819     displayMsg : 'Displaying {0} - {1} of {2}',
26820     /**
26821      * @cfg {String} emptyMsg
26822      * The message to display when no records are found (defaults to "No data to display")
26823      */
26824     emptyMsg : 'No data to display',
26825     /**
26826      * Customizable piece of the default paging text (defaults to "Page")
26827      * @type String
26828      */
26829     beforePageText : "Page",
26830     /**
26831      * Customizable piece of the default paging text (defaults to "of %0")
26832      * @type String
26833      */
26834     afterPageText : "of {0}",
26835     /**
26836      * Customizable piece of the default paging text (defaults to "First Page")
26837      * @type String
26838      */
26839     firstText : "First Page",
26840     /**
26841      * Customizable piece of the default paging text (defaults to "Previous Page")
26842      * @type String
26843      */
26844     prevText : "Previous Page",
26845     /**
26846      * Customizable piece of the default paging text (defaults to "Next Page")
26847      * @type String
26848      */
26849     nextText : "Next Page",
26850     /**
26851      * Customizable piece of the default paging text (defaults to "Last Page")
26852      * @type String
26853      */
26854     lastText : "Last Page",
26855     /**
26856      * Customizable piece of the default paging text (defaults to "Refresh")
26857      * @type String
26858      */
26859     refreshText : "Refresh",
26860
26861     buttons : false,
26862     // private
26863     onRender : function(ct, position) 
26864     {
26865         Roo.bootstrap.PagingToolbar.superclass.onRender.call(this, ct, position);
26866         this.navgroup.parentId = this.id;
26867         this.navgroup.onRender(this.el, null);
26868         // add the buttons to the navgroup
26869         
26870         if(this.displayInfo){
26871             this.el.select('ul.navbar-nav',true).first().createChild({cls:'x-paging-info'});
26872             this.displayEl = this.el.select('.x-paging-info', true).first();
26873 //            var navel = this.navgroup.addItem( { tagtype : 'span', html : '', cls : 'x-paging-info', preventDefault : true } );
26874 //            this.displayEl = navel.el.select('span',true).first();
26875         }
26876         
26877         var _this = this;
26878         
26879         if(this.buttons){
26880             Roo.each(_this.buttons, function(e){ // this might need to use render????
26881                Roo.factory(e).render(_this.el);
26882             });
26883         }
26884             
26885         Roo.each(_this.toolbarItems, function(e) {
26886             _this.navgroup.addItem(e);
26887         });
26888         
26889         
26890         this.first = this.navgroup.addItem({
26891             tooltip: this.firstText,
26892             cls: "prev btn-outline-secondary",
26893             html : ' <i class="fa fa-step-backward"></i>',
26894             disabled: true,
26895             preventDefault: true,
26896             listeners : { click : this.onClick.createDelegate(this, ["first"]) }
26897         });
26898         
26899         this.prev =  this.navgroup.addItem({
26900             tooltip: this.prevText,
26901             cls: "prev btn-outline-secondary",
26902             html : ' <i class="fa fa-backward"></i>',
26903             disabled: true,
26904             preventDefault: true,
26905             listeners : { click :  this.onClick.createDelegate(this, ["prev"]) }
26906         });
26907     //this.addSeparator();
26908         
26909         
26910         var field = this.navgroup.addItem( {
26911             tagtype : 'span',
26912             cls : 'x-paging-position  btn-outline-secondary',
26913              disabled: true,
26914             html : this.beforePageText  +
26915                 '<input type="text" size="3" value="1" class="x-grid-page-number">' +
26916                 '<span class="x-paging-after">' +  String.format(this.afterPageText, 1) + '</span>'
26917          } ); //?? escaped?
26918         
26919         this.field = field.el.select('input', true).first();
26920         this.field.on("keydown", this.onPagingKeydown, this);
26921         this.field.on("focus", function(){this.dom.select();});
26922     
26923     
26924         this.afterTextEl =  field.el.select('.x-paging-after',true).first();
26925         //this.field.setHeight(18);
26926         //this.addSeparator();
26927         this.next = this.navgroup.addItem({
26928             tooltip: this.nextText,
26929             cls: "next btn-outline-secondary",
26930             html : ' <i class="fa fa-forward"></i>',
26931             disabled: true,
26932             preventDefault: true,
26933             listeners : { click :  this.onClick.createDelegate(this, ["next"]) }
26934         });
26935         this.last = this.navgroup.addItem({
26936             tooltip: this.lastText,
26937             html : ' <i class="fa fa-step-forward"></i>',
26938             cls: "next btn-outline-secondary",
26939             disabled: true,
26940             preventDefault: true,
26941             listeners : { click :  this.onClick.createDelegate(this, ["last"]) }
26942         });
26943     //this.addSeparator();
26944         this.loading = this.navgroup.addItem({
26945             tooltip: this.refreshText,
26946             cls: "btn-outline-secondary",
26947             html : ' <i class="fa fa-refresh"></i>',
26948             preventDefault: true,
26949             listeners : { click : this.onClick.createDelegate(this, ["refresh"]) }
26950         });
26951         
26952     },
26953
26954     // private
26955     updateInfo : function(){
26956         if(this.displayEl){
26957             var count = (typeof(this.getCount) == 'undefined') ? this.ds.getCount() : this.getCount();
26958             var msg = count == 0 ?
26959                 this.emptyMsg :
26960                 String.format(
26961                     this.displayMsg,
26962                     this.cursor+1, this.cursor+count, this.ds.getTotalCount()    
26963                 );
26964             this.displayEl.update(msg);
26965         }
26966     },
26967
26968     // private
26969     onLoad : function(ds, r, o)
26970     {
26971         this.cursor = o.params.start ? o.params.start : 0;
26972         
26973         var d = this.getPageData(),
26974             ap = d.activePage,
26975             ps = d.pages;
26976         
26977         
26978         this.afterTextEl.dom.innerHTML = String.format(this.afterPageText, d.pages);
26979         this.field.dom.value = ap;
26980         this.first.setDisabled(ap == 1);
26981         this.prev.setDisabled(ap == 1);
26982         this.next.setDisabled(ap == ps);
26983         this.last.setDisabled(ap == ps);
26984         this.loading.enable();
26985         this.updateInfo();
26986     },
26987
26988     // private
26989     getPageData : function(){
26990         var total = this.ds.getTotalCount();
26991         return {
26992             total : total,
26993             activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
26994             pages :  total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
26995         };
26996     },
26997
26998     // private
26999     onLoadError : function(){
27000         this.loading.enable();
27001     },
27002
27003     // private
27004     onPagingKeydown : function(e){
27005         var k = e.getKey();
27006         var d = this.getPageData();
27007         if(k == e.RETURN){
27008             var v = this.field.dom.value, pageNum;
27009             if(!v || isNaN(pageNum = parseInt(v, 10))){
27010                 this.field.dom.value = d.activePage;
27011                 return;
27012             }
27013             pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
27014             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
27015             e.stopEvent();
27016         }
27017         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))
27018         {
27019           var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
27020           this.field.dom.value = pageNum;
27021           this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
27022           e.stopEvent();
27023         }
27024         else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
27025         {
27026           var v = this.field.dom.value, pageNum; 
27027           var increment = (e.shiftKey) ? 10 : 1;
27028           if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN) {
27029                 increment *= -1;
27030           }
27031           if(!v || isNaN(pageNum = parseInt(v, 10))) {
27032             this.field.dom.value = d.activePage;
27033             return;
27034           }
27035           else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
27036           {
27037             this.field.dom.value = parseInt(v, 10) + increment;
27038             pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
27039             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
27040           }
27041           e.stopEvent();
27042         }
27043     },
27044
27045     // private
27046     beforeLoad : function(){
27047         if(this.loading){
27048             this.loading.disable();
27049         }
27050     },
27051
27052     // private
27053     onClick : function(which){
27054         
27055         var ds = this.ds;
27056         if (!ds) {
27057             return;
27058         }
27059         
27060         switch(which){
27061             case "first":
27062                 ds.load({params:{start: 0, limit: this.pageSize}});
27063             break;
27064             case "prev":
27065                 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
27066             break;
27067             case "next":
27068                 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
27069             break;
27070             case "last":
27071                 var total = ds.getTotalCount();
27072                 var extra = total % this.pageSize;
27073                 var lastStart = extra ? (total - extra) : total-this.pageSize;
27074                 ds.load({params:{start: lastStart, limit: this.pageSize}});
27075             break;
27076             case "refresh":
27077                 ds.load({params:{start: this.cursor, limit: this.pageSize}});
27078             break;
27079         }
27080     },
27081
27082     /**
27083      * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
27084      * @param {Roo.data.Store} store The data store to unbind
27085      */
27086     unbind : function(ds){
27087         ds.un("beforeload", this.beforeLoad, this);
27088         ds.un("load", this.onLoad, this);
27089         ds.un("loadexception", this.onLoadError, this);
27090         ds.un("remove", this.updateInfo, this);
27091         ds.un("add", this.updateInfo, this);
27092         this.ds = undefined;
27093     },
27094
27095     /**
27096      * Binds the paging toolbar to the specified {@link Roo.data.Store}
27097      * @param {Roo.data.Store} store The data store to bind
27098      */
27099     bind : function(ds){
27100         ds.on("beforeload", this.beforeLoad, this);
27101         ds.on("load", this.onLoad, this);
27102         ds.on("loadexception", this.onLoadError, this);
27103         ds.on("remove", this.updateInfo, this);
27104         ds.on("add", this.updateInfo, this);
27105         this.ds = ds;
27106     }
27107 });/*
27108  * - LGPL
27109  *
27110  * element
27111  * 
27112  */
27113
27114 /**
27115  * @class Roo.bootstrap.MessageBar
27116  * @extends Roo.bootstrap.Component
27117  * Bootstrap MessageBar class
27118  * @cfg {String} html contents of the MessageBar
27119  * @cfg {String} weight (info | success | warning | danger) default info
27120  * @cfg {String} beforeClass insert the bar before the given class
27121  * @cfg {Boolean} closable (true | false) default false
27122  * @cfg {Boolean} fixed (true | false) default false, fix the bar at the top
27123  * 
27124  * @constructor
27125  * Create a new Element
27126  * @param {Object} config The config object
27127  */
27128
27129 Roo.bootstrap.MessageBar = function(config){
27130     Roo.bootstrap.MessageBar.superclass.constructor.call(this, config);
27131 };
27132
27133 Roo.extend(Roo.bootstrap.MessageBar, Roo.bootstrap.Component,  {
27134     
27135     html: '',
27136     weight: 'info',
27137     closable: false,
27138     fixed: false,
27139     beforeClass: 'bootstrap-sticky-wrap',
27140     
27141     getAutoCreate : function(){
27142         
27143         var cfg = {
27144             tag: 'div',
27145             cls: 'alert alert-dismissable alert-' + this.weight,
27146             cn: [
27147                 {
27148                     tag: 'span',
27149                     cls: 'message',
27150                     html: this.html || ''
27151                 }
27152             ]
27153         };
27154         
27155         if(this.fixed){
27156             cfg.cls += ' alert-messages-fixed';
27157         }
27158         
27159         if(this.closable){
27160             cfg.cn.push({
27161                 tag: 'button',
27162                 cls: 'close',
27163                 html: 'x'
27164             });
27165         }
27166         
27167         return cfg;
27168     },
27169     
27170     onRender : function(ct, position)
27171     {
27172         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
27173         
27174         if(!this.el){
27175             var cfg = Roo.apply({},  this.getAutoCreate());
27176             cfg.id = Roo.id();
27177             
27178             if (this.cls) {
27179                 cfg.cls += ' ' + this.cls;
27180             }
27181             if (this.style) {
27182                 cfg.style = this.style;
27183             }
27184             this.el = Roo.get(document.body).createChild(cfg, Roo.select('.'+this.beforeClass, true).first());
27185             
27186             this.el.setVisibilityMode(Roo.Element.DISPLAY);
27187         }
27188         
27189         this.el.select('>button.close').on('click', this.hide, this);
27190         
27191     },
27192     
27193     show : function()
27194     {
27195         if (!this.rendered) {
27196             this.render();
27197         }
27198         
27199         this.el.show();
27200         
27201         this.fireEvent('show', this);
27202         
27203     },
27204     
27205     hide : function()
27206     {
27207         if (!this.rendered) {
27208             this.render();
27209         }
27210         
27211         this.el.hide();
27212         
27213         this.fireEvent('hide', this);
27214     },
27215     
27216     update : function()
27217     {
27218 //        var e = this.el.dom.firstChild;
27219 //        
27220 //        if(this.closable){
27221 //            e = e.nextSibling;
27222 //        }
27223 //        
27224 //        e.data = this.html || '';
27225
27226         this.el.select('>.message', true).first().dom.innerHTML = this.html || '';
27227     }
27228    
27229 });
27230
27231  
27232
27233      /*
27234  * - LGPL
27235  *
27236  * Graph
27237  * 
27238  */
27239
27240
27241 /**
27242  * @class Roo.bootstrap.Graph
27243  * @extends Roo.bootstrap.Component
27244  * Bootstrap Graph class
27245 > Prameters
27246  -sm {number} sm 4
27247  -md {number} md 5
27248  @cfg {String} graphtype  bar | vbar | pie
27249  @cfg {number} g_x coodinator | centre x (pie)
27250  @cfg {number} g_y coodinator | centre y (pie)
27251  @cfg {number} g_r radius (pie)
27252  @cfg {number} g_height height of the chart (respected by all elements in the set)
27253  @cfg {number} g_width width of the chart (respected by all elements in the set)
27254  @cfg {Object} title The title of the chart
27255     
27256  -{Array}  values
27257  -opts (object) options for the chart 
27258      o {
27259      o type (string) type of endings of the bar. Default: 'square'. Other options are: 'round', 'sharp', 'soft'.
27260      o gutter (number)(string) default '20%' (WHAT DOES IT DO?)
27261      o vgutter (number)
27262      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.
27263      o stacked (boolean) whether or not to tread values as in a stacked bar chart
27264      o to
27265      o stretch (boolean)
27266      o }
27267  -opts (object) options for the pie
27268      o{
27269      o cut
27270      o startAngle (number)
27271      o endAngle (number)
27272      } 
27273  *
27274  * @constructor
27275  * Create a new Input
27276  * @param {Object} config The config object
27277  */
27278
27279 Roo.bootstrap.Graph = function(config){
27280     Roo.bootstrap.Graph.superclass.constructor.call(this, config);
27281     
27282     this.addEvents({
27283         // img events
27284         /**
27285          * @event click
27286          * The img click event for the img.
27287          * @param {Roo.EventObject} e
27288          */
27289         "click" : true
27290     });
27291 };
27292
27293 Roo.extend(Roo.bootstrap.Graph, Roo.bootstrap.Component,  {
27294     
27295     sm: 4,
27296     md: 5,
27297     graphtype: 'bar',
27298     g_height: 250,
27299     g_width: 400,
27300     g_x: 50,
27301     g_y: 50,
27302     g_r: 30,
27303     opts:{
27304         //g_colors: this.colors,
27305         g_type: 'soft',
27306         g_gutter: '20%'
27307
27308     },
27309     title : false,
27310
27311     getAutoCreate : function(){
27312         
27313         var cfg = {
27314             tag: 'div',
27315             html : null
27316         };
27317         
27318         
27319         return  cfg;
27320     },
27321
27322     onRender : function(ct,position){
27323         
27324         
27325         Roo.bootstrap.Graph.superclass.onRender.call(this,ct,position);
27326         
27327         if (typeof(Raphael) == 'undefined') {
27328             Roo.bootstrap.MessageBox.alert("Error","Raphael is not availabe");
27329             return;
27330         }
27331         
27332         this.raphael = Raphael(this.el.dom);
27333         
27334                     // data1 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
27335                     // data2 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
27336                     // data3 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
27337                     // txtattr = { font: "12px 'Fontin Sans', Fontin-Sans, sans-serif" };
27338                 /*
27339                 r.text(160, 10, "Single Series Chart").attr(txtattr);
27340                 r.text(480, 10, "Multiline Series Chart").attr(txtattr);
27341                 r.text(160, 250, "Multiple Series Stacked Chart").attr(txtattr);
27342                 r.text(480, 250, 'Multiline Series Stacked Vertical Chart. Type "round"').attr(txtattr);
27343                 
27344                 r.barchart(10, 10, 300, 220, [[55, 20, 13, 32, 5, 1, 2, 10]], 0, {type: "sharp"});
27345                 r.barchart(330, 10, 300, 220, data1);
27346                 r.barchart(10, 250, 300, 220, data2, {stacked: true});
27347                 r.barchart(330, 250, 300, 220, data3, {stacked: true, type: "round"});
27348                 */
27349                 
27350                 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
27351                 // r.barchart(30, 30, 560, 250,  xdata, {
27352                 //    labels : [55, 20, 13, 32, 5, 1, 2, 10,5 , 10],
27353                 //     axis : "0 0 1 1",
27354                 //     axisxlabels :  xdata
27355                 //     //yvalues : cols,
27356                    
27357                 // });
27358 //        var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
27359 //        
27360 //        this.load(null,xdata,{
27361 //                axis : "0 0 1 1",
27362 //                axisxlabels :  xdata
27363 //                });
27364
27365     },
27366
27367     load : function(graphtype,xdata,opts)
27368     {
27369         this.raphael.clear();
27370         if(!graphtype) {
27371             graphtype = this.graphtype;
27372         }
27373         if(!opts){
27374             opts = this.opts;
27375         }
27376         var r = this.raphael,
27377             fin = function () {
27378                 this.flag = r.popup(this.bar.x, this.bar.y, this.bar.value || "0").insertBefore(this);
27379             },
27380             fout = function () {
27381                 this.flag.animate({opacity: 0}, 300, function () {this.remove();});
27382             },
27383             pfin = function() {
27384                 this.sector.stop();
27385                 this.sector.scale(1.1, 1.1, this.cx, this.cy);
27386
27387                 if (this.label) {
27388                     this.label[0].stop();
27389                     this.label[0].attr({ r: 7.5 });
27390                     this.label[1].attr({ "font-weight": 800 });
27391                 }
27392             },
27393             pfout = function() {
27394                 this.sector.animate({ transform: 's1 1 ' + this.cx + ' ' + this.cy }, 500, "bounce");
27395
27396                 if (this.label) {
27397                     this.label[0].animate({ r: 5 }, 500, "bounce");
27398                     this.label[1].attr({ "font-weight": 400 });
27399                 }
27400             };
27401
27402         switch(graphtype){
27403             case 'bar':
27404                 this.raphael.barchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
27405                 break;
27406             case 'hbar':
27407                 this.raphael.hbarchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
27408                 break;
27409             case 'pie':
27410 //                opts = { legend: ["%% - Enterprise Users", "% - ddd","Chrome Users"], legendpos: "west", 
27411 //                href: ["http://raphaeljs.com", "http://g.raphaeljs.com"]};
27412 //            
27413                 this.raphael.piechart(this.g_x,this.g_y,this.g_r,xdata,opts).hover(pfin, pfout);
27414                 
27415                 break;
27416
27417         }
27418         
27419         if(this.title){
27420             this.raphael.text(this.title.x, this.title.y, this.title.text).attr(this.title.attr);
27421         }
27422         
27423     },
27424     
27425     setTitle: function(o)
27426     {
27427         this.title = o;
27428     },
27429     
27430     initEvents: function() {
27431         
27432         if(!this.href){
27433             this.el.on('click', this.onClick, this);
27434         }
27435     },
27436     
27437     onClick : function(e)
27438     {
27439         Roo.log('img onclick');
27440         this.fireEvent('click', this, e);
27441     }
27442    
27443 });
27444
27445  
27446 /*
27447  * - LGPL
27448  *
27449  * numberBox
27450  * 
27451  */
27452 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
27453
27454 /**
27455  * @class Roo.bootstrap.dash.NumberBox
27456  * @extends Roo.bootstrap.Component
27457  * Bootstrap NumberBox class
27458  * @cfg {String} headline Box headline
27459  * @cfg {String} content Box content
27460  * @cfg {String} icon Box icon
27461  * @cfg {String} footer Footer text
27462  * @cfg {String} fhref Footer href
27463  * 
27464  * @constructor
27465  * Create a new NumberBox
27466  * @param {Object} config The config object
27467  */
27468
27469
27470 Roo.bootstrap.dash.NumberBox = function(config){
27471     Roo.bootstrap.dash.NumberBox.superclass.constructor.call(this, config);
27472     
27473 };
27474
27475 Roo.extend(Roo.bootstrap.dash.NumberBox, Roo.bootstrap.Component,  {
27476     
27477     headline : '',
27478     content : '',
27479     icon : '',
27480     footer : '',
27481     fhref : '',
27482     ficon : '',
27483     
27484     getAutoCreate : function(){
27485         
27486         var cfg = {
27487             tag : 'div',
27488             cls : 'small-box ',
27489             cn : [
27490                 {
27491                     tag : 'div',
27492                     cls : 'inner',
27493                     cn :[
27494                         {
27495                             tag : 'h3',
27496                             cls : 'roo-headline',
27497                             html : this.headline
27498                         },
27499                         {
27500                             tag : 'p',
27501                             cls : 'roo-content',
27502                             html : this.content
27503                         }
27504                     ]
27505                 }
27506             ]
27507         };
27508         
27509         if(this.icon){
27510             cfg.cn.push({
27511                 tag : 'div',
27512                 cls : 'icon',
27513                 cn :[
27514                     {
27515                         tag : 'i',
27516                         cls : 'ion ' + this.icon
27517                     }
27518                 ]
27519             });
27520         }
27521         
27522         if(this.footer){
27523             var footer = {
27524                 tag : 'a',
27525                 cls : 'small-box-footer',
27526                 href : this.fhref || '#',
27527                 html : this.footer
27528             };
27529             
27530             cfg.cn.push(footer);
27531             
27532         }
27533         
27534         return  cfg;
27535     },
27536
27537     onRender : function(ct,position){
27538         Roo.bootstrap.dash.NumberBox.superclass.onRender.call(this,ct,position);
27539
27540
27541        
27542                 
27543     },
27544
27545     setHeadline: function (value)
27546     {
27547         this.el.select('.roo-headline',true).first().dom.innerHTML = value;
27548     },
27549     
27550     setFooter: function (value, href)
27551     {
27552         this.el.select('a.small-box-footer',true).first().dom.innerHTML = value;
27553         
27554         if(href){
27555             this.el.select('a.small-box-footer',true).first().attr('href', href);
27556         }
27557         
27558     },
27559
27560     setContent: function (value)
27561     {
27562         this.el.select('.roo-content',true).first().dom.innerHTML = value;
27563     },
27564
27565     initEvents: function() 
27566     {   
27567         
27568     }
27569     
27570 });
27571
27572  
27573 /*
27574  * - LGPL
27575  *
27576  * TabBox
27577  * 
27578  */
27579 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
27580
27581 /**
27582  * @class Roo.bootstrap.dash.TabBox
27583  * @extends Roo.bootstrap.Component
27584  * Bootstrap TabBox class
27585  * @cfg {String} title Title of the TabBox
27586  * @cfg {String} icon Icon of the TabBox
27587  * @cfg {Boolean} showtabs (true|false) show the tabs default true
27588  * @cfg {Boolean} tabScrollable (true|false) tab scrollable when mobile view default false
27589  * 
27590  * @constructor
27591  * Create a new TabBox
27592  * @param {Object} config The config object
27593  */
27594
27595
27596 Roo.bootstrap.dash.TabBox = function(config){
27597     Roo.bootstrap.dash.TabBox.superclass.constructor.call(this, config);
27598     this.addEvents({
27599         // raw events
27600         /**
27601          * @event addpane
27602          * When a pane is added
27603          * @param {Roo.bootstrap.dash.TabPane} pane
27604          */
27605         "addpane" : true,
27606         /**
27607          * @event activatepane
27608          * When a pane is activated
27609          * @param {Roo.bootstrap.dash.TabPane} pane
27610          */
27611         "activatepane" : true
27612         
27613          
27614     });
27615     
27616     this.panes = [];
27617 };
27618
27619 Roo.extend(Roo.bootstrap.dash.TabBox, Roo.bootstrap.Component,  {
27620
27621     title : '',
27622     icon : false,
27623     showtabs : true,
27624     tabScrollable : false,
27625     
27626     getChildContainer : function()
27627     {
27628         return this.el.select('.tab-content', true).first();
27629     },
27630     
27631     getAutoCreate : function(){
27632         
27633         var header = {
27634             tag: 'li',
27635             cls: 'pull-left header',
27636             html: this.title,
27637             cn : []
27638         };
27639         
27640         if(this.icon){
27641             header.cn.push({
27642                 tag: 'i',
27643                 cls: 'fa ' + this.icon
27644             });
27645         }
27646         
27647         var h = {
27648             tag: 'ul',
27649             cls: 'nav nav-tabs pull-right',
27650             cn: [
27651                 header
27652             ]
27653         };
27654         
27655         if(this.tabScrollable){
27656             h = {
27657                 tag: 'div',
27658                 cls: 'tab-header',
27659                 cn: [
27660                     {
27661                         tag: 'ul',
27662                         cls: 'nav nav-tabs pull-right',
27663                         cn: [
27664                             header
27665                         ]
27666                     }
27667                 ]
27668             };
27669         }
27670         
27671         var cfg = {
27672             tag: 'div',
27673             cls: 'nav-tabs-custom',
27674             cn: [
27675                 h,
27676                 {
27677                     tag: 'div',
27678                     cls: 'tab-content no-padding',
27679                     cn: []
27680                 }
27681             ]
27682         };
27683
27684         return  cfg;
27685     },
27686     initEvents : function()
27687     {
27688         //Roo.log('add add pane handler');
27689         this.on('addpane', this.onAddPane, this);
27690     },
27691      /**
27692      * Updates the box title
27693      * @param {String} html to set the title to.
27694      */
27695     setTitle : function(value)
27696     {
27697         this.el.select('.nav-tabs .header', true).first().dom.innerHTML = value;
27698     },
27699     onAddPane : function(pane)
27700     {
27701         this.panes.push(pane);
27702         //Roo.log('addpane');
27703         //Roo.log(pane);
27704         // tabs are rendere left to right..
27705         if(!this.showtabs){
27706             return;
27707         }
27708         
27709         var ctr = this.el.select('.nav-tabs', true).first();
27710          
27711          
27712         var existing = ctr.select('.nav-tab',true);
27713         var qty = existing.getCount();;
27714         
27715         
27716         var tab = ctr.createChild({
27717             tag : 'li',
27718             cls : 'nav-tab' + (qty ? '' : ' active'),
27719             cn : [
27720                 {
27721                     tag : 'a',
27722                     href:'#',
27723                     html : pane.title
27724                 }
27725             ]
27726         }, qty ? existing.first().dom : ctr.select('.header', true).first().dom );
27727         pane.tab = tab;
27728         
27729         tab.on('click', this.onTabClick.createDelegate(this, [pane], true));
27730         if (!qty) {
27731             pane.el.addClass('active');
27732         }
27733         
27734                 
27735     },
27736     onTabClick : function(ev,un,ob,pane)
27737     {
27738         //Roo.log('tab - prev default');
27739         ev.preventDefault();
27740         
27741         
27742         this.el.select('.nav-tabs li.nav-tab', true).removeClass('active');
27743         pane.tab.addClass('active');
27744         //Roo.log(pane.title);
27745         this.getChildContainer().select('.tab-pane',true).removeClass('active');
27746         // technically we should have a deactivate event.. but maybe add later.
27747         // and it should not de-activate the selected tab...
27748         this.fireEvent('activatepane', pane);
27749         pane.el.addClass('active');
27750         pane.fireEvent('activate');
27751         
27752         
27753     },
27754     
27755     getActivePane : function()
27756     {
27757         var r = false;
27758         Roo.each(this.panes, function(p) {
27759             if(p.el.hasClass('active')){
27760                 r = p;
27761                 return false;
27762             }
27763             
27764             return;
27765         });
27766         
27767         return r;
27768     }
27769     
27770     
27771 });
27772
27773  
27774 /*
27775  * - LGPL
27776  *
27777  * Tab pane
27778  * 
27779  */
27780 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
27781 /**
27782  * @class Roo.bootstrap.TabPane
27783  * @extends Roo.bootstrap.Component
27784  * Bootstrap TabPane class
27785  * @cfg {Boolean} active (false | true) Default false
27786  * @cfg {String} title title of panel
27787
27788  * 
27789  * @constructor
27790  * Create a new TabPane
27791  * @param {Object} config The config object
27792  */
27793
27794 Roo.bootstrap.dash.TabPane = function(config){
27795     Roo.bootstrap.dash.TabPane.superclass.constructor.call(this, config);
27796     
27797     this.addEvents({
27798         // raw events
27799         /**
27800          * @event activate
27801          * When a pane is activated
27802          * @param {Roo.bootstrap.dash.TabPane} pane
27803          */
27804         "activate" : true
27805          
27806     });
27807 };
27808
27809 Roo.extend(Roo.bootstrap.dash.TabPane, Roo.bootstrap.Component,  {
27810     
27811     active : false,
27812     title : '',
27813     
27814     // the tabBox that this is attached to.
27815     tab : false,
27816      
27817     getAutoCreate : function() 
27818     {
27819         var cfg = {
27820             tag: 'div',
27821             cls: 'tab-pane'
27822         };
27823         
27824         if(this.active){
27825             cfg.cls += ' active';
27826         }
27827         
27828         return cfg;
27829     },
27830     initEvents  : function()
27831     {
27832         //Roo.log('trigger add pane handler');
27833         this.parent().fireEvent('addpane', this)
27834     },
27835     
27836      /**
27837      * Updates the tab title 
27838      * @param {String} html to set the title to.
27839      */
27840     setTitle: function(str)
27841     {
27842         if (!this.tab) {
27843             return;
27844         }
27845         this.title = str;
27846         this.tab.select('a', true).first().dom.innerHTML = str;
27847         
27848     }
27849     
27850     
27851     
27852 });
27853
27854  
27855
27856
27857  /*
27858  * - LGPL
27859  *
27860  * menu
27861  * 
27862  */
27863 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
27864
27865 /**
27866  * @class Roo.bootstrap.menu.Menu
27867  * @extends Roo.bootstrap.Component
27868  * Bootstrap Menu class - container for Menu
27869  * @cfg {String} html Text of the menu
27870  * @cfg {String} weight (default | primary | success | info | warning | danger | inverse)
27871  * @cfg {String} icon Font awesome icon
27872  * @cfg {String} pos Menu align to (top | bottom) default bottom
27873  * 
27874  * 
27875  * @constructor
27876  * Create a new Menu
27877  * @param {Object} config The config object
27878  */
27879
27880
27881 Roo.bootstrap.menu.Menu = function(config){
27882     Roo.bootstrap.menu.Menu.superclass.constructor.call(this, config);
27883     
27884     this.addEvents({
27885         /**
27886          * @event beforeshow
27887          * Fires before this menu is displayed
27888          * @param {Roo.bootstrap.menu.Menu} this
27889          */
27890         beforeshow : true,
27891         /**
27892          * @event beforehide
27893          * Fires before this menu is hidden
27894          * @param {Roo.bootstrap.menu.Menu} this
27895          */
27896         beforehide : true,
27897         /**
27898          * @event show
27899          * Fires after this menu is displayed
27900          * @param {Roo.bootstrap.menu.Menu} this
27901          */
27902         show : true,
27903         /**
27904          * @event hide
27905          * Fires after this menu is hidden
27906          * @param {Roo.bootstrap.menu.Menu} this
27907          */
27908         hide : true,
27909         /**
27910          * @event click
27911          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
27912          * @param {Roo.bootstrap.menu.Menu} this
27913          * @param {Roo.EventObject} e
27914          */
27915         click : true
27916     });
27917     
27918 };
27919
27920 Roo.extend(Roo.bootstrap.menu.Menu, Roo.bootstrap.Component,  {
27921     
27922     submenu : false,
27923     html : '',
27924     weight : 'default',
27925     icon : false,
27926     pos : 'bottom',
27927     
27928     
27929     getChildContainer : function() {
27930         if(this.isSubMenu){
27931             return this.el;
27932         }
27933         
27934         return this.el.select('ul.dropdown-menu', true).first();  
27935     },
27936     
27937     getAutoCreate : function()
27938     {
27939         var text = [
27940             {
27941                 tag : 'span',
27942                 cls : 'roo-menu-text',
27943                 html : this.html
27944             }
27945         ];
27946         
27947         if(this.icon){
27948             text.unshift({
27949                 tag : 'i',
27950                 cls : 'fa ' + this.icon
27951             })
27952         }
27953         
27954         
27955         var cfg = {
27956             tag : 'div',
27957             cls : 'btn-group',
27958             cn : [
27959                 {
27960                     tag : 'button',
27961                     cls : 'dropdown-button btn btn-' + this.weight,
27962                     cn : text
27963                 },
27964                 {
27965                     tag : 'button',
27966                     cls : 'dropdown-toggle btn btn-' + this.weight,
27967                     cn : [
27968                         {
27969                             tag : 'span',
27970                             cls : 'caret'
27971                         }
27972                     ]
27973                 },
27974                 {
27975                     tag : 'ul',
27976                     cls : 'dropdown-menu'
27977                 }
27978             ]
27979             
27980         };
27981         
27982         if(this.pos == 'top'){
27983             cfg.cls += ' dropup';
27984         }
27985         
27986         if(this.isSubMenu){
27987             cfg = {
27988                 tag : 'ul',
27989                 cls : 'dropdown-menu'
27990             }
27991         }
27992         
27993         return cfg;
27994     },
27995     
27996     onRender : function(ct, position)
27997     {
27998         this.isSubMenu = ct.hasClass('dropdown-submenu');
27999         
28000         Roo.bootstrap.menu.Menu.superclass.onRender.call(this, ct, position);
28001     },
28002     
28003     initEvents : function() 
28004     {
28005         if(this.isSubMenu){
28006             return;
28007         }
28008         
28009         this.hidden = true;
28010         
28011         this.triggerEl = this.el.select('button.dropdown-toggle', true).first();
28012         this.triggerEl.on('click', this.onTriggerPress, this);
28013         
28014         this.buttonEl = this.el.select('button.dropdown-button', true).first();
28015         this.buttonEl.on('click', this.onClick, this);
28016         
28017     },
28018     
28019     list : function()
28020     {
28021         if(this.isSubMenu){
28022             return this.el;
28023         }
28024         
28025         return this.el.select('ul.dropdown-menu', true).first();
28026     },
28027     
28028     onClick : function(e)
28029     {
28030         this.fireEvent("click", this, e);
28031     },
28032     
28033     onTriggerPress  : function(e)
28034     {   
28035         if (this.isVisible()) {
28036             this.hide();
28037         } else {
28038             this.show();
28039         }
28040     },
28041     
28042     isVisible : function(){
28043         return !this.hidden;
28044     },
28045     
28046     show : function()
28047     {
28048         this.fireEvent("beforeshow", this);
28049         
28050         this.hidden = false;
28051         this.el.addClass('open');
28052         
28053         Roo.get(document).on("mouseup", this.onMouseUp, this);
28054         
28055         this.fireEvent("show", this);
28056         
28057         
28058     },
28059     
28060     hide : function()
28061     {
28062         this.fireEvent("beforehide", this);
28063         
28064         this.hidden = true;
28065         this.el.removeClass('open');
28066         
28067         Roo.get(document).un("mouseup", this.onMouseUp);
28068         
28069         this.fireEvent("hide", this);
28070     },
28071     
28072     onMouseUp : function()
28073     {
28074         this.hide();
28075     }
28076     
28077 });
28078
28079  
28080  /*
28081  * - LGPL
28082  *
28083  * menu item
28084  * 
28085  */
28086 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
28087
28088 /**
28089  * @class Roo.bootstrap.menu.Item
28090  * @extends Roo.bootstrap.Component
28091  * Bootstrap MenuItem class
28092  * @cfg {Boolean} submenu (true | false) default false
28093  * @cfg {String} html text of the item
28094  * @cfg {String} href the link
28095  * @cfg {Boolean} disable (true | false) default false
28096  * @cfg {Boolean} preventDefault (true | false) default true
28097  * @cfg {String} icon Font awesome icon
28098  * @cfg {String} pos Submenu align to (left | right) default right 
28099  * 
28100  * 
28101  * @constructor
28102  * Create a new Item
28103  * @param {Object} config The config object
28104  */
28105
28106
28107 Roo.bootstrap.menu.Item = function(config){
28108     Roo.bootstrap.menu.Item.superclass.constructor.call(this, config);
28109     this.addEvents({
28110         /**
28111          * @event mouseover
28112          * Fires when the mouse is hovering over this menu
28113          * @param {Roo.bootstrap.menu.Item} this
28114          * @param {Roo.EventObject} e
28115          */
28116         mouseover : true,
28117         /**
28118          * @event mouseout
28119          * Fires when the mouse exits this menu
28120          * @param {Roo.bootstrap.menu.Item} this
28121          * @param {Roo.EventObject} e
28122          */
28123         mouseout : true,
28124         // raw events
28125         /**
28126          * @event click
28127          * The raw click event for the entire grid.
28128          * @param {Roo.EventObject} e
28129          */
28130         click : true
28131     });
28132 };
28133
28134 Roo.extend(Roo.bootstrap.menu.Item, Roo.bootstrap.Component,  {
28135     
28136     submenu : false,
28137     href : '',
28138     html : '',
28139     preventDefault: true,
28140     disable : false,
28141     icon : false,
28142     pos : 'right',
28143     
28144     getAutoCreate : function()
28145     {
28146         var text = [
28147             {
28148                 tag : 'span',
28149                 cls : 'roo-menu-item-text',
28150                 html : this.html
28151             }
28152         ];
28153         
28154         if(this.icon){
28155             text.unshift({
28156                 tag : 'i',
28157                 cls : 'fa ' + this.icon
28158             })
28159         }
28160         
28161         var cfg = {
28162             tag : 'li',
28163             cn : [
28164                 {
28165                     tag : 'a',
28166                     href : this.href || '#',
28167                     cn : text
28168                 }
28169             ]
28170         };
28171         
28172         if(this.disable){
28173             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'disabled' : (cfg.cls + ' disabled');
28174         }
28175         
28176         if(this.submenu){
28177             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'dropdown-submenu' : (cfg.cls + ' dropdown-submenu');
28178             
28179             if(this.pos == 'left'){
28180                 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'pull-left' : (cfg.cls + ' pull-left');
28181             }
28182         }
28183         
28184         return cfg;
28185     },
28186     
28187     initEvents : function() 
28188     {
28189         this.el.on('mouseover', this.onMouseOver, this);
28190         this.el.on('mouseout', this.onMouseOut, this);
28191         
28192         this.el.select('a', true).first().on('click', this.onClick, this);
28193         
28194     },
28195     
28196     onClick : function(e)
28197     {
28198         if(this.preventDefault){
28199             e.preventDefault();
28200         }
28201         
28202         this.fireEvent("click", this, e);
28203     },
28204     
28205     onMouseOver : function(e)
28206     {
28207         if(this.submenu && this.pos == 'left'){
28208             this.el.select('ul.dropdown-menu', true).first().setLeft(this.el.select('ul.dropdown-menu', true).first().getWidth() * -1);
28209         }
28210         
28211         this.fireEvent("mouseover", this, e);
28212     },
28213     
28214     onMouseOut : function(e)
28215     {
28216         this.fireEvent("mouseout", this, e);
28217     }
28218 });
28219
28220  
28221
28222  /*
28223  * - LGPL
28224  *
28225  * menu separator
28226  * 
28227  */
28228 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
28229
28230 /**
28231  * @class Roo.bootstrap.menu.Separator
28232  * @extends Roo.bootstrap.Component
28233  * Bootstrap Separator class
28234  * 
28235  * @constructor
28236  * Create a new Separator
28237  * @param {Object} config The config object
28238  */
28239
28240
28241 Roo.bootstrap.menu.Separator = function(config){
28242     Roo.bootstrap.menu.Separator.superclass.constructor.call(this, config);
28243 };
28244
28245 Roo.extend(Roo.bootstrap.menu.Separator, Roo.bootstrap.Component,  {
28246     
28247     getAutoCreate : function(){
28248         var cfg = {
28249             tag : 'li',
28250             cls: 'divider'
28251         };
28252         
28253         return cfg;
28254     }
28255    
28256 });
28257
28258  
28259
28260  /*
28261  * - LGPL
28262  *
28263  * Tooltip
28264  * 
28265  */
28266
28267 /**
28268  * @class Roo.bootstrap.Tooltip
28269  * Bootstrap Tooltip class
28270  * This is basic at present - all componets support it by default, however they should add tooltipEl() method
28271  * to determine which dom element triggers the tooltip.
28272  * 
28273  * It needs to add support for additional attributes like tooltip-position
28274  * 
28275  * @constructor
28276  * Create a new Toolti
28277  * @param {Object} config The config object
28278  */
28279
28280 Roo.bootstrap.Tooltip = function(config){
28281     Roo.bootstrap.Tooltip.superclass.constructor.call(this, config);
28282     
28283     this.alignment = Roo.bootstrap.Tooltip.alignment;
28284     
28285     if(typeof(config) != 'undefined' && typeof(config.alignment) != 'undefined'){
28286         this.alignment = config.alignment;
28287     }
28288     
28289 };
28290
28291 Roo.apply(Roo.bootstrap.Tooltip, {
28292     /**
28293      * @function init initialize tooltip monitoring.
28294      * @static
28295      */
28296     currentEl : false,
28297     currentTip : false,
28298     currentRegion : false,
28299     
28300     //  init : delay?
28301     
28302     init : function()
28303     {
28304         Roo.get(document).on('mouseover', this.enter ,this);
28305         Roo.get(document).on('mouseout', this.leave, this);
28306          
28307         
28308         this.currentTip = new Roo.bootstrap.Tooltip();
28309     },
28310     
28311     enter : function(ev)
28312     {
28313         var dom = ev.getTarget();
28314         
28315         //Roo.log(['enter',dom]);
28316         var el = Roo.fly(dom);
28317         if (this.currentEl) {
28318             //Roo.log(dom);
28319             //Roo.log(this.currentEl);
28320             //Roo.log(this.currentEl.contains(dom));
28321             if (this.currentEl == el) {
28322                 return;
28323             }
28324             if (dom != this.currentEl.dom && this.currentEl.contains(dom)) {
28325                 return;
28326             }
28327
28328         }
28329         
28330         if (this.currentTip.el) {
28331             this.currentTip.el.setVisibilityMode(Roo.Element.DISPLAY).hide(); // force hiding...
28332         }    
28333         //Roo.log(ev);
28334         
28335         if(!el || el.dom == document){
28336             return;
28337         }
28338         
28339         var bindEl = el;
28340         
28341         // you can not look for children, as if el is the body.. then everythign is the child..
28342         if (!el.attr('tooltip')) { //
28343             if (!el.select("[tooltip]").elements.length) {
28344                 return;
28345             }
28346             // is the mouse over this child...?
28347             bindEl = el.select("[tooltip]").first();
28348             var xy = ev.getXY();
28349             if (!bindEl.getRegion().contains( { top : xy[1] ,right : xy[0] , bottom : xy[1], left : xy[0]})) {
28350                 //Roo.log("not in region.");
28351                 return;
28352             }
28353             //Roo.log("child element over..");
28354             
28355         }
28356         this.currentEl = bindEl;
28357         this.currentTip.bind(bindEl);
28358         this.currentRegion = Roo.lib.Region.getRegion(dom);
28359         this.currentTip.enter();
28360         
28361     },
28362     leave : function(ev)
28363     {
28364         var dom = ev.getTarget();
28365         //Roo.log(['leave',dom]);
28366         if (!this.currentEl) {
28367             return;
28368         }
28369         
28370         
28371         if (dom != this.currentEl.dom) {
28372             return;
28373         }
28374         var xy = ev.getXY();
28375         if (this.currentRegion.contains( new Roo.lib.Region( xy[1], xy[0] ,xy[1], xy[0]  ))) {
28376             return;
28377         }
28378         // only activate leave if mouse cursor is outside... bounding box..
28379         
28380         
28381         
28382         
28383         if (this.currentTip) {
28384             this.currentTip.leave();
28385         }
28386         //Roo.log('clear currentEl');
28387         this.currentEl = false;
28388         
28389         
28390     },
28391     alignment : {
28392         'left' : ['r-l', [-2,0], 'right'],
28393         'right' : ['l-r', [2,0], 'left'],
28394         'bottom' : ['t-b', [0,2], 'top'],
28395         'top' : [ 'b-t', [0,-2], 'bottom']
28396     }
28397     
28398 });
28399
28400
28401 Roo.extend(Roo.bootstrap.Tooltip, Roo.bootstrap.Component,  {
28402     
28403     
28404     bindEl : false,
28405     
28406     delay : null, // can be { show : 300 , hide: 500}
28407     
28408     timeout : null,
28409     
28410     hoverState : null, //???
28411     
28412     placement : 'bottom', 
28413     
28414     alignment : false,
28415     
28416     getAutoCreate : function(){
28417     
28418         var cfg = {
28419            cls : 'tooltip',   
28420            role : 'tooltip',
28421            cn : [
28422                 {
28423                     cls : 'tooltip-arrow arrow'
28424                 },
28425                 {
28426                     cls : 'tooltip-inner'
28427                 }
28428            ]
28429         };
28430         
28431         return cfg;
28432     },
28433     bind : function(el)
28434     {
28435         this.bindEl = el;
28436     },
28437     
28438     initEvents : function()
28439     {
28440         this.arrowEl = this.el.select('.arrow', true).first();
28441         this.innerEl = this.el.select('.tooltip-inner', true).first();
28442     },
28443     
28444     enter : function () {
28445        
28446         if (this.timeout != null) {
28447             clearTimeout(this.timeout);
28448         }
28449         
28450         this.hoverState = 'in';
28451          //Roo.log("enter - show");
28452         if (!this.delay || !this.delay.show) {
28453             this.show();
28454             return;
28455         }
28456         var _t = this;
28457         this.timeout = setTimeout(function () {
28458             if (_t.hoverState == 'in') {
28459                 _t.show();
28460             }
28461         }, this.delay.show);
28462     },
28463     leave : function()
28464     {
28465         clearTimeout(this.timeout);
28466     
28467         this.hoverState = 'out';
28468          if (!this.delay || !this.delay.hide) {
28469             this.hide();
28470             return;
28471         }
28472        
28473         var _t = this;
28474         this.timeout = setTimeout(function () {
28475             //Roo.log("leave - timeout");
28476             
28477             if (_t.hoverState == 'out') {
28478                 _t.hide();
28479                 Roo.bootstrap.Tooltip.currentEl = false;
28480             }
28481         }, delay);
28482     },
28483     
28484     show : function (msg)
28485     {
28486         if (!this.el) {
28487             this.render(document.body);
28488         }
28489         // set content.
28490         //Roo.log([this.bindEl, this.bindEl.attr('tooltip')]);
28491         
28492         var tip = msg || this.bindEl.attr('tooltip') || this.bindEl.select("[tooltip]").first().attr('tooltip');
28493         
28494         this.el.select('.tooltip-inner',true).first().dom.innerHTML = tip;
28495         
28496         this.el.removeClass(['fade','top','bottom', 'left', 'right','in',
28497                              'bs-tooltip-top','bs-tooltip-bottom', 'bs-tooltip-left', 'bs-tooltip-right']);
28498         
28499         var placement = typeof this.placement == 'function' ?
28500             this.placement.call(this, this.el, on_el) :
28501             this.placement;
28502             
28503         var autoToken = /\s?auto?\s?/i;
28504         var autoPlace = autoToken.test(placement);
28505         if (autoPlace) {
28506             placement = placement.replace(autoToken, '') || 'top';
28507         }
28508         
28509         //this.el.detach()
28510         //this.el.setXY([0,0]);
28511         this.el.show();
28512         //this.el.dom.style.display='block';
28513         
28514         //this.el.appendTo(on_el);
28515         
28516         var p = this.getPosition();
28517         var box = this.el.getBox();
28518         
28519         if (autoPlace) {
28520             // fixme..
28521         }
28522         
28523         var align = this.alignment[placement];
28524         
28525         var xy = this.el.getAlignToXY(this.bindEl, align[0], align[1]);
28526         
28527         if(placement == 'top' || placement == 'bottom'){
28528             if(xy[0] < 0){
28529                 placement = 'right';
28530             }
28531             
28532             if(xy[0] + this.el.getWidth() > Roo.lib.Dom.getViewWidth()){
28533                 placement = 'left';
28534             }
28535             
28536             var scroll = Roo.select('body', true).first().getScroll();
28537             
28538             if(xy[1] > Roo.lib.Dom.getViewHeight() + scroll.top - this.el.getHeight()){
28539                 placement = 'top';
28540             }
28541             
28542             align = this.alignment[placement];
28543             
28544             this.arrowEl.setLeft((this.innerEl.getWidth()/2) - 5);
28545             
28546         }
28547         
28548         this.el.alignTo(this.bindEl, align[0],align[1]);
28549         //var arrow = this.el.select('.arrow',true).first();
28550         //arrow.set(align[2], 
28551         
28552         this.el.addClass(placement);
28553         this.el.addClass("bs-tooltip-"+ placement);
28554         
28555         this.el.addClass('in fade show');
28556         
28557         this.hoverState = null;
28558         
28559         if (this.el.hasClass('fade')) {
28560             // fade it?
28561         }
28562         
28563         
28564         
28565         
28566         
28567     },
28568     hide : function()
28569     {
28570          
28571         if (!this.el) {
28572             return;
28573         }
28574         //this.el.setXY([0,0]);
28575         this.el.removeClass(['show', 'in']);
28576         //this.el.hide();
28577         
28578     }
28579     
28580 });
28581  
28582
28583  /*
28584  * - LGPL
28585  *
28586  * Location Picker
28587  * 
28588  */
28589
28590 /**
28591  * @class Roo.bootstrap.LocationPicker
28592  * @extends Roo.bootstrap.Component
28593  * Bootstrap LocationPicker class
28594  * @cfg {Number} latitude Position when init default 0
28595  * @cfg {Number} longitude Position when init default 0
28596  * @cfg {Number} zoom default 15
28597  * @cfg {String} mapTypeId default google.maps.MapTypeId.ROADMAP
28598  * @cfg {Boolean} mapTypeControl default false
28599  * @cfg {Boolean} disableDoubleClickZoom default false
28600  * @cfg {Boolean} scrollwheel default true
28601  * @cfg {Boolean} streetViewControl default false
28602  * @cfg {Number} radius default 0
28603  * @cfg {String} locationName
28604  * @cfg {Boolean} draggable default true
28605  * @cfg {Boolean} enableAutocomplete default false
28606  * @cfg {Boolean} enableReverseGeocode default true
28607  * @cfg {String} markerTitle
28608  * 
28609  * @constructor
28610  * Create a new LocationPicker
28611  * @param {Object} config The config object
28612  */
28613
28614
28615 Roo.bootstrap.LocationPicker = function(config){
28616     
28617     Roo.bootstrap.LocationPicker.superclass.constructor.call(this, config);
28618     
28619     this.addEvents({
28620         /**
28621          * @event initial
28622          * Fires when the picker initialized.
28623          * @param {Roo.bootstrap.LocationPicker} this
28624          * @param {Google Location} location
28625          */
28626         initial : true,
28627         /**
28628          * @event positionchanged
28629          * Fires when the picker position changed.
28630          * @param {Roo.bootstrap.LocationPicker} this
28631          * @param {Google Location} location
28632          */
28633         positionchanged : true,
28634         /**
28635          * @event resize
28636          * Fires when the map resize.
28637          * @param {Roo.bootstrap.LocationPicker} this
28638          */
28639         resize : true,
28640         /**
28641          * @event show
28642          * Fires when the map show.
28643          * @param {Roo.bootstrap.LocationPicker} this
28644          */
28645         show : true,
28646         /**
28647          * @event hide
28648          * Fires when the map hide.
28649          * @param {Roo.bootstrap.LocationPicker} this
28650          */
28651         hide : true,
28652         /**
28653          * @event mapClick
28654          * Fires when click the map.
28655          * @param {Roo.bootstrap.LocationPicker} this
28656          * @param {Map event} e
28657          */
28658         mapClick : true,
28659         /**
28660          * @event mapRightClick
28661          * Fires when right click the map.
28662          * @param {Roo.bootstrap.LocationPicker} this
28663          * @param {Map event} e
28664          */
28665         mapRightClick : true,
28666         /**
28667          * @event markerClick
28668          * Fires when click the marker.
28669          * @param {Roo.bootstrap.LocationPicker} this
28670          * @param {Map event} e
28671          */
28672         markerClick : true,
28673         /**
28674          * @event markerRightClick
28675          * Fires when right click the marker.
28676          * @param {Roo.bootstrap.LocationPicker} this
28677          * @param {Map event} e
28678          */
28679         markerRightClick : true,
28680         /**
28681          * @event OverlayViewDraw
28682          * Fires when OverlayView Draw
28683          * @param {Roo.bootstrap.LocationPicker} this
28684          */
28685         OverlayViewDraw : true,
28686         /**
28687          * @event OverlayViewOnAdd
28688          * Fires when OverlayView Draw
28689          * @param {Roo.bootstrap.LocationPicker} this
28690          */
28691         OverlayViewOnAdd : true,
28692         /**
28693          * @event OverlayViewOnRemove
28694          * Fires when OverlayView Draw
28695          * @param {Roo.bootstrap.LocationPicker} this
28696          */
28697         OverlayViewOnRemove : true,
28698         /**
28699          * @event OverlayViewShow
28700          * Fires when OverlayView Draw
28701          * @param {Roo.bootstrap.LocationPicker} this
28702          * @param {Pixel} cpx
28703          */
28704         OverlayViewShow : true,
28705         /**
28706          * @event OverlayViewHide
28707          * Fires when OverlayView Draw
28708          * @param {Roo.bootstrap.LocationPicker} this
28709          */
28710         OverlayViewHide : true,
28711         /**
28712          * @event loadexception
28713          * Fires when load google lib failed.
28714          * @param {Roo.bootstrap.LocationPicker} this
28715          */
28716         loadexception : true
28717     });
28718         
28719 };
28720
28721 Roo.extend(Roo.bootstrap.LocationPicker, Roo.bootstrap.Component,  {
28722     
28723     gMapContext: false,
28724     
28725     latitude: 0,
28726     longitude: 0,
28727     zoom: 15,
28728     mapTypeId: false,
28729     mapTypeControl: false,
28730     disableDoubleClickZoom: false,
28731     scrollwheel: true,
28732     streetViewControl: false,
28733     radius: 0,
28734     locationName: '',
28735     draggable: true,
28736     enableAutocomplete: false,
28737     enableReverseGeocode: true,
28738     markerTitle: '',
28739     
28740     getAutoCreate: function()
28741     {
28742
28743         var cfg = {
28744             tag: 'div',
28745             cls: 'roo-location-picker'
28746         };
28747         
28748         return cfg
28749     },
28750     
28751     initEvents: function(ct, position)
28752     {       
28753         if(!this.el.getWidth() || this.isApplied()){
28754             return;
28755         }
28756         
28757         this.el.setVisibilityMode(Roo.Element.DISPLAY);
28758         
28759         this.initial();
28760     },
28761     
28762     initial: function()
28763     {
28764         if(typeof(google) == 'undefined' || typeof(google.maps) == 'undefined'){
28765             this.fireEvent('loadexception', this);
28766             return;
28767         }
28768         
28769         if(!this.mapTypeId){
28770             this.mapTypeId = google.maps.MapTypeId.ROADMAP;
28771         }
28772         
28773         this.gMapContext = this.GMapContext();
28774         
28775         this.initOverlayView();
28776         
28777         this.OverlayView = new Roo.bootstrap.LocationPicker.OverlayView(this.gMapContext.map);
28778         
28779         var _this = this;
28780                 
28781         google.maps.event.addListener(this.gMapContext.marker, "dragend", function(event) {
28782             _this.setPosition(_this.gMapContext.marker.position);
28783         });
28784         
28785         google.maps.event.addListener(this.gMapContext.map, 'click', function(event){
28786             _this.fireEvent('mapClick', this, event);
28787             
28788         });
28789
28790         google.maps.event.addListener(this.gMapContext.map, 'rightclick', function(event){
28791             _this.fireEvent('mapRightClick', this, event);
28792             
28793         });
28794         
28795         google.maps.event.addListener(this.gMapContext.marker, 'click', function(event){
28796             _this.fireEvent('markerClick', this, event);
28797             
28798         });
28799
28800         google.maps.event.addListener(this.gMapContext.marker, 'rightclick', function(event){
28801             _this.fireEvent('markerRightClick', this, event);
28802             
28803         });
28804         
28805         this.setPosition(this.gMapContext.location);
28806         
28807         this.fireEvent('initial', this, this.gMapContext.location);
28808     },
28809     
28810     initOverlayView: function()
28811     {
28812         var _this = this;
28813         
28814         Roo.bootstrap.LocationPicker.OverlayView.prototype = Roo.apply(new google.maps.OverlayView(), {
28815             
28816             draw: function()
28817             {
28818                 _this.fireEvent('OverlayViewDraw', _this);
28819             },
28820             
28821             onAdd: function()
28822             {
28823                 _this.fireEvent('OverlayViewOnAdd', _this);
28824             },
28825             
28826             onRemove: function()
28827             {
28828                 _this.fireEvent('OverlayViewOnRemove', _this);
28829             },
28830             
28831             show: function(cpx)
28832             {
28833                 _this.fireEvent('OverlayViewShow', _this, cpx);
28834             },
28835             
28836             hide: function()
28837             {
28838                 _this.fireEvent('OverlayViewHide', _this);
28839             }
28840             
28841         });
28842     },
28843     
28844     fromLatLngToContainerPixel: function(event)
28845     {
28846         return this.OverlayView.getProjection().fromLatLngToContainerPixel(event.latLng);
28847     },
28848     
28849     isApplied: function() 
28850     {
28851         return this.getGmapContext() == false ? false : true;
28852     },
28853     
28854     getGmapContext: function() 
28855     {
28856         return (typeof(this.gMapContext) == 'undefined') ? false : this.gMapContext;
28857     },
28858     
28859     GMapContext: function() 
28860     {
28861         var position = new google.maps.LatLng(this.latitude, this.longitude);
28862         
28863         var _map = new google.maps.Map(this.el.dom, {
28864             center: position,
28865             zoom: this.zoom,
28866             mapTypeId: this.mapTypeId,
28867             mapTypeControl: this.mapTypeControl,
28868             disableDoubleClickZoom: this.disableDoubleClickZoom,
28869             scrollwheel: this.scrollwheel,
28870             streetViewControl: this.streetViewControl,
28871             locationName: this.locationName,
28872             draggable: this.draggable,
28873             enableAutocomplete: this.enableAutocomplete,
28874             enableReverseGeocode: this.enableReverseGeocode
28875         });
28876         
28877         var _marker = new google.maps.Marker({
28878             position: position,
28879             map: _map,
28880             title: this.markerTitle,
28881             draggable: this.draggable
28882         });
28883         
28884         return {
28885             map: _map,
28886             marker: _marker,
28887             circle: null,
28888             location: position,
28889             radius: this.radius,
28890             locationName: this.locationName,
28891             addressComponents: {
28892                 formatted_address: null,
28893                 addressLine1: null,
28894                 addressLine2: null,
28895                 streetName: null,
28896                 streetNumber: null,
28897                 city: null,
28898                 district: null,
28899                 state: null,
28900                 stateOrProvince: null
28901             },
28902             settings: this,
28903             domContainer: this.el.dom,
28904             geodecoder: new google.maps.Geocoder()
28905         };
28906     },
28907     
28908     drawCircle: function(center, radius, options) 
28909     {
28910         if (this.gMapContext.circle != null) {
28911             this.gMapContext.circle.setMap(null);
28912         }
28913         if (radius > 0) {
28914             radius *= 1;
28915             options = Roo.apply({}, options, {
28916                 strokeColor: "#0000FF",
28917                 strokeOpacity: .35,
28918                 strokeWeight: 2,
28919                 fillColor: "#0000FF",
28920                 fillOpacity: .2
28921             });
28922             
28923             options.map = this.gMapContext.map;
28924             options.radius = radius;
28925             options.center = center;
28926             this.gMapContext.circle = new google.maps.Circle(options);
28927             return this.gMapContext.circle;
28928         }
28929         
28930         return null;
28931     },
28932     
28933     setPosition: function(location) 
28934     {
28935         this.gMapContext.location = location;
28936         this.gMapContext.marker.setPosition(location);
28937         this.gMapContext.map.panTo(location);
28938         this.drawCircle(location, this.gMapContext.radius, {});
28939         
28940         var _this = this;
28941         
28942         if (this.gMapContext.settings.enableReverseGeocode) {
28943             this.gMapContext.geodecoder.geocode({
28944                 latLng: this.gMapContext.location
28945             }, function(results, status) {
28946                 
28947                 if (status == google.maps.GeocoderStatus.OK && results.length > 0) {
28948                     _this.gMapContext.locationName = results[0].formatted_address;
28949                     _this.gMapContext.addressComponents = _this.address_component_from_google_geocode(results[0].address_components);
28950                     
28951                     _this.fireEvent('positionchanged', this, location);
28952                 }
28953             });
28954             
28955             return;
28956         }
28957         
28958         this.fireEvent('positionchanged', this, location);
28959     },
28960     
28961     resize: function()
28962     {
28963         google.maps.event.trigger(this.gMapContext.map, "resize");
28964         
28965         this.gMapContext.map.setCenter(this.gMapContext.marker.position);
28966         
28967         this.fireEvent('resize', this);
28968     },
28969     
28970     setPositionByLatLng: function(latitude, longitude)
28971     {
28972         this.setPosition(new google.maps.LatLng(latitude, longitude));
28973     },
28974     
28975     getCurrentPosition: function() 
28976     {
28977         return {
28978             latitude: this.gMapContext.location.lat(),
28979             longitude: this.gMapContext.location.lng()
28980         };
28981     },
28982     
28983     getAddressName: function() 
28984     {
28985         return this.gMapContext.locationName;
28986     },
28987     
28988     getAddressComponents: function() 
28989     {
28990         return this.gMapContext.addressComponents;
28991     },
28992     
28993     address_component_from_google_geocode: function(address_components) 
28994     {
28995         var result = {};
28996         
28997         for (var i = 0; i < address_components.length; i++) {
28998             var component = address_components[i];
28999             if (component.types.indexOf("postal_code") >= 0) {
29000                 result.postalCode = component.short_name;
29001             } else if (component.types.indexOf("street_number") >= 0) {
29002                 result.streetNumber = component.short_name;
29003             } else if (component.types.indexOf("route") >= 0) {
29004                 result.streetName = component.short_name;
29005             } else if (component.types.indexOf("neighborhood") >= 0) {
29006                 result.city = component.short_name;
29007             } else if (component.types.indexOf("locality") >= 0) {
29008                 result.city = component.short_name;
29009             } else if (component.types.indexOf("sublocality") >= 0) {
29010                 result.district = component.short_name;
29011             } else if (component.types.indexOf("administrative_area_level_1") >= 0) {
29012                 result.stateOrProvince = component.short_name;
29013             } else if (component.types.indexOf("country") >= 0) {
29014                 result.country = component.short_name;
29015             }
29016         }
29017         
29018         result.addressLine1 = [ result.streetNumber, result.streetName ].join(" ").trim();
29019         result.addressLine2 = "";
29020         return result;
29021     },
29022     
29023     setZoomLevel: function(zoom)
29024     {
29025         this.gMapContext.map.setZoom(zoom);
29026     },
29027     
29028     show: function()
29029     {
29030         if(!this.el){
29031             return;
29032         }
29033         
29034         this.el.show();
29035         
29036         this.resize();
29037         
29038         this.fireEvent('show', this);
29039     },
29040     
29041     hide: function()
29042     {
29043         if(!this.el){
29044             return;
29045         }
29046         
29047         this.el.hide();
29048         
29049         this.fireEvent('hide', this);
29050     }
29051     
29052 });
29053
29054 Roo.apply(Roo.bootstrap.LocationPicker, {
29055     
29056     OverlayView : function(map, options)
29057     {
29058         options = options || {};
29059         
29060         this.setMap(map);
29061     }
29062     
29063     
29064 });/**
29065  * @class Roo.bootstrap.Alert
29066  * @extends Roo.bootstrap.Component
29067  * Bootstrap Alert class - shows an alert area box
29068  * eg
29069  * <div class="alert alert-danger" role="alert"><span class="fa fa-exclamation-triangle"></span><span class="sr-only">Error:</span>
29070   Enter a valid email address
29071 </div>
29072  * @licence LGPL
29073  * @cfg {String} title The title of alert
29074  * @cfg {String} html The content of alert
29075  * @cfg {String} weight (  success | info | warning | danger )
29076  * @cfg {String} faicon font-awesomeicon
29077  * 
29078  * @constructor
29079  * Create a new alert
29080  * @param {Object} config The config object
29081  */
29082
29083
29084 Roo.bootstrap.Alert = function(config){
29085     Roo.bootstrap.Alert.superclass.constructor.call(this, config);
29086     
29087 };
29088
29089 Roo.extend(Roo.bootstrap.Alert, Roo.bootstrap.Component,  {
29090     
29091     title: '',
29092     html: '',
29093     weight: false,
29094     faicon: false,
29095     
29096     getAutoCreate : function()
29097     {
29098         
29099         var cfg = {
29100             tag : 'div',
29101             cls : 'alert',
29102             cn : [
29103                 {
29104                     tag : 'i',
29105                     cls : 'roo-alert-icon'
29106                     
29107                 },
29108                 {
29109                     tag : 'b',
29110                     cls : 'roo-alert-title',
29111                     html : this.title
29112                 },
29113                 {
29114                     tag : 'span',
29115                     cls : 'roo-alert-text',
29116                     html : this.html
29117                 }
29118             ]
29119         };
29120         
29121         if(this.faicon){
29122             cfg.cn[0].cls += ' fa ' + this.faicon;
29123         }
29124         
29125         if(this.weight){
29126             cfg.cls += ' alert-' + this.weight;
29127         }
29128         
29129         return cfg;
29130     },
29131     
29132     initEvents: function() 
29133     {
29134         this.el.setVisibilityMode(Roo.Element.DISPLAY);
29135     },
29136     
29137     setTitle : function(str)
29138     {
29139         this.el.select('.roo-alert-title',true).first().dom.innerHTML = str;
29140     },
29141     
29142     setText : function(str)
29143     {
29144         this.el.select('.roo-alert-text',true).first().dom.innerHTML = str;
29145     },
29146     
29147     setWeight : function(weight)
29148     {
29149         if(this.weight){
29150             this.el.select('.alert',true).first().removeClass('alert-' + this.weight);
29151         }
29152         
29153         this.weight = weight;
29154         
29155         this.el.select('.alert',true).first().addClass('alert-' + this.weight);
29156     },
29157     
29158     setIcon : function(icon)
29159     {
29160         if(this.faicon){
29161             this.el.select('.roo-alert-icon',true).first().removeClass(['fa', 'fa-' + this.faicon]);
29162         }
29163         
29164         this.faicon = icon;
29165         
29166         this.el.select('.roo-alert-icon',true).first().addClass(['fa', 'fa-' + this.faicon]);
29167     },
29168     
29169     hide: function() 
29170     {
29171         this.el.hide();   
29172     },
29173     
29174     show: function() 
29175     {  
29176         this.el.show();   
29177     }
29178     
29179 });
29180
29181  
29182 /*
29183 * Licence: LGPL
29184 */
29185
29186 /**
29187  * @class Roo.bootstrap.UploadCropbox
29188  * @extends Roo.bootstrap.Component
29189  * Bootstrap UploadCropbox class
29190  * @cfg {String} emptyText show when image has been loaded
29191  * @cfg {String} rotateNotify show when image too small to rotate
29192  * @cfg {Number} errorTimeout default 3000
29193  * @cfg {Number} minWidth default 300
29194  * @cfg {Number} minHeight default 300
29195  * @cfg {Array} buttons default ['rotateLeft', 'pictureBtn', 'rotateRight']
29196  * @cfg {Boolean} isDocument (true|false) default false
29197  * @cfg {String} url action url
29198  * @cfg {String} paramName default 'imageUpload'
29199  * @cfg {String} method default POST
29200  * @cfg {Boolean} loadMask (true|false) default true
29201  * @cfg {Boolean} loadingText default 'Loading...'
29202  * 
29203  * @constructor
29204  * Create a new UploadCropbox
29205  * @param {Object} config The config object
29206  */
29207
29208 Roo.bootstrap.UploadCropbox = function(config){
29209     Roo.bootstrap.UploadCropbox.superclass.constructor.call(this, config);
29210     
29211     this.addEvents({
29212         /**
29213          * @event beforeselectfile
29214          * Fire before select file
29215          * @param {Roo.bootstrap.UploadCropbox} this
29216          */
29217         "beforeselectfile" : true,
29218         /**
29219          * @event initial
29220          * Fire after initEvent
29221          * @param {Roo.bootstrap.UploadCropbox} this
29222          */
29223         "initial" : true,
29224         /**
29225          * @event crop
29226          * Fire after initEvent
29227          * @param {Roo.bootstrap.UploadCropbox} this
29228          * @param {String} data
29229          */
29230         "crop" : true,
29231         /**
29232          * @event prepare
29233          * Fire when preparing the file data
29234          * @param {Roo.bootstrap.UploadCropbox} this
29235          * @param {Object} file
29236          */
29237         "prepare" : true,
29238         /**
29239          * @event exception
29240          * Fire when get exception
29241          * @param {Roo.bootstrap.UploadCropbox} this
29242          * @param {XMLHttpRequest} xhr
29243          */
29244         "exception" : true,
29245         /**
29246          * @event beforeloadcanvas
29247          * Fire before load the canvas
29248          * @param {Roo.bootstrap.UploadCropbox} this
29249          * @param {String} src
29250          */
29251         "beforeloadcanvas" : true,
29252         /**
29253          * @event trash
29254          * Fire when trash image
29255          * @param {Roo.bootstrap.UploadCropbox} this
29256          */
29257         "trash" : true,
29258         /**
29259          * @event download
29260          * Fire when download the image
29261          * @param {Roo.bootstrap.UploadCropbox} this
29262          */
29263         "download" : true,
29264         /**
29265          * @event footerbuttonclick
29266          * Fire when footerbuttonclick
29267          * @param {Roo.bootstrap.UploadCropbox} this
29268          * @param {String} type
29269          */
29270         "footerbuttonclick" : true,
29271         /**
29272          * @event resize
29273          * Fire when resize
29274          * @param {Roo.bootstrap.UploadCropbox} this
29275          */
29276         "resize" : true,
29277         /**
29278          * @event rotate
29279          * Fire when rotate the image
29280          * @param {Roo.bootstrap.UploadCropbox} this
29281          * @param {String} pos
29282          */
29283         "rotate" : true,
29284         /**
29285          * @event inspect
29286          * Fire when inspect the file
29287          * @param {Roo.bootstrap.UploadCropbox} this
29288          * @param {Object} file
29289          */
29290         "inspect" : true,
29291         /**
29292          * @event upload
29293          * Fire when xhr upload the file
29294          * @param {Roo.bootstrap.UploadCropbox} this
29295          * @param {Object} data
29296          */
29297         "upload" : true,
29298         /**
29299          * @event arrange
29300          * Fire when arrange the file data
29301          * @param {Roo.bootstrap.UploadCropbox} this
29302          * @param {Object} formData
29303          */
29304         "arrange" : true
29305     });
29306     
29307     this.buttons = this.buttons || Roo.bootstrap.UploadCropbox.footer.STANDARD;
29308 };
29309
29310 Roo.extend(Roo.bootstrap.UploadCropbox, Roo.bootstrap.Component,  {
29311     
29312     emptyText : 'Click to upload image',
29313     rotateNotify : 'Image is too small to rotate',
29314     errorTimeout : 3000,
29315     scale : 0,
29316     baseScale : 1,
29317     rotate : 0,
29318     dragable : false,
29319     pinching : false,
29320     mouseX : 0,
29321     mouseY : 0,
29322     cropData : false,
29323     minWidth : 300,
29324     minHeight : 300,
29325     file : false,
29326     exif : {},
29327     baseRotate : 1,
29328     cropType : 'image/jpeg',
29329     buttons : false,
29330     canvasLoaded : false,
29331     isDocument : false,
29332     method : 'POST',
29333     paramName : 'imageUpload',
29334     loadMask : true,
29335     loadingText : 'Loading...',
29336     maskEl : false,
29337     
29338     getAutoCreate : function()
29339     {
29340         var cfg = {
29341             tag : 'div',
29342             cls : 'roo-upload-cropbox',
29343             cn : [
29344                 {
29345                     tag : 'input',
29346                     cls : 'roo-upload-cropbox-selector',
29347                     type : 'file'
29348                 },
29349                 {
29350                     tag : 'div',
29351                     cls : 'roo-upload-cropbox-body',
29352                     style : 'cursor:pointer',
29353                     cn : [
29354                         {
29355                             tag : 'div',
29356                             cls : 'roo-upload-cropbox-preview'
29357                         },
29358                         {
29359                             tag : 'div',
29360                             cls : 'roo-upload-cropbox-thumb'
29361                         },
29362                         {
29363                             tag : 'div',
29364                             cls : 'roo-upload-cropbox-empty-notify',
29365                             html : this.emptyText
29366                         },
29367                         {
29368                             tag : 'div',
29369                             cls : 'roo-upload-cropbox-error-notify alert alert-danger',
29370                             html : this.rotateNotify
29371                         }
29372                     ]
29373                 },
29374                 {
29375                     tag : 'div',
29376                     cls : 'roo-upload-cropbox-footer',
29377                     cn : {
29378                         tag : 'div',
29379                         cls : 'btn-group btn-group-justified roo-upload-cropbox-btn-group',
29380                         cn : []
29381                     }
29382                 }
29383             ]
29384         };
29385         
29386         return cfg;
29387     },
29388     
29389     onRender : function(ct, position)
29390     {
29391         Roo.bootstrap.UploadCropbox.superclass.onRender.call(this, ct, position);
29392         
29393         if (this.buttons.length) {
29394             
29395             Roo.each(this.buttons, function(bb) {
29396                 
29397                 var btn = this.el.select('.roo-upload-cropbox-footer div.roo-upload-cropbox-btn-group').first().createChild(bb);
29398                 
29399                 btn.on('click', this.onFooterButtonClick.createDelegate(this, [bb.action], true));
29400                 
29401             }, this);
29402         }
29403         
29404         if(this.loadMask){
29405             this.maskEl = this.el;
29406         }
29407     },
29408     
29409     initEvents : function()
29410     {
29411         this.urlAPI = (window.createObjectURL && window) || 
29412                                 (window.URL && URL.revokeObjectURL && URL) || 
29413                                 (window.webkitURL && webkitURL);
29414                         
29415         this.bodyEl = this.el.select('.roo-upload-cropbox-body', true).first();
29416         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29417         
29418         this.selectorEl = this.el.select('.roo-upload-cropbox-selector', true).first();
29419         this.selectorEl.hide();
29420         
29421         this.previewEl = this.el.select('.roo-upload-cropbox-preview', true).first();
29422         this.previewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29423         
29424         this.thumbEl = this.el.select('.roo-upload-cropbox-thumb', true).first();
29425         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29426         this.thumbEl.hide();
29427         
29428         this.notifyEl = this.el.select('.roo-upload-cropbox-empty-notify', true).first();
29429         this.notifyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29430         
29431         this.errorEl = this.el.select('.roo-upload-cropbox-error-notify', true).first();
29432         this.errorEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29433         this.errorEl.hide();
29434         
29435         this.footerEl = this.el.select('.roo-upload-cropbox-footer', true).first();
29436         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29437         this.footerEl.hide();
29438         
29439         this.setThumbBoxSize();
29440         
29441         this.bind();
29442         
29443         this.resize();
29444         
29445         this.fireEvent('initial', this);
29446     },
29447
29448     bind : function()
29449     {
29450         var _this = this;
29451         
29452         window.addEventListener("resize", function() { _this.resize(); } );
29453         
29454         this.bodyEl.on('click', this.beforeSelectFile, this);
29455         
29456         if(Roo.isTouch){
29457             this.bodyEl.on('touchstart', this.onTouchStart, this);
29458             this.bodyEl.on('touchmove', this.onTouchMove, this);
29459             this.bodyEl.on('touchend', this.onTouchEnd, this);
29460         }
29461         
29462         if(!Roo.isTouch){
29463             this.bodyEl.on('mousedown', this.onMouseDown, this);
29464             this.bodyEl.on('mousemove', this.onMouseMove, this);
29465             var mousewheel = (/Firefox/i.test(navigator.userAgent))? 'DOMMouseScroll' : 'mousewheel';
29466             this.bodyEl.on(mousewheel, this.onMouseWheel, this);
29467             Roo.get(document).on('mouseup', this.onMouseUp, this);
29468         }
29469         
29470         this.selectorEl.on('change', this.onFileSelected, this);
29471     },
29472     
29473     reset : function()
29474     {    
29475         this.scale = 0;
29476         this.baseScale = 1;
29477         this.rotate = 0;
29478         this.baseRotate = 1;
29479         this.dragable = false;
29480         this.pinching = false;
29481         this.mouseX = 0;
29482         this.mouseY = 0;
29483         this.cropData = false;
29484         this.notifyEl.dom.innerHTML = this.emptyText;
29485         
29486         this.selectorEl.dom.value = '';
29487         
29488     },
29489     
29490     resize : function()
29491     {
29492         if(this.fireEvent('resize', this) != false){
29493             this.setThumbBoxPosition();
29494             this.setCanvasPosition();
29495         }
29496     },
29497     
29498     onFooterButtonClick : function(e, el, o, type)
29499     {
29500         switch (type) {
29501             case 'rotate-left' :
29502                 this.onRotateLeft(e);
29503                 break;
29504             case 'rotate-right' :
29505                 this.onRotateRight(e);
29506                 break;
29507             case 'picture' :
29508                 this.beforeSelectFile(e);
29509                 break;
29510             case 'trash' :
29511                 this.trash(e);
29512                 break;
29513             case 'crop' :
29514                 this.crop(e);
29515                 break;
29516             case 'download' :
29517                 this.download(e);
29518                 break;
29519             default :
29520                 break;
29521         }
29522         
29523         this.fireEvent('footerbuttonclick', this, type);
29524     },
29525     
29526     beforeSelectFile : function(e)
29527     {
29528         e.preventDefault();
29529         
29530         if(this.fireEvent('beforeselectfile', this) != false){
29531             this.selectorEl.dom.click();
29532         }
29533     },
29534     
29535     onFileSelected : function(e)
29536     {
29537         e.preventDefault();
29538         
29539         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
29540             return;
29541         }
29542         
29543         var file = this.selectorEl.dom.files[0];
29544         
29545         if(this.fireEvent('inspect', this, file) != false){
29546             this.prepare(file);
29547         }
29548         
29549     },
29550     
29551     trash : function(e)
29552     {
29553         this.fireEvent('trash', this);
29554     },
29555     
29556     download : function(e)
29557     {
29558         this.fireEvent('download', this);
29559     },
29560     
29561     loadCanvas : function(src)
29562     {   
29563         if(this.fireEvent('beforeloadcanvas', this, src) != false){
29564             
29565             this.reset();
29566             
29567             this.imageEl = document.createElement('img');
29568             
29569             var _this = this;
29570             
29571             this.imageEl.addEventListener("load", function(){ _this.onLoadCanvas(); });
29572             
29573             this.imageEl.src = src;
29574         }
29575     },
29576     
29577     onLoadCanvas : function()
29578     {   
29579         this.imageEl.OriginWidth = this.imageEl.naturalWidth || this.imageEl.width;
29580         this.imageEl.OriginHeight = this.imageEl.naturalHeight || this.imageEl.height;
29581         
29582         this.bodyEl.un('click', this.beforeSelectFile, this);
29583         
29584         this.notifyEl.hide();
29585         this.thumbEl.show();
29586         this.footerEl.show();
29587         
29588         this.baseRotateLevel();
29589         
29590         if(this.isDocument){
29591             this.setThumbBoxSize();
29592         }
29593         
29594         this.setThumbBoxPosition();
29595         
29596         this.baseScaleLevel();
29597         
29598         this.draw();
29599         
29600         this.resize();
29601         
29602         this.canvasLoaded = true;
29603         
29604         if(this.loadMask){
29605             this.maskEl.unmask();
29606         }
29607         
29608     },
29609     
29610     setCanvasPosition : function()
29611     {   
29612         if(!this.canvasEl){
29613             return;
29614         }
29615         
29616         var pw = Math.ceil((this.bodyEl.getWidth() - this.canvasEl.width) / 2);
29617         var ph = Math.ceil((this.bodyEl.getHeight() - this.canvasEl.height) / 2);
29618         
29619         this.previewEl.setLeft(pw);
29620         this.previewEl.setTop(ph);
29621         
29622     },
29623     
29624     onMouseDown : function(e)
29625     {   
29626         e.stopEvent();
29627         
29628         this.dragable = true;
29629         this.pinching = false;
29630         
29631         if(this.isDocument && (this.canvasEl.width < this.thumbEl.getWidth() || this.canvasEl.height < this.thumbEl.getHeight())){
29632             this.dragable = false;
29633             return;
29634         }
29635         
29636         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
29637         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
29638         
29639     },
29640     
29641     onMouseMove : function(e)
29642     {   
29643         e.stopEvent();
29644         
29645         if(!this.canvasLoaded){
29646             return;
29647         }
29648         
29649         if (!this.dragable){
29650             return;
29651         }
29652         
29653         var minX = Math.ceil(this.thumbEl.getLeft(true));
29654         var minY = Math.ceil(this.thumbEl.getTop(true));
29655         
29656         var maxX = Math.ceil(minX + this.thumbEl.getWidth() - this.canvasEl.width);
29657         var maxY = Math.ceil(minY + this.thumbEl.getHeight() - this.canvasEl.height);
29658         
29659         var x = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
29660         var y = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
29661         
29662         x = x - this.mouseX;
29663         y = y - this.mouseY;
29664         
29665         var bgX = Math.ceil(x + this.previewEl.getLeft(true));
29666         var bgY = Math.ceil(y + this.previewEl.getTop(true));
29667         
29668         bgX = (minX < bgX) ? minX : ((maxX > bgX) ? maxX : bgX);
29669         bgY = (minY < bgY) ? minY : ((maxY > bgY) ? maxY : bgY);
29670         
29671         this.previewEl.setLeft(bgX);
29672         this.previewEl.setTop(bgY);
29673         
29674         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
29675         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
29676     },
29677     
29678     onMouseUp : function(e)
29679     {   
29680         e.stopEvent();
29681         
29682         this.dragable = false;
29683     },
29684     
29685     onMouseWheel : function(e)
29686     {   
29687         e.stopEvent();
29688         
29689         this.startScale = this.scale;
29690         
29691         this.scale = (e.getWheelDelta() == 1) ? (this.scale + 1) : (this.scale - 1);
29692         
29693         if(!this.zoomable()){
29694             this.scale = this.startScale;
29695             return;
29696         }
29697         
29698         this.draw();
29699         
29700         return;
29701     },
29702     
29703     zoomable : function()
29704     {
29705         var minScale = this.thumbEl.getWidth() / this.minWidth;
29706         
29707         if(this.minWidth < this.minHeight){
29708             minScale = this.thumbEl.getHeight() / this.minHeight;
29709         }
29710         
29711         var width = Math.ceil(this.imageEl.OriginWidth * this.getScaleLevel() / minScale);
29712         var height = Math.ceil(this.imageEl.OriginHeight * this.getScaleLevel() / minScale);
29713         
29714         if(
29715                 this.isDocument &&
29716                 (this.rotate == 0 || this.rotate == 180) && 
29717                 (
29718                     width > this.imageEl.OriginWidth || 
29719                     height > this.imageEl.OriginHeight ||
29720                     (width < this.minWidth && height < this.minHeight)
29721                 )
29722         ){
29723             return false;
29724         }
29725         
29726         if(
29727                 this.isDocument &&
29728                 (this.rotate == 90 || this.rotate == 270) && 
29729                 (
29730                     width > this.imageEl.OriginWidth || 
29731                     height > this.imageEl.OriginHeight ||
29732                     (width < this.minHeight && height < this.minWidth)
29733                 )
29734         ){
29735             return false;
29736         }
29737         
29738         if(
29739                 !this.isDocument &&
29740                 (this.rotate == 0 || this.rotate == 180) && 
29741                 (
29742                     width < this.minWidth || 
29743                     width > this.imageEl.OriginWidth || 
29744                     height < this.minHeight || 
29745                     height > this.imageEl.OriginHeight
29746                 )
29747         ){
29748             return false;
29749         }
29750         
29751         if(
29752                 !this.isDocument &&
29753                 (this.rotate == 90 || this.rotate == 270) && 
29754                 (
29755                     width < this.minHeight || 
29756                     width > this.imageEl.OriginWidth || 
29757                     height < this.minWidth || 
29758                     height > this.imageEl.OriginHeight
29759                 )
29760         ){
29761             return false;
29762         }
29763         
29764         return true;
29765         
29766     },
29767     
29768     onRotateLeft : function(e)
29769     {   
29770         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
29771             
29772             var minScale = this.thumbEl.getWidth() / this.minWidth;
29773             
29774             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
29775             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
29776             
29777             this.startScale = this.scale;
29778             
29779             while (this.getScaleLevel() < minScale){
29780             
29781                 this.scale = this.scale + 1;
29782                 
29783                 if(!this.zoomable()){
29784                     break;
29785                 }
29786                 
29787                 if(
29788                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
29789                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
29790                 ){
29791                     continue;
29792                 }
29793                 
29794                 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
29795
29796                 this.draw();
29797                 
29798                 return;
29799             }
29800             
29801             this.scale = this.startScale;
29802             
29803             this.onRotateFail();
29804             
29805             return false;
29806         }
29807         
29808         this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
29809
29810         if(this.isDocument){
29811             this.setThumbBoxSize();
29812             this.setThumbBoxPosition();
29813             this.setCanvasPosition();
29814         }
29815         
29816         this.draw();
29817         
29818         this.fireEvent('rotate', this, 'left');
29819         
29820     },
29821     
29822     onRotateRight : function(e)
29823     {
29824         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
29825             
29826             var minScale = this.thumbEl.getWidth() / this.minWidth;
29827         
29828             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
29829             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
29830             
29831             this.startScale = this.scale;
29832             
29833             while (this.getScaleLevel() < minScale){
29834             
29835                 this.scale = this.scale + 1;
29836                 
29837                 if(!this.zoomable()){
29838                     break;
29839                 }
29840                 
29841                 if(
29842                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
29843                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
29844                 ){
29845                     continue;
29846                 }
29847                 
29848                 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
29849
29850                 this.draw();
29851                 
29852                 return;
29853             }
29854             
29855             this.scale = this.startScale;
29856             
29857             this.onRotateFail();
29858             
29859             return false;
29860         }
29861         
29862         this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
29863
29864         if(this.isDocument){
29865             this.setThumbBoxSize();
29866             this.setThumbBoxPosition();
29867             this.setCanvasPosition();
29868         }
29869         
29870         this.draw();
29871         
29872         this.fireEvent('rotate', this, 'right');
29873     },
29874     
29875     onRotateFail : function()
29876     {
29877         this.errorEl.show(true);
29878         
29879         var _this = this;
29880         
29881         (function() { _this.errorEl.hide(true); }).defer(this.errorTimeout);
29882     },
29883     
29884     draw : function()
29885     {
29886         this.previewEl.dom.innerHTML = '';
29887         
29888         var canvasEl = document.createElement("canvas");
29889         
29890         var contextEl = canvasEl.getContext("2d");
29891         
29892         canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
29893         canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
29894         var center = this.imageEl.OriginWidth / 2;
29895         
29896         if(this.imageEl.OriginWidth < this.imageEl.OriginHeight){
29897             canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
29898             canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
29899             center = this.imageEl.OriginHeight / 2;
29900         }
29901         
29902         contextEl.scale(this.getScaleLevel(), this.getScaleLevel());
29903         
29904         contextEl.translate(center, center);
29905         contextEl.rotate(this.rotate * Math.PI / 180);
29906
29907         contextEl.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
29908         
29909         this.canvasEl = document.createElement("canvas");
29910         
29911         this.contextEl = this.canvasEl.getContext("2d");
29912         
29913         switch (this.rotate) {
29914             case 0 :
29915                 
29916                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
29917                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
29918                 
29919                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
29920                 
29921                 break;
29922             case 90 : 
29923                 
29924                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
29925                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
29926                 
29927                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
29928                     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);
29929                     break;
29930                 }
29931                 
29932                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
29933                 
29934                 break;
29935             case 180 :
29936                 
29937                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
29938                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
29939                 
29940                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
29941                     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);
29942                     break;
29943                 }
29944                 
29945                 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);
29946                 
29947                 break;
29948             case 270 :
29949                 
29950                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
29951                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
29952         
29953                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
29954                     this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
29955                     break;
29956                 }
29957                 
29958                 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);
29959                 
29960                 break;
29961             default : 
29962                 break;
29963         }
29964         
29965         this.previewEl.appendChild(this.canvasEl);
29966         
29967         this.setCanvasPosition();
29968     },
29969     
29970     crop : function()
29971     {
29972         if(!this.canvasLoaded){
29973             return;
29974         }
29975         
29976         var imageCanvas = document.createElement("canvas");
29977         
29978         var imageContext = imageCanvas.getContext("2d");
29979         
29980         imageCanvas.width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
29981         imageCanvas.height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
29982         
29983         var center = imageCanvas.width / 2;
29984         
29985         imageContext.translate(center, center);
29986         
29987         imageContext.rotate(this.rotate * Math.PI / 180);
29988         
29989         imageContext.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
29990         
29991         var canvas = document.createElement("canvas");
29992         
29993         var context = canvas.getContext("2d");
29994                 
29995         canvas.width = this.minWidth;
29996         canvas.height = this.minHeight;
29997
29998         switch (this.rotate) {
29999             case 0 :
30000                 
30001                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
30002                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
30003                 
30004                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
30005                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
30006                 
30007                 var targetWidth = this.minWidth - 2 * x;
30008                 var targetHeight = this.minHeight - 2 * y;
30009                 
30010                 var scale = 1;
30011                 
30012                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
30013                     scale = targetWidth / width;
30014                 }
30015                 
30016                 if(x > 0 && y == 0){
30017                     scale = targetHeight / height;
30018                 }
30019                 
30020                 if(x > 0 && y > 0){
30021                     scale = targetWidth / width;
30022                     
30023                     if(width < height){
30024                         scale = targetHeight / height;
30025                     }
30026                 }
30027                 
30028                 context.scale(scale, scale);
30029                 
30030                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
30031                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
30032
30033                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
30034                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
30035
30036                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
30037                 
30038                 break;
30039             case 90 : 
30040                 
30041                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
30042                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
30043                 
30044                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
30045                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
30046                 
30047                 var targetWidth = this.minWidth - 2 * x;
30048                 var targetHeight = this.minHeight - 2 * y;
30049                 
30050                 var scale = 1;
30051                 
30052                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
30053                     scale = targetWidth / width;
30054                 }
30055                 
30056                 if(x > 0 && y == 0){
30057                     scale = targetHeight / height;
30058                 }
30059                 
30060                 if(x > 0 && y > 0){
30061                     scale = targetWidth / width;
30062                     
30063                     if(width < height){
30064                         scale = targetHeight / height;
30065                     }
30066                 }
30067                 
30068                 context.scale(scale, scale);
30069                 
30070                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
30071                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
30072
30073                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
30074                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
30075                 
30076                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
30077                 
30078                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
30079                 
30080                 break;
30081             case 180 :
30082                 
30083                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
30084                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
30085                 
30086                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
30087                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
30088                 
30089                 var targetWidth = this.minWidth - 2 * x;
30090                 var targetHeight = this.minHeight - 2 * y;
30091                 
30092                 var scale = 1;
30093                 
30094                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
30095                     scale = targetWidth / width;
30096                 }
30097                 
30098                 if(x > 0 && y == 0){
30099                     scale = targetHeight / height;
30100                 }
30101                 
30102                 if(x > 0 && y > 0){
30103                     scale = targetWidth / width;
30104                     
30105                     if(width < height){
30106                         scale = targetHeight / height;
30107                     }
30108                 }
30109                 
30110                 context.scale(scale, scale);
30111                 
30112                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
30113                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
30114
30115                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
30116                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
30117
30118                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
30119                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
30120                 
30121                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
30122                 
30123                 break;
30124             case 270 :
30125                 
30126                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
30127                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
30128                 
30129                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
30130                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
30131                 
30132                 var targetWidth = this.minWidth - 2 * x;
30133                 var targetHeight = this.minHeight - 2 * y;
30134                 
30135                 var scale = 1;
30136                 
30137                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
30138                     scale = targetWidth / width;
30139                 }
30140                 
30141                 if(x > 0 && y == 0){
30142                     scale = targetHeight / height;
30143                 }
30144                 
30145                 if(x > 0 && y > 0){
30146                     scale = targetWidth / width;
30147                     
30148                     if(width < height){
30149                         scale = targetHeight / height;
30150                     }
30151                 }
30152                 
30153                 context.scale(scale, scale);
30154                 
30155                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
30156                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
30157
30158                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
30159                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
30160                 
30161                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
30162                 
30163                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
30164                 
30165                 break;
30166             default : 
30167                 break;
30168         }
30169         
30170         this.cropData = canvas.toDataURL(this.cropType);
30171         
30172         if(this.fireEvent('crop', this, this.cropData) !== false){
30173             this.process(this.file, this.cropData);
30174         }
30175         
30176         return;
30177         
30178     },
30179     
30180     setThumbBoxSize : function()
30181     {
30182         var width, height;
30183         
30184         if(this.isDocument && typeof(this.imageEl) != 'undefined'){
30185             width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.max(this.minWidth, this.minHeight) : Math.min(this.minWidth, this.minHeight);
30186             height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.min(this.minWidth, this.minHeight) : Math.max(this.minWidth, this.minHeight);
30187             
30188             this.minWidth = width;
30189             this.minHeight = height;
30190             
30191             if(this.rotate == 90 || this.rotate == 270){
30192                 this.minWidth = height;
30193                 this.minHeight = width;
30194             }
30195         }
30196         
30197         height = 300;
30198         width = Math.ceil(this.minWidth * height / this.minHeight);
30199         
30200         if(this.minWidth > this.minHeight){
30201             width = 300;
30202             height = Math.ceil(this.minHeight * width / this.minWidth);
30203         }
30204         
30205         this.thumbEl.setStyle({
30206             width : width + 'px',
30207             height : height + 'px'
30208         });
30209
30210         return;
30211             
30212     },
30213     
30214     setThumbBoxPosition : function()
30215     {
30216         var x = Math.ceil((this.bodyEl.getWidth() - this.thumbEl.getWidth()) / 2 );
30217         var y = Math.ceil((this.bodyEl.getHeight() - this.thumbEl.getHeight()) / 2);
30218         
30219         this.thumbEl.setLeft(x);
30220         this.thumbEl.setTop(y);
30221         
30222     },
30223     
30224     baseRotateLevel : function()
30225     {
30226         this.baseRotate = 1;
30227         
30228         if(
30229                 typeof(this.exif) != 'undefined' &&
30230                 typeof(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != 'undefined' &&
30231                 [1, 3, 6, 8].indexOf(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != -1
30232         ){
30233             this.baseRotate = this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']];
30234         }
30235         
30236         this.rotate = Roo.bootstrap.UploadCropbox['Orientation'][this.baseRotate];
30237         
30238     },
30239     
30240     baseScaleLevel : function()
30241     {
30242         var width, height;
30243         
30244         if(this.isDocument){
30245             
30246             if(this.baseRotate == 6 || this.baseRotate == 8){
30247             
30248                 height = this.thumbEl.getHeight();
30249                 this.baseScale = height / this.imageEl.OriginWidth;
30250
30251                 if(this.imageEl.OriginHeight * this.baseScale > this.thumbEl.getWidth()){
30252                     width = this.thumbEl.getWidth();
30253                     this.baseScale = width / this.imageEl.OriginHeight;
30254                 }
30255
30256                 return;
30257             }
30258
30259             height = this.thumbEl.getHeight();
30260             this.baseScale = height / this.imageEl.OriginHeight;
30261
30262             if(this.imageEl.OriginWidth * this.baseScale > this.thumbEl.getWidth()){
30263                 width = this.thumbEl.getWidth();
30264                 this.baseScale = width / this.imageEl.OriginWidth;
30265             }
30266
30267             return;
30268         }
30269         
30270         if(this.baseRotate == 6 || this.baseRotate == 8){
30271             
30272             width = this.thumbEl.getHeight();
30273             this.baseScale = width / this.imageEl.OriginHeight;
30274             
30275             if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getWidth()){
30276                 height = this.thumbEl.getWidth();
30277                 this.baseScale = height / this.imageEl.OriginHeight;
30278             }
30279             
30280             if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
30281                 height = this.thumbEl.getWidth();
30282                 this.baseScale = height / this.imageEl.OriginHeight;
30283                 
30284                 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getHeight()){
30285                     width = this.thumbEl.getHeight();
30286                     this.baseScale = width / this.imageEl.OriginWidth;
30287                 }
30288             }
30289             
30290             return;
30291         }
30292         
30293         width = this.thumbEl.getWidth();
30294         this.baseScale = width / this.imageEl.OriginWidth;
30295         
30296         if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getHeight()){
30297             height = this.thumbEl.getHeight();
30298             this.baseScale = height / this.imageEl.OriginHeight;
30299         }
30300         
30301         if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
30302             
30303             height = this.thumbEl.getHeight();
30304             this.baseScale = height / this.imageEl.OriginHeight;
30305             
30306             if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getWidth()){
30307                 width = this.thumbEl.getWidth();
30308                 this.baseScale = width / this.imageEl.OriginWidth;
30309             }
30310             
30311         }
30312         
30313         return;
30314     },
30315     
30316     getScaleLevel : function()
30317     {
30318         return this.baseScale * Math.pow(1.1, this.scale);
30319     },
30320     
30321     onTouchStart : function(e)
30322     {
30323         if(!this.canvasLoaded){
30324             this.beforeSelectFile(e);
30325             return;
30326         }
30327         
30328         var touches = e.browserEvent.touches;
30329         
30330         if(!touches){
30331             return;
30332         }
30333         
30334         if(touches.length == 1){
30335             this.onMouseDown(e);
30336             return;
30337         }
30338         
30339         if(touches.length != 2){
30340             return;
30341         }
30342         
30343         var coords = [];
30344         
30345         for(var i = 0, finger; finger = touches[i]; i++){
30346             coords.push(finger.pageX, finger.pageY);
30347         }
30348         
30349         var x = Math.pow(coords[0] - coords[2], 2);
30350         var y = Math.pow(coords[1] - coords[3], 2);
30351         
30352         this.startDistance = Math.sqrt(x + y);
30353         
30354         this.startScale = this.scale;
30355         
30356         this.pinching = true;
30357         this.dragable = false;
30358         
30359     },
30360     
30361     onTouchMove : function(e)
30362     {
30363         if(!this.pinching && !this.dragable){
30364             return;
30365         }
30366         
30367         var touches = e.browserEvent.touches;
30368         
30369         if(!touches){
30370             return;
30371         }
30372         
30373         if(this.dragable){
30374             this.onMouseMove(e);
30375             return;
30376         }
30377         
30378         var coords = [];
30379         
30380         for(var i = 0, finger; finger = touches[i]; i++){
30381             coords.push(finger.pageX, finger.pageY);
30382         }
30383         
30384         var x = Math.pow(coords[0] - coords[2], 2);
30385         var y = Math.pow(coords[1] - coords[3], 2);
30386         
30387         this.endDistance = Math.sqrt(x + y);
30388         
30389         this.scale = this.startScale + Math.floor(Math.log(this.endDistance / this.startDistance) / Math.log(1.1));
30390         
30391         if(!this.zoomable()){
30392             this.scale = this.startScale;
30393             return;
30394         }
30395         
30396         this.draw();
30397         
30398     },
30399     
30400     onTouchEnd : function(e)
30401     {
30402         this.pinching = false;
30403         this.dragable = false;
30404         
30405     },
30406     
30407     process : function(file, crop)
30408     {
30409         if(this.loadMask){
30410             this.maskEl.mask(this.loadingText);
30411         }
30412         
30413         this.xhr = new XMLHttpRequest();
30414         
30415         file.xhr = this.xhr;
30416
30417         this.xhr.open(this.method, this.url, true);
30418         
30419         var headers = {
30420             "Accept": "application/json",
30421             "Cache-Control": "no-cache",
30422             "X-Requested-With": "XMLHttpRequest"
30423         };
30424         
30425         for (var headerName in headers) {
30426             var headerValue = headers[headerName];
30427             if (headerValue) {
30428                 this.xhr.setRequestHeader(headerName, headerValue);
30429             }
30430         }
30431         
30432         var _this = this;
30433         
30434         this.xhr.onload = function()
30435         {
30436             _this.xhrOnLoad(_this.xhr);
30437         }
30438         
30439         this.xhr.onerror = function()
30440         {
30441             _this.xhrOnError(_this.xhr);
30442         }
30443         
30444         var formData = new FormData();
30445
30446         formData.append('returnHTML', 'NO');
30447         
30448         if(crop){
30449             formData.append('crop', crop);
30450         }
30451         
30452         if(typeof(file) != 'undefined' && (typeof(file.id) == 'undefined' || file.id * 1 < 1)){
30453             formData.append(this.paramName, file, file.name);
30454         }
30455         
30456         if(typeof(file.filename) != 'undefined'){
30457             formData.append('filename', file.filename);
30458         }
30459         
30460         if(typeof(file.mimetype) != 'undefined'){
30461             formData.append('mimetype', file.mimetype);
30462         }
30463         
30464         if(this.fireEvent('arrange', this, formData) != false){
30465             this.xhr.send(formData);
30466         };
30467     },
30468     
30469     xhrOnLoad : function(xhr)
30470     {
30471         if(this.loadMask){
30472             this.maskEl.unmask();
30473         }
30474         
30475         if (xhr.readyState !== 4) {
30476             this.fireEvent('exception', this, xhr);
30477             return;
30478         }
30479
30480         var response = Roo.decode(xhr.responseText);
30481         
30482         if(!response.success){
30483             this.fireEvent('exception', this, xhr);
30484             return;
30485         }
30486         
30487         var response = Roo.decode(xhr.responseText);
30488         
30489         this.fireEvent('upload', this, response);
30490         
30491     },
30492     
30493     xhrOnError : function()
30494     {
30495         if(this.loadMask){
30496             this.maskEl.unmask();
30497         }
30498         
30499         Roo.log('xhr on error');
30500         
30501         var response = Roo.decode(xhr.responseText);
30502           
30503         Roo.log(response);
30504         
30505     },
30506     
30507     prepare : function(file)
30508     {   
30509         if(this.loadMask){
30510             this.maskEl.mask(this.loadingText);
30511         }
30512         
30513         this.file = false;
30514         this.exif = {};
30515         
30516         if(typeof(file) === 'string'){
30517             this.loadCanvas(file);
30518             return;
30519         }
30520         
30521         if(!file || !this.urlAPI){
30522             return;
30523         }
30524         
30525         this.file = file;
30526         this.cropType = file.type;
30527         
30528         var _this = this;
30529         
30530         if(this.fireEvent('prepare', this, this.file) != false){
30531             
30532             var reader = new FileReader();
30533             
30534             reader.onload = function (e) {
30535                 if (e.target.error) {
30536                     Roo.log(e.target.error);
30537                     return;
30538                 }
30539                 
30540                 var buffer = e.target.result,
30541                     dataView = new DataView(buffer),
30542                     offset = 2,
30543                     maxOffset = dataView.byteLength - 4,
30544                     markerBytes,
30545                     markerLength;
30546                 
30547                 if (dataView.getUint16(0) === 0xffd8) {
30548                     while (offset < maxOffset) {
30549                         markerBytes = dataView.getUint16(offset);
30550                         
30551                         if ((markerBytes >= 0xffe0 && markerBytes <= 0xffef) || markerBytes === 0xfffe) {
30552                             markerLength = dataView.getUint16(offset + 2) + 2;
30553                             if (offset + markerLength > dataView.byteLength) {
30554                                 Roo.log('Invalid meta data: Invalid segment size.');
30555                                 break;
30556                             }
30557                             
30558                             if(markerBytes == 0xffe1){
30559                                 _this.parseExifData(
30560                                     dataView,
30561                                     offset,
30562                                     markerLength
30563                                 );
30564                             }
30565                             
30566                             offset += markerLength;
30567                             
30568                             continue;
30569                         }
30570                         
30571                         break;
30572                     }
30573                     
30574                 }
30575                 
30576                 var url = _this.urlAPI.createObjectURL(_this.file);
30577                 
30578                 _this.loadCanvas(url);
30579                 
30580                 return;
30581             }
30582             
30583             reader.readAsArrayBuffer(this.file);
30584             
30585         }
30586         
30587     },
30588     
30589     parseExifData : function(dataView, offset, length)
30590     {
30591         var tiffOffset = offset + 10,
30592             littleEndian,
30593             dirOffset;
30594     
30595         if (dataView.getUint32(offset + 4) !== 0x45786966) {
30596             // No Exif data, might be XMP data instead
30597             return;
30598         }
30599         
30600         // Check for the ASCII code for "Exif" (0x45786966):
30601         if (dataView.getUint32(offset + 4) !== 0x45786966) {
30602             // No Exif data, might be XMP data instead
30603             return;
30604         }
30605         if (tiffOffset + 8 > dataView.byteLength) {
30606             Roo.log('Invalid Exif data: Invalid segment size.');
30607             return;
30608         }
30609         // Check for the two null bytes:
30610         if (dataView.getUint16(offset + 8) !== 0x0000) {
30611             Roo.log('Invalid Exif data: Missing byte alignment offset.');
30612             return;
30613         }
30614         // Check the byte alignment:
30615         switch (dataView.getUint16(tiffOffset)) {
30616         case 0x4949:
30617             littleEndian = true;
30618             break;
30619         case 0x4D4D:
30620             littleEndian = false;
30621             break;
30622         default:
30623             Roo.log('Invalid Exif data: Invalid byte alignment marker.');
30624             return;
30625         }
30626         // Check for the TIFF tag marker (0x002A):
30627         if (dataView.getUint16(tiffOffset + 2, littleEndian) !== 0x002A) {
30628             Roo.log('Invalid Exif data: Missing TIFF marker.');
30629             return;
30630         }
30631         // Retrieve the directory offset bytes, usually 0x00000008 or 8 decimal:
30632         dirOffset = dataView.getUint32(tiffOffset + 4, littleEndian);
30633         
30634         this.parseExifTags(
30635             dataView,
30636             tiffOffset,
30637             tiffOffset + dirOffset,
30638             littleEndian
30639         );
30640     },
30641     
30642     parseExifTags : function(dataView, tiffOffset, dirOffset, littleEndian)
30643     {
30644         var tagsNumber,
30645             dirEndOffset,
30646             i;
30647         if (dirOffset + 6 > dataView.byteLength) {
30648             Roo.log('Invalid Exif data: Invalid directory offset.');
30649             return;
30650         }
30651         tagsNumber = dataView.getUint16(dirOffset, littleEndian);
30652         dirEndOffset = dirOffset + 2 + 12 * tagsNumber;
30653         if (dirEndOffset + 4 > dataView.byteLength) {
30654             Roo.log('Invalid Exif data: Invalid directory size.');
30655             return;
30656         }
30657         for (i = 0; i < tagsNumber; i += 1) {
30658             this.parseExifTag(
30659                 dataView,
30660                 tiffOffset,
30661                 dirOffset + 2 + 12 * i, // tag offset
30662                 littleEndian
30663             );
30664         }
30665         // Return the offset to the next directory:
30666         return dataView.getUint32(dirEndOffset, littleEndian);
30667     },
30668     
30669     parseExifTag : function (dataView, tiffOffset, offset, littleEndian) 
30670     {
30671         var tag = dataView.getUint16(offset, littleEndian);
30672         
30673         this.exif[tag] = this.getExifValue(
30674             dataView,
30675             tiffOffset,
30676             offset,
30677             dataView.getUint16(offset + 2, littleEndian), // tag type
30678             dataView.getUint32(offset + 4, littleEndian), // tag length
30679             littleEndian
30680         );
30681     },
30682     
30683     getExifValue : function (dataView, tiffOffset, offset, type, length, littleEndian)
30684     {
30685         var tagType = Roo.bootstrap.UploadCropbox.exifTagTypes[type],
30686             tagSize,
30687             dataOffset,
30688             values,
30689             i,
30690             str,
30691             c;
30692     
30693         if (!tagType) {
30694             Roo.log('Invalid Exif data: Invalid tag type.');
30695             return;
30696         }
30697         
30698         tagSize = tagType.size * length;
30699         // Determine if the value is contained in the dataOffset bytes,
30700         // or if the value at the dataOffset is a pointer to the actual data:
30701         dataOffset = tagSize > 4 ?
30702                 tiffOffset + dataView.getUint32(offset + 8, littleEndian) : (offset + 8);
30703         if (dataOffset + tagSize > dataView.byteLength) {
30704             Roo.log('Invalid Exif data: Invalid data offset.');
30705             return;
30706         }
30707         if (length === 1) {
30708             return tagType.getValue(dataView, dataOffset, littleEndian);
30709         }
30710         values = [];
30711         for (i = 0; i < length; i += 1) {
30712             values[i] = tagType.getValue(dataView, dataOffset + i * tagType.size, littleEndian);
30713         }
30714         
30715         if (tagType.ascii) {
30716             str = '';
30717             // Concatenate the chars:
30718             for (i = 0; i < values.length; i += 1) {
30719                 c = values[i];
30720                 // Ignore the terminating NULL byte(s):
30721                 if (c === '\u0000') {
30722                     break;
30723                 }
30724                 str += c;
30725             }
30726             return str;
30727         }
30728         return values;
30729     }
30730     
30731 });
30732
30733 Roo.apply(Roo.bootstrap.UploadCropbox, {
30734     tags : {
30735         'Orientation': 0x0112
30736     },
30737     
30738     Orientation: {
30739             1: 0, //'top-left',
30740 //            2: 'top-right',
30741             3: 180, //'bottom-right',
30742 //            4: 'bottom-left',
30743 //            5: 'left-top',
30744             6: 90, //'right-top',
30745 //            7: 'right-bottom',
30746             8: 270 //'left-bottom'
30747     },
30748     
30749     exifTagTypes : {
30750         // byte, 8-bit unsigned int:
30751         1: {
30752             getValue: function (dataView, dataOffset) {
30753                 return dataView.getUint8(dataOffset);
30754             },
30755             size: 1
30756         },
30757         // ascii, 8-bit byte:
30758         2: {
30759             getValue: function (dataView, dataOffset) {
30760                 return String.fromCharCode(dataView.getUint8(dataOffset));
30761             },
30762             size: 1,
30763             ascii: true
30764         },
30765         // short, 16 bit int:
30766         3: {
30767             getValue: function (dataView, dataOffset, littleEndian) {
30768                 return dataView.getUint16(dataOffset, littleEndian);
30769             },
30770             size: 2
30771         },
30772         // long, 32 bit int:
30773         4: {
30774             getValue: function (dataView, dataOffset, littleEndian) {
30775                 return dataView.getUint32(dataOffset, littleEndian);
30776             },
30777             size: 4
30778         },
30779         // rational = two long values, first is numerator, second is denominator:
30780         5: {
30781             getValue: function (dataView, dataOffset, littleEndian) {
30782                 return dataView.getUint32(dataOffset, littleEndian) /
30783                     dataView.getUint32(dataOffset + 4, littleEndian);
30784             },
30785             size: 8
30786         },
30787         // slong, 32 bit signed int:
30788         9: {
30789             getValue: function (dataView, dataOffset, littleEndian) {
30790                 return dataView.getInt32(dataOffset, littleEndian);
30791             },
30792             size: 4
30793         },
30794         // srational, two slongs, first is numerator, second is denominator:
30795         10: {
30796             getValue: function (dataView, dataOffset, littleEndian) {
30797                 return dataView.getInt32(dataOffset, littleEndian) /
30798                     dataView.getInt32(dataOffset + 4, littleEndian);
30799             },
30800             size: 8
30801         }
30802     },
30803     
30804     footer : {
30805         STANDARD : [
30806             {
30807                 tag : 'div',
30808                 cls : 'btn-group roo-upload-cropbox-rotate-left',
30809                 action : 'rotate-left',
30810                 cn : [
30811                     {
30812                         tag : 'button',
30813                         cls : 'btn btn-default',
30814                         html : '<i class="fa fa-undo"></i>'
30815                     }
30816                 ]
30817             },
30818             {
30819                 tag : 'div',
30820                 cls : 'btn-group roo-upload-cropbox-picture',
30821                 action : 'picture',
30822                 cn : [
30823                     {
30824                         tag : 'button',
30825                         cls : 'btn btn-default',
30826                         html : '<i class="fa fa-picture-o"></i>'
30827                     }
30828                 ]
30829             },
30830             {
30831                 tag : 'div',
30832                 cls : 'btn-group roo-upload-cropbox-rotate-right',
30833                 action : 'rotate-right',
30834                 cn : [
30835                     {
30836                         tag : 'button',
30837                         cls : 'btn btn-default',
30838                         html : '<i class="fa fa-repeat"></i>'
30839                     }
30840                 ]
30841             }
30842         ],
30843         DOCUMENT : [
30844             {
30845                 tag : 'div',
30846                 cls : 'btn-group roo-upload-cropbox-rotate-left',
30847                 action : 'rotate-left',
30848                 cn : [
30849                     {
30850                         tag : 'button',
30851                         cls : 'btn btn-default',
30852                         html : '<i class="fa fa-undo"></i>'
30853                     }
30854                 ]
30855             },
30856             {
30857                 tag : 'div',
30858                 cls : 'btn-group roo-upload-cropbox-download',
30859                 action : 'download',
30860                 cn : [
30861                     {
30862                         tag : 'button',
30863                         cls : 'btn btn-default',
30864                         html : '<i class="fa fa-download"></i>'
30865                     }
30866                 ]
30867             },
30868             {
30869                 tag : 'div',
30870                 cls : 'btn-group roo-upload-cropbox-crop',
30871                 action : 'crop',
30872                 cn : [
30873                     {
30874                         tag : 'button',
30875                         cls : 'btn btn-default',
30876                         html : '<i class="fa fa-crop"></i>'
30877                     }
30878                 ]
30879             },
30880             {
30881                 tag : 'div',
30882                 cls : 'btn-group roo-upload-cropbox-trash',
30883                 action : 'trash',
30884                 cn : [
30885                     {
30886                         tag : 'button',
30887                         cls : 'btn btn-default',
30888                         html : '<i class="fa fa-trash"></i>'
30889                     }
30890                 ]
30891             },
30892             {
30893                 tag : 'div',
30894                 cls : 'btn-group roo-upload-cropbox-rotate-right',
30895                 action : 'rotate-right',
30896                 cn : [
30897                     {
30898                         tag : 'button',
30899                         cls : 'btn btn-default',
30900                         html : '<i class="fa fa-repeat"></i>'
30901                     }
30902                 ]
30903             }
30904         ],
30905         ROTATOR : [
30906             {
30907                 tag : 'div',
30908                 cls : 'btn-group roo-upload-cropbox-rotate-left',
30909                 action : 'rotate-left',
30910                 cn : [
30911                     {
30912                         tag : 'button',
30913                         cls : 'btn btn-default',
30914                         html : '<i class="fa fa-undo"></i>'
30915                     }
30916                 ]
30917             },
30918             {
30919                 tag : 'div',
30920                 cls : 'btn-group roo-upload-cropbox-rotate-right',
30921                 action : 'rotate-right',
30922                 cn : [
30923                     {
30924                         tag : 'button',
30925                         cls : 'btn btn-default',
30926                         html : '<i class="fa fa-repeat"></i>'
30927                     }
30928                 ]
30929             }
30930         ]
30931     }
30932 });
30933
30934 /*
30935 * Licence: LGPL
30936 */
30937
30938 /**
30939  * @class Roo.bootstrap.DocumentManager
30940  * @extends Roo.bootstrap.Component
30941  * Bootstrap DocumentManager class
30942  * @cfg {String} paramName default 'imageUpload'
30943  * @cfg {String} toolTipName default 'filename'
30944  * @cfg {String} method default POST
30945  * @cfg {String} url action url
30946  * @cfg {Number} boxes number of boxes, 0 is no limit.. default 0
30947  * @cfg {Boolean} multiple multiple upload default true
30948  * @cfg {Number} thumbSize default 300
30949  * @cfg {String} fieldLabel
30950  * @cfg {Number} labelWidth default 4
30951  * @cfg {String} labelAlign (left|top) default left
30952  * @cfg {Boolean} editable (true|false) allow edit when upload a image default true
30953 * @cfg {Number} labellg set the width of label (1-12)
30954  * @cfg {Number} labelmd set the width of label (1-12)
30955  * @cfg {Number} labelsm set the width of label (1-12)
30956  * @cfg {Number} labelxs set the width of label (1-12)
30957  * 
30958  * @constructor
30959  * Create a new DocumentManager
30960  * @param {Object} config The config object
30961  */
30962
30963 Roo.bootstrap.DocumentManager = function(config){
30964     Roo.bootstrap.DocumentManager.superclass.constructor.call(this, config);
30965     
30966     this.files = [];
30967     this.delegates = [];
30968     
30969     this.addEvents({
30970         /**
30971          * @event initial
30972          * Fire when initial the DocumentManager
30973          * @param {Roo.bootstrap.DocumentManager} this
30974          */
30975         "initial" : true,
30976         /**
30977          * @event inspect
30978          * inspect selected file
30979          * @param {Roo.bootstrap.DocumentManager} this
30980          * @param {File} file
30981          */
30982         "inspect" : true,
30983         /**
30984          * @event exception
30985          * Fire when xhr load exception
30986          * @param {Roo.bootstrap.DocumentManager} this
30987          * @param {XMLHttpRequest} xhr
30988          */
30989         "exception" : true,
30990         /**
30991          * @event afterupload
30992          * Fire when xhr load exception
30993          * @param {Roo.bootstrap.DocumentManager} this
30994          * @param {XMLHttpRequest} xhr
30995          */
30996         "afterupload" : true,
30997         /**
30998          * @event prepare
30999          * prepare the form data
31000          * @param {Roo.bootstrap.DocumentManager} this
31001          * @param {Object} formData
31002          */
31003         "prepare" : true,
31004         /**
31005          * @event remove
31006          * Fire when remove the file
31007          * @param {Roo.bootstrap.DocumentManager} this
31008          * @param {Object} file
31009          */
31010         "remove" : true,
31011         /**
31012          * @event refresh
31013          * Fire after refresh the file
31014          * @param {Roo.bootstrap.DocumentManager} this
31015          */
31016         "refresh" : true,
31017         /**
31018          * @event click
31019          * Fire after click the image
31020          * @param {Roo.bootstrap.DocumentManager} this
31021          * @param {Object} file
31022          */
31023         "click" : true,
31024         /**
31025          * @event edit
31026          * Fire when upload a image and editable set to true
31027          * @param {Roo.bootstrap.DocumentManager} this
31028          * @param {Object} file
31029          */
31030         "edit" : true,
31031         /**
31032          * @event beforeselectfile
31033          * Fire before select file
31034          * @param {Roo.bootstrap.DocumentManager} this
31035          */
31036         "beforeselectfile" : true,
31037         /**
31038          * @event process
31039          * Fire before process file
31040          * @param {Roo.bootstrap.DocumentManager} this
31041          * @param {Object} file
31042          */
31043         "process" : true,
31044         /**
31045          * @event previewrendered
31046          * Fire when preview rendered
31047          * @param {Roo.bootstrap.DocumentManager} this
31048          * @param {Object} file
31049          */
31050         "previewrendered" : true,
31051         /**
31052          */
31053         "previewResize" : true
31054         
31055     });
31056 };
31057
31058 Roo.extend(Roo.bootstrap.DocumentManager, Roo.bootstrap.Component,  {
31059     
31060     boxes : 0,
31061     inputName : '',
31062     thumbSize : 300,
31063     multiple : true,
31064     files : false,
31065     method : 'POST',
31066     url : '',
31067     paramName : 'imageUpload',
31068     toolTipName : 'filename',
31069     fieldLabel : '',
31070     labelWidth : 4,
31071     labelAlign : 'left',
31072     editable : true,
31073     delegates : false,
31074     xhr : false, 
31075     
31076     labellg : 0,
31077     labelmd : 0,
31078     labelsm : 0,
31079     labelxs : 0,
31080     
31081     getAutoCreate : function()
31082     {   
31083         var managerWidget = {
31084             tag : 'div',
31085             cls : 'roo-document-manager',
31086             cn : [
31087                 {
31088                     tag : 'input',
31089                     cls : 'roo-document-manager-selector',
31090                     type : 'file'
31091                 },
31092                 {
31093                     tag : 'div',
31094                     cls : 'roo-document-manager-uploader',
31095                     cn : [
31096                         {
31097                             tag : 'div',
31098                             cls : 'roo-document-manager-upload-btn',
31099                             html : '<i class="fa fa-plus"></i>'
31100                         }
31101                     ]
31102                     
31103                 }
31104             ]
31105         };
31106         
31107         var content = [
31108             {
31109                 tag : 'div',
31110                 cls : 'column col-md-12',
31111                 cn : managerWidget
31112             }
31113         ];
31114         
31115         if(this.fieldLabel.length){
31116             
31117             content = [
31118                 {
31119                     tag : 'div',
31120                     cls : 'column col-md-12',
31121                     html : this.fieldLabel
31122                 },
31123                 {
31124                     tag : 'div',
31125                     cls : 'column col-md-12',
31126                     cn : managerWidget
31127                 }
31128             ];
31129
31130             if(this.labelAlign == 'left'){
31131                 content = [
31132                     {
31133                         tag : 'div',
31134                         cls : 'column',
31135                         html : this.fieldLabel
31136                     },
31137                     {
31138                         tag : 'div',
31139                         cls : 'column',
31140                         cn : managerWidget
31141                     }
31142                 ];
31143                 
31144                 if(this.labelWidth > 12){
31145                     content[0].style = "width: " + this.labelWidth + 'px';
31146                 }
31147
31148                 if(this.labelWidth < 13 && this.labelmd == 0){
31149                     this.labelmd = this.labelWidth;
31150                 }
31151
31152                 if(this.labellg > 0){
31153                     content[0].cls += ' col-lg-' + this.labellg;
31154                     content[1].cls += ' col-lg-' + (12 - this.labellg);
31155                 }
31156
31157                 if(this.labelmd > 0){
31158                     content[0].cls += ' col-md-' + this.labelmd;
31159                     content[1].cls += ' col-md-' + (12 - this.labelmd);
31160                 }
31161
31162                 if(this.labelsm > 0){
31163                     content[0].cls += ' col-sm-' + this.labelsm;
31164                     content[1].cls += ' col-sm-' + (12 - this.labelsm);
31165                 }
31166
31167                 if(this.labelxs > 0){
31168                     content[0].cls += ' col-xs-' + this.labelxs;
31169                     content[1].cls += ' col-xs-' + (12 - this.labelxs);
31170                 }
31171                 
31172             }
31173         }
31174         
31175         var cfg = {
31176             tag : 'div',
31177             cls : 'row clearfix',
31178             cn : content
31179         };
31180         
31181         return cfg;
31182         
31183     },
31184     
31185     initEvents : function()
31186     {
31187         this.managerEl = this.el.select('.roo-document-manager', true).first();
31188         this.managerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
31189         
31190         this.selectorEl = this.el.select('.roo-document-manager-selector', true).first();
31191         this.selectorEl.hide();
31192         
31193         if(this.multiple){
31194             this.selectorEl.attr('multiple', 'multiple');
31195         }
31196         
31197         this.selectorEl.on('change', this.onFileSelected, this);
31198         
31199         this.uploader = this.el.select('.roo-document-manager-uploader', true).first();
31200         this.uploader.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
31201         
31202         this.uploader.on('click', this.onUploaderClick, this);
31203         
31204         this.renderProgressDialog();
31205         
31206         var _this = this;
31207         
31208         window.addEventListener("resize", function() { _this.refresh(); } );
31209         
31210         this.fireEvent('initial', this);
31211     },
31212     
31213     renderProgressDialog : function()
31214     {
31215         var _this = this;
31216         
31217         this.progressDialog = new Roo.bootstrap.Modal({
31218             cls : 'roo-document-manager-progress-dialog',
31219             allow_close : false,
31220             animate : false,
31221             title : '',
31222             buttons : [
31223                 {
31224                     name  :'cancel',
31225                     weight : 'danger',
31226                     html : 'Cancel'
31227                 }
31228             ], 
31229             listeners : { 
31230                 btnclick : function() {
31231                     _this.uploadCancel();
31232                     this.hide();
31233                 }
31234             }
31235         });
31236          
31237         this.progressDialog.render(Roo.get(document.body));
31238          
31239         this.progress = new Roo.bootstrap.Progress({
31240             cls : 'roo-document-manager-progress',
31241             active : true,
31242             striped : true
31243         });
31244         
31245         this.progress.render(this.progressDialog.getChildContainer());
31246         
31247         this.progressBar = new Roo.bootstrap.ProgressBar({
31248             cls : 'roo-document-manager-progress-bar',
31249             aria_valuenow : 0,
31250             aria_valuemin : 0,
31251             aria_valuemax : 12,
31252             panel : 'success'
31253         });
31254         
31255         this.progressBar.render(this.progress.getChildContainer());
31256     },
31257     
31258     onUploaderClick : function(e)
31259     {
31260         e.preventDefault();
31261      
31262         if(this.fireEvent('beforeselectfile', this) != false){
31263             this.selectorEl.dom.click();
31264         }
31265         
31266     },
31267     
31268     onFileSelected : function(e)
31269     {
31270         e.preventDefault();
31271         
31272         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
31273             return;
31274         }
31275         
31276         Roo.each(this.selectorEl.dom.files, function(file){
31277             if(this.fireEvent('inspect', this, file) != false){
31278                 this.files.push(file);
31279             }
31280         }, this);
31281         
31282         this.queue();
31283         
31284     },
31285     
31286     queue : function()
31287     {
31288         this.selectorEl.dom.value = '';
31289         
31290         if(!this.files || !this.files.length){
31291             return;
31292         }
31293         
31294         if(this.boxes > 0 && this.files.length > this.boxes){
31295             this.files = this.files.slice(0, this.boxes);
31296         }
31297         
31298         this.uploader.show();
31299         
31300         if(this.boxes > 0 && this.files.length > this.boxes - 1){
31301             this.uploader.hide();
31302         }
31303         
31304         var _this = this;
31305         
31306         var files = [];
31307         
31308         var docs = [];
31309         
31310         Roo.each(this.files, function(file){
31311             
31312             if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
31313                 var f = this.renderPreview(file);
31314                 files.push(f);
31315                 return;
31316             }
31317             
31318             if(file.type.indexOf('image') != -1){
31319                 this.delegates.push(
31320                     (function(){
31321                         _this.process(file);
31322                     }).createDelegate(this)
31323                 );
31324         
31325                 return;
31326             }
31327             
31328             docs.push(
31329                 (function(){
31330                     _this.process(file);
31331                 }).createDelegate(this)
31332             );
31333             
31334         }, this);
31335         
31336         this.files = files;
31337         
31338         this.delegates = this.delegates.concat(docs);
31339         
31340         if(!this.delegates.length){
31341             this.refresh();
31342             return;
31343         }
31344         
31345         this.progressBar.aria_valuemax = this.delegates.length;
31346         
31347         this.arrange();
31348         
31349         return;
31350     },
31351     
31352     arrange : function()
31353     {
31354         if(!this.delegates.length){
31355             this.progressDialog.hide();
31356             this.refresh();
31357             return;
31358         }
31359         
31360         var delegate = this.delegates.shift();
31361         
31362         this.progressDialog.show();
31363         
31364         this.progressDialog.setTitle((this.progressBar.aria_valuemax - this.delegates.length) + ' / ' + this.progressBar.aria_valuemax);
31365         
31366         this.progressBar.update(this.progressBar.aria_valuemax - this.delegates.length);
31367         
31368         delegate();
31369     },
31370     
31371     refresh : function()
31372     {
31373         this.uploader.show();
31374         
31375         if(this.boxes > 0 && this.files.length > this.boxes - 1){
31376             this.uploader.hide();
31377         }
31378         
31379         Roo.isTouch ? this.closable(false) : this.closable(true);
31380         
31381         this.fireEvent('refresh', this);
31382     },
31383     
31384     onRemove : function(e, el, o)
31385     {
31386         e.preventDefault();
31387         
31388         this.fireEvent('remove', this, o);
31389         
31390     },
31391     
31392     remove : function(o)
31393     {
31394         var files = [];
31395         
31396         Roo.each(this.files, function(file){
31397             if(typeof(file.id) == 'undefined' || file.id * 1 < 1 || file.id != o.id){
31398                 files.push(file);
31399                 return;
31400             }
31401
31402             o.target.remove();
31403
31404         }, this);
31405         
31406         this.files = files;
31407         
31408         this.refresh();
31409     },
31410     
31411     clear : function()
31412     {
31413         Roo.each(this.files, function(file){
31414             if(!file.target){
31415                 return;
31416             }
31417             
31418             file.target.remove();
31419
31420         }, this);
31421         
31422         this.files = [];
31423         
31424         this.refresh();
31425     },
31426     
31427     onClick : function(e, el, o)
31428     {
31429         e.preventDefault();
31430         
31431         this.fireEvent('click', this, o);
31432         
31433     },
31434     
31435     closable : function(closable)
31436     {
31437         Roo.each(this.managerEl.select('.roo-document-manager-preview > button.close', true).elements, function(el){
31438             
31439             el.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
31440             
31441             if(closable){
31442                 el.show();
31443                 return;
31444             }
31445             
31446             el.hide();
31447             
31448         }, this);
31449     },
31450     
31451     xhrOnLoad : function(xhr)
31452     {
31453         Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
31454             el.remove();
31455         }, this);
31456         
31457         if (xhr.readyState !== 4) {
31458             this.arrange();
31459             this.fireEvent('exception', this, xhr);
31460             return;
31461         }
31462
31463         var response = Roo.decode(xhr.responseText);
31464         
31465         if(!response.success){
31466             this.arrange();
31467             this.fireEvent('exception', this, xhr);
31468             return;
31469         }
31470         
31471         var file = this.renderPreview(response.data);
31472         
31473         this.files.push(file);
31474         
31475         this.arrange();
31476         
31477         this.fireEvent('afterupload', this, xhr);
31478         
31479     },
31480     
31481     xhrOnError : function(xhr)
31482     {
31483         Roo.log('xhr on error');
31484         
31485         var response = Roo.decode(xhr.responseText);
31486           
31487         Roo.log(response);
31488         
31489         this.arrange();
31490     },
31491     
31492     process : function(file)
31493     {
31494         if(this.fireEvent('process', this, file) !== false){
31495             if(this.editable && file.type.indexOf('image') != -1){
31496                 this.fireEvent('edit', this, file);
31497                 return;
31498             }
31499
31500             this.uploadStart(file, false);
31501
31502             return;
31503         }
31504         
31505     },
31506     
31507     uploadStart : function(file, crop)
31508     {
31509         this.xhr = new XMLHttpRequest();
31510         
31511         if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
31512             this.arrange();
31513             return;
31514         }
31515         
31516         file.xhr = this.xhr;
31517             
31518         this.managerEl.createChild({
31519             tag : 'div',
31520             cls : 'roo-document-manager-loading',
31521             cn : [
31522                 {
31523                     tag : 'div',
31524                     tooltip : file.name,
31525                     cls : 'roo-document-manager-thumb',
31526                     html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
31527                 }
31528             ]
31529
31530         });
31531
31532         this.xhr.open(this.method, this.url, true);
31533         
31534         var headers = {
31535             "Accept": "application/json",
31536             "Cache-Control": "no-cache",
31537             "X-Requested-With": "XMLHttpRequest"
31538         };
31539         
31540         for (var headerName in headers) {
31541             var headerValue = headers[headerName];
31542             if (headerValue) {
31543                 this.xhr.setRequestHeader(headerName, headerValue);
31544             }
31545         }
31546         
31547         var _this = this;
31548         
31549         this.xhr.onload = function()
31550         {
31551             _this.xhrOnLoad(_this.xhr);
31552         }
31553         
31554         this.xhr.onerror = function()
31555         {
31556             _this.xhrOnError(_this.xhr);
31557         }
31558         
31559         var formData = new FormData();
31560
31561         formData.append('returnHTML', 'NO');
31562         
31563         if(crop){
31564             formData.append('crop', crop);
31565         }
31566         
31567         formData.append(this.paramName, file, file.name);
31568         
31569         var options = {
31570             file : file, 
31571             manually : false
31572         };
31573         
31574         if(this.fireEvent('prepare', this, formData, options) != false){
31575             
31576             if(options.manually){
31577                 return;
31578             }
31579             
31580             this.xhr.send(formData);
31581             return;
31582         };
31583         
31584         this.uploadCancel();
31585     },
31586     
31587     uploadCancel : function()
31588     {
31589         if (this.xhr) {
31590             this.xhr.abort();
31591         }
31592         
31593         this.delegates = [];
31594         
31595         Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
31596             el.remove();
31597         }, this);
31598         
31599         this.arrange();
31600     },
31601     
31602     renderPreview : function(file)
31603     {
31604         if(typeof(file.target) != 'undefined' && file.target){
31605             return file;
31606         }
31607         
31608         var img_src = encodeURI(baseURL +'/Images/Thumb/' + this.thumbSize + '/' + file.id + '/' + file.filename);
31609         
31610         var previewEl = this.managerEl.createChild({
31611             tag : 'div',
31612             cls : 'roo-document-manager-preview',
31613             cn : [
31614                 {
31615                     tag : 'div',
31616                     tooltip : file[this.toolTipName],
31617                     cls : 'roo-document-manager-thumb',
31618                     html : '<img tooltip="' + file[this.toolTipName] + '" src="' + img_src + '">'
31619                 },
31620                 {
31621                     tag : 'button',
31622                     cls : 'close',
31623                     html : '<i class="fa fa-times-circle"></i>'
31624                 }
31625             ]
31626         });
31627
31628         var close = previewEl.select('button.close', true).first();
31629
31630         close.on('click', this.onRemove, this, file);
31631
31632         file.target = previewEl;
31633
31634         var image = previewEl.select('img', true).first();
31635         
31636         var _this = this;
31637         
31638         image.dom.addEventListener("load", function(){ _this.onPreviewLoad(file, image); });
31639         
31640         image.on('click', this.onClick, this, file);
31641         
31642         this.fireEvent('previewrendered', this, file);
31643         
31644         return file;
31645         
31646     },
31647     
31648     onPreviewLoad : function(file, image)
31649     {
31650         if(typeof(file.target) == 'undefined' || !file.target){
31651             return;
31652         }
31653         
31654         var width = image.dom.naturalWidth || image.dom.width;
31655         var height = image.dom.naturalHeight || image.dom.height;
31656         
31657         if(!this.previewResize) {
31658             return;
31659         }
31660         
31661         if(width > height){
31662             file.target.addClass('wide');
31663             return;
31664         }
31665         
31666         file.target.addClass('tall');
31667         return;
31668         
31669     },
31670     
31671     uploadFromSource : function(file, crop)
31672     {
31673         this.xhr = new XMLHttpRequest();
31674         
31675         this.managerEl.createChild({
31676             tag : 'div',
31677             cls : 'roo-document-manager-loading',
31678             cn : [
31679                 {
31680                     tag : 'div',
31681                     tooltip : file.name,
31682                     cls : 'roo-document-manager-thumb',
31683                     html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
31684                 }
31685             ]
31686
31687         });
31688
31689         this.xhr.open(this.method, this.url, true);
31690         
31691         var headers = {
31692             "Accept": "application/json",
31693             "Cache-Control": "no-cache",
31694             "X-Requested-With": "XMLHttpRequest"
31695         };
31696         
31697         for (var headerName in headers) {
31698             var headerValue = headers[headerName];
31699             if (headerValue) {
31700                 this.xhr.setRequestHeader(headerName, headerValue);
31701             }
31702         }
31703         
31704         var _this = this;
31705         
31706         this.xhr.onload = function()
31707         {
31708             _this.xhrOnLoad(_this.xhr);
31709         }
31710         
31711         this.xhr.onerror = function()
31712         {
31713             _this.xhrOnError(_this.xhr);
31714         }
31715         
31716         var formData = new FormData();
31717
31718         formData.append('returnHTML', 'NO');
31719         
31720         formData.append('crop', crop);
31721         
31722         if(typeof(file.filename) != 'undefined'){
31723             formData.append('filename', file.filename);
31724         }
31725         
31726         if(typeof(file.mimetype) != 'undefined'){
31727             formData.append('mimetype', file.mimetype);
31728         }
31729         
31730         Roo.log(formData);
31731         
31732         if(this.fireEvent('prepare', this, formData) != false){
31733             this.xhr.send(formData);
31734         };
31735     }
31736 });
31737
31738 /*
31739 * Licence: LGPL
31740 */
31741
31742 /**
31743  * @class Roo.bootstrap.DocumentViewer
31744  * @extends Roo.bootstrap.Component
31745  * Bootstrap DocumentViewer class
31746  * @cfg {Boolean} showDownload (true|false) show download button (default true)
31747  * @cfg {Boolean} showTrash (true|false) show trash button (default true)
31748  * 
31749  * @constructor
31750  * Create a new DocumentViewer
31751  * @param {Object} config The config object
31752  */
31753
31754 Roo.bootstrap.DocumentViewer = function(config){
31755     Roo.bootstrap.DocumentViewer.superclass.constructor.call(this, config);
31756     
31757     this.addEvents({
31758         /**
31759          * @event initial
31760          * Fire after initEvent
31761          * @param {Roo.bootstrap.DocumentViewer} this
31762          */
31763         "initial" : true,
31764         /**
31765          * @event click
31766          * Fire after click
31767          * @param {Roo.bootstrap.DocumentViewer} this
31768          */
31769         "click" : true,
31770         /**
31771          * @event download
31772          * Fire after download button
31773          * @param {Roo.bootstrap.DocumentViewer} this
31774          */
31775         "download" : true,
31776         /**
31777          * @event trash
31778          * Fire after trash button
31779          * @param {Roo.bootstrap.DocumentViewer} this
31780          */
31781         "trash" : true
31782         
31783     });
31784 };
31785
31786 Roo.extend(Roo.bootstrap.DocumentViewer, Roo.bootstrap.Component,  {
31787     
31788     showDownload : true,
31789     
31790     showTrash : true,
31791     
31792     getAutoCreate : function()
31793     {
31794         var cfg = {
31795             tag : 'div',
31796             cls : 'roo-document-viewer',
31797             cn : [
31798                 {
31799                     tag : 'div',
31800                     cls : 'roo-document-viewer-body',
31801                     cn : [
31802                         {
31803                             tag : 'div',
31804                             cls : 'roo-document-viewer-thumb',
31805                             cn : [
31806                                 {
31807                                     tag : 'img',
31808                                     cls : 'roo-document-viewer-image'
31809                                 }
31810                             ]
31811                         }
31812                     ]
31813                 },
31814                 {
31815                     tag : 'div',
31816                     cls : 'roo-document-viewer-footer',
31817                     cn : {
31818                         tag : 'div',
31819                         cls : 'btn-group btn-group-justified roo-document-viewer-btn-group',
31820                         cn : [
31821                             {
31822                                 tag : 'div',
31823                                 cls : 'btn-group roo-document-viewer-download',
31824                                 cn : [
31825                                     {
31826                                         tag : 'button',
31827                                         cls : 'btn btn-default',
31828                                         html : '<i class="fa fa-download"></i>'
31829                                     }
31830                                 ]
31831                             },
31832                             {
31833                                 tag : 'div',
31834                                 cls : 'btn-group roo-document-viewer-trash',
31835                                 cn : [
31836                                     {
31837                                         tag : 'button',
31838                                         cls : 'btn btn-default',
31839                                         html : '<i class="fa fa-trash"></i>'
31840                                     }
31841                                 ]
31842                             }
31843                         ]
31844                     }
31845                 }
31846             ]
31847         };
31848         
31849         return cfg;
31850     },
31851     
31852     initEvents : function()
31853     {
31854         this.bodyEl = this.el.select('.roo-document-viewer-body', true).first();
31855         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
31856         
31857         this.thumbEl = this.el.select('.roo-document-viewer-thumb', true).first();
31858         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
31859         
31860         this.imageEl = this.el.select('.roo-document-viewer-image', true).first();
31861         this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
31862         
31863         this.footerEl = this.el.select('.roo-document-viewer-footer', true).first();
31864         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY);
31865         
31866         this.downloadBtn = this.el.select('.roo-document-viewer-download', true).first();
31867         this.downloadBtn.setVisibilityMode(Roo.Element.DISPLAY);
31868         
31869         this.trashBtn = this.el.select('.roo-document-viewer-trash', true).first();
31870         this.trashBtn.setVisibilityMode(Roo.Element.DISPLAY);
31871         
31872         this.bodyEl.on('click', this.onClick, this);
31873         this.downloadBtn.on('click', this.onDownload, this);
31874         this.trashBtn.on('click', this.onTrash, this);
31875         
31876         this.downloadBtn.hide();
31877         this.trashBtn.hide();
31878         
31879         if(this.showDownload){
31880             this.downloadBtn.show();
31881         }
31882         
31883         if(this.showTrash){
31884             this.trashBtn.show();
31885         }
31886         
31887         if(!this.showDownload && !this.showTrash) {
31888             this.footerEl.hide();
31889         }
31890         
31891     },
31892     
31893     initial : function()
31894     {
31895         this.fireEvent('initial', this);
31896         
31897     },
31898     
31899     onClick : function(e)
31900     {
31901         e.preventDefault();
31902         
31903         this.fireEvent('click', this);
31904     },
31905     
31906     onDownload : function(e)
31907     {
31908         e.preventDefault();
31909         
31910         this.fireEvent('download', this);
31911     },
31912     
31913     onTrash : function(e)
31914     {
31915         e.preventDefault();
31916         
31917         this.fireEvent('trash', this);
31918     }
31919     
31920 });
31921 /*
31922  * - LGPL
31923  *
31924  * nav progress bar
31925  * 
31926  */
31927
31928 /**
31929  * @class Roo.bootstrap.NavProgressBar
31930  * @extends Roo.bootstrap.Component
31931  * Bootstrap NavProgressBar class
31932  * 
31933  * @constructor
31934  * Create a new nav progress bar
31935  * @param {Object} config The config object
31936  */
31937
31938 Roo.bootstrap.NavProgressBar = function(config){
31939     Roo.bootstrap.NavProgressBar.superclass.constructor.call(this, config);
31940
31941     this.bullets = this.bullets || [];
31942    
31943 //    Roo.bootstrap.NavProgressBar.register(this);
31944      this.addEvents({
31945         /**
31946              * @event changed
31947              * Fires when the active item changes
31948              * @param {Roo.bootstrap.NavProgressBar} this
31949              * @param {Roo.bootstrap.NavProgressItem} selected The item selected
31950              * @param {Roo.bootstrap.NavProgressItem} prev The previously selected item 
31951          */
31952         'changed': true
31953      });
31954     
31955 };
31956
31957 Roo.extend(Roo.bootstrap.NavProgressBar, Roo.bootstrap.Component,  {
31958     
31959     bullets : [],
31960     barItems : [],
31961     
31962     getAutoCreate : function()
31963     {
31964         var cfg = Roo.apply({}, Roo.bootstrap.NavProgressBar.superclass.getAutoCreate.call(this));
31965         
31966         cfg = {
31967             tag : 'div',
31968             cls : 'roo-navigation-bar-group',
31969             cn : [
31970                 {
31971                     tag : 'div',
31972                     cls : 'roo-navigation-top-bar'
31973                 },
31974                 {
31975                     tag : 'div',
31976                     cls : 'roo-navigation-bullets-bar',
31977                     cn : [
31978                         {
31979                             tag : 'ul',
31980                             cls : 'roo-navigation-bar'
31981                         }
31982                     ]
31983                 },
31984                 
31985                 {
31986                     tag : 'div',
31987                     cls : 'roo-navigation-bottom-bar'
31988                 }
31989             ]
31990             
31991         };
31992         
31993         return cfg;
31994         
31995     },
31996     
31997     initEvents: function() 
31998     {
31999         
32000     },
32001     
32002     onRender : function(ct, position) 
32003     {
32004         Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
32005         
32006         if(this.bullets.length){
32007             Roo.each(this.bullets, function(b){
32008                this.addItem(b);
32009             }, this);
32010         }
32011         
32012         this.format();
32013         
32014     },
32015     
32016     addItem : function(cfg)
32017     {
32018         var item = new Roo.bootstrap.NavProgressItem(cfg);
32019         
32020         item.parentId = this.id;
32021         item.render(this.el.select('.roo-navigation-bar', true).first(), null);
32022         
32023         if(cfg.html){
32024             var top = new Roo.bootstrap.Element({
32025                 tag : 'div',
32026                 cls : 'roo-navigation-bar-text'
32027             });
32028             
32029             var bottom = new Roo.bootstrap.Element({
32030                 tag : 'div',
32031                 cls : 'roo-navigation-bar-text'
32032             });
32033             
32034             top.onRender(this.el.select('.roo-navigation-top-bar', true).first(), null);
32035             bottom.onRender(this.el.select('.roo-navigation-bottom-bar', true).first(), null);
32036             
32037             var topText = new Roo.bootstrap.Element({
32038                 tag : 'span',
32039                 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? cfg.html : ''
32040             });
32041             
32042             var bottomText = new Roo.bootstrap.Element({
32043                 tag : 'span',
32044                 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? '' : cfg.html
32045             });
32046             
32047             topText.onRender(top.el, null);
32048             bottomText.onRender(bottom.el, null);
32049             
32050             item.topEl = top;
32051             item.bottomEl = bottom;
32052         }
32053         
32054         this.barItems.push(item);
32055         
32056         return item;
32057     },
32058     
32059     getActive : function()
32060     {
32061         var active = false;
32062         
32063         Roo.each(this.barItems, function(v){
32064             
32065             if (!v.isActive()) {
32066                 return;
32067             }
32068             
32069             active = v;
32070             return false;
32071             
32072         });
32073         
32074         return active;
32075     },
32076     
32077     setActiveItem : function(item)
32078     {
32079         var prev = false;
32080         
32081         Roo.each(this.barItems, function(v){
32082             if (v.rid == item.rid) {
32083                 return ;
32084             }
32085             
32086             if (v.isActive()) {
32087                 v.setActive(false);
32088                 prev = v;
32089             }
32090         });
32091
32092         item.setActive(true);
32093         
32094         this.fireEvent('changed', this, item, prev);
32095     },
32096     
32097     getBarItem: function(rid)
32098     {
32099         var ret = false;
32100         
32101         Roo.each(this.barItems, function(e) {
32102             if (e.rid != rid) {
32103                 return;
32104             }
32105             
32106             ret =  e;
32107             return false;
32108         });
32109         
32110         return ret;
32111     },
32112     
32113     indexOfItem : function(item)
32114     {
32115         var index = false;
32116         
32117         Roo.each(this.barItems, function(v, i){
32118             
32119             if (v.rid != item.rid) {
32120                 return;
32121             }
32122             
32123             index = i;
32124             return false
32125         });
32126         
32127         return index;
32128     },
32129     
32130     setActiveNext : function()
32131     {
32132         var i = this.indexOfItem(this.getActive());
32133         
32134         if (i > this.barItems.length) {
32135             return;
32136         }
32137         
32138         this.setActiveItem(this.barItems[i+1]);
32139     },
32140     
32141     setActivePrev : function()
32142     {
32143         var i = this.indexOfItem(this.getActive());
32144         
32145         if (i  < 1) {
32146             return;
32147         }
32148         
32149         this.setActiveItem(this.barItems[i-1]);
32150     },
32151     
32152     format : function()
32153     {
32154         if(!this.barItems.length){
32155             return;
32156         }
32157      
32158         var width = 100 / this.barItems.length;
32159         
32160         Roo.each(this.barItems, function(i){
32161             i.el.setStyle('width', width + '%');
32162             i.topEl.el.setStyle('width', width + '%');
32163             i.bottomEl.el.setStyle('width', width + '%');
32164         }, this);
32165         
32166     }
32167     
32168 });
32169 /*
32170  * - LGPL
32171  *
32172  * Nav Progress Item
32173  * 
32174  */
32175
32176 /**
32177  * @class Roo.bootstrap.NavProgressItem
32178  * @extends Roo.bootstrap.Component
32179  * Bootstrap NavProgressItem class
32180  * @cfg {String} rid the reference id
32181  * @cfg {Boolean} active (true|false) Is item active default false
32182  * @cfg {Boolean} disabled (true|false) Is item active default false
32183  * @cfg {String} html
32184  * @cfg {String} position (top|bottom) text position default bottom
32185  * @cfg {String} icon show icon instead of number
32186  * 
32187  * @constructor
32188  * Create a new NavProgressItem
32189  * @param {Object} config The config object
32190  */
32191 Roo.bootstrap.NavProgressItem = function(config){
32192     Roo.bootstrap.NavProgressItem.superclass.constructor.call(this, config);
32193     this.addEvents({
32194         // raw events
32195         /**
32196          * @event click
32197          * The raw click event for the entire grid.
32198          * @param {Roo.bootstrap.NavProgressItem} this
32199          * @param {Roo.EventObject} e
32200          */
32201         "click" : true
32202     });
32203    
32204 };
32205
32206 Roo.extend(Roo.bootstrap.NavProgressItem, Roo.bootstrap.Component,  {
32207     
32208     rid : '',
32209     active : false,
32210     disabled : false,
32211     html : '',
32212     position : 'bottom',
32213     icon : false,
32214     
32215     getAutoCreate : function()
32216     {
32217         var iconCls = 'roo-navigation-bar-item-icon';
32218         
32219         iconCls += ((this.icon) ? (' ' + this.icon) : (' step-number')) ;
32220         
32221         var cfg = {
32222             tag: 'li',
32223             cls: 'roo-navigation-bar-item',
32224             cn : [
32225                 {
32226                     tag : 'i',
32227                     cls : iconCls
32228                 }
32229             ]
32230         };
32231         
32232         if(this.active){
32233             cfg.cls += ' active';
32234         }
32235         if(this.disabled){
32236             cfg.cls += ' disabled';
32237         }
32238         
32239         return cfg;
32240     },
32241     
32242     disable : function()
32243     {
32244         this.setDisabled(true);
32245     },
32246     
32247     enable : function()
32248     {
32249         this.setDisabled(false);
32250     },
32251     
32252     initEvents: function() 
32253     {
32254         this.iconEl = this.el.select('.roo-navigation-bar-item-icon', true).first();
32255         
32256         this.iconEl.on('click', this.onClick, this);
32257     },
32258     
32259     onClick : function(e)
32260     {
32261         e.preventDefault();
32262         
32263         if(this.disabled){
32264             return;
32265         }
32266         
32267         if(this.fireEvent('click', this, e) === false){
32268             return;
32269         };
32270         
32271         this.parent().setActiveItem(this);
32272     },
32273     
32274     isActive: function () 
32275     {
32276         return this.active;
32277     },
32278     
32279     setActive : function(state)
32280     {
32281         if(this.active == state){
32282             return;
32283         }
32284         
32285         this.active = state;
32286         
32287         if (state) {
32288             this.el.addClass('active');
32289             return;
32290         }
32291         
32292         this.el.removeClass('active');
32293         
32294         return;
32295     },
32296     
32297     setDisabled : function(state)
32298     {
32299         if(this.disabled == state){
32300             return;
32301         }
32302         
32303         this.disabled = state;
32304         
32305         if (state) {
32306             this.el.addClass('disabled');
32307             return;
32308         }
32309         
32310         this.el.removeClass('disabled');
32311     },
32312     
32313     tooltipEl : function()
32314     {
32315         return this.el.select('.roo-navigation-bar-item-icon', true).first();;
32316     }
32317 });
32318  
32319
32320  /*
32321  * - LGPL
32322  *
32323  * FieldLabel
32324  * 
32325  */
32326
32327 /**
32328  * @class Roo.bootstrap.FieldLabel
32329  * @extends Roo.bootstrap.Component
32330  * Bootstrap FieldLabel class
32331  * @cfg {String} html contents of the element
32332  * @cfg {String} tag tag of the element default label
32333  * @cfg {String} cls class of the element
32334  * @cfg {String} target label target 
32335  * @cfg {Boolean} allowBlank (true|false) target allowBlank default true
32336  * @cfg {String} invalidClass DEPRICATED - BS4 uses is-invalid
32337  * @cfg {String} validClass DEPRICATED - BS4 uses is-valid
32338  * @cfg {String} iconTooltip default "This field is required"
32339  * @cfg {String} indicatorpos (left|right) default left
32340  * 
32341  * @constructor
32342  * Create a new FieldLabel
32343  * @param {Object} config The config object
32344  */
32345
32346 Roo.bootstrap.FieldLabel = function(config){
32347     Roo.bootstrap.Element.superclass.constructor.call(this, config);
32348     
32349     this.addEvents({
32350             /**
32351              * @event invalid
32352              * Fires after the field has been marked as invalid.
32353              * @param {Roo.form.FieldLabel} this
32354              * @param {String} msg The validation message
32355              */
32356             invalid : true,
32357             /**
32358              * @event valid
32359              * Fires after the field has been validated with no errors.
32360              * @param {Roo.form.FieldLabel} this
32361              */
32362             valid : true
32363         });
32364 };
32365
32366 Roo.extend(Roo.bootstrap.FieldLabel, Roo.bootstrap.Component,  {
32367     
32368     tag: 'label',
32369     cls: '',
32370     html: '',
32371     target: '',
32372     allowBlank : true,
32373     invalidClass : 'has-warning',
32374     validClass : 'has-success',
32375     iconTooltip : 'This field is required',
32376     indicatorpos : 'left',
32377     
32378     getAutoCreate : function(){
32379         
32380         var cls = "";
32381         if (!this.allowBlank) {
32382             cls  = "visible";
32383         }
32384         
32385         var cfg = {
32386             tag : this.tag,
32387             cls : 'roo-bootstrap-field-label ' + this.cls,
32388             for : this.target,
32389             cn : [
32390                 {
32391                     tag : 'i',
32392                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star ' + cls,
32393                     tooltip : this.iconTooltip
32394                 },
32395                 {
32396                     tag : 'span',
32397                     html : this.html
32398                 }
32399             ] 
32400         };
32401         
32402         if(this.indicatorpos == 'right'){
32403             var cfg = {
32404                 tag : this.tag,
32405                 cls : 'roo-bootstrap-field-label ' + this.cls,
32406                 for : this.target,
32407                 cn : [
32408                     {
32409                         tag : 'span',
32410                         html : this.html
32411                     },
32412                     {
32413                         tag : 'i',
32414                         cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star '+ cls,
32415                         tooltip : this.iconTooltip
32416                     }
32417                 ] 
32418             };
32419         }
32420         
32421         return cfg;
32422     },
32423     
32424     initEvents: function() 
32425     {
32426         Roo.bootstrap.Element.superclass.initEvents.call(this);
32427         
32428         this.indicator = this.indicatorEl();
32429         
32430         if(this.indicator){
32431             this.indicator.removeClass('visible');
32432             this.indicator.addClass('invisible');
32433         }
32434         
32435         Roo.bootstrap.FieldLabel.register(this);
32436     },
32437     
32438     indicatorEl : function()
32439     {
32440         var indicator = this.el.select('i.roo-required-indicator',true).first();
32441         
32442         if(!indicator){
32443             return false;
32444         }
32445         
32446         return indicator;
32447         
32448     },
32449     
32450     /**
32451      * Mark this field as valid
32452      */
32453     markValid : function()
32454     {
32455         if(this.indicator){
32456             this.indicator.removeClass('visible');
32457             this.indicator.addClass('invisible');
32458         }
32459         if (Roo.bootstrap.version == 3) {
32460             this.el.removeClass(this.invalidClass);
32461             this.el.addClass(this.validClass);
32462         } else {
32463             this.el.removeClass('is-invalid');
32464             this.el.addClass('is-valid');
32465         }
32466         
32467         
32468         this.fireEvent('valid', this);
32469     },
32470     
32471     /**
32472      * Mark this field as invalid
32473      * @param {String} msg The validation message
32474      */
32475     markInvalid : function(msg)
32476     {
32477         if(this.indicator){
32478             this.indicator.removeClass('invisible');
32479             this.indicator.addClass('visible');
32480         }
32481           if (Roo.bootstrap.version == 3) {
32482             this.el.removeClass(this.validClass);
32483             this.el.addClass(this.invalidClass);
32484         } else {
32485             this.el.removeClass('is-valid');
32486             this.el.addClass('is-invalid');
32487         }
32488         
32489         
32490         this.fireEvent('invalid', this, msg);
32491     }
32492     
32493    
32494 });
32495
32496 Roo.apply(Roo.bootstrap.FieldLabel, {
32497     
32498     groups: {},
32499     
32500      /**
32501     * register a FieldLabel Group
32502     * @param {Roo.bootstrap.FieldLabel} the FieldLabel to add
32503     */
32504     register : function(label)
32505     {
32506         if(this.groups.hasOwnProperty(label.target)){
32507             return;
32508         }
32509      
32510         this.groups[label.target] = label;
32511         
32512     },
32513     /**
32514     * fetch a FieldLabel Group based on the target
32515     * @param {string} target
32516     * @returns {Roo.bootstrap.FieldLabel} the CheckBox group
32517     */
32518     get: function(target) {
32519         if (typeof(this.groups[target]) == 'undefined') {
32520             return false;
32521         }
32522         
32523         return this.groups[target] ;
32524     }
32525 });
32526
32527  
32528
32529  /*
32530  * - LGPL
32531  *
32532  * page DateSplitField.
32533  * 
32534  */
32535
32536
32537 /**
32538  * @class Roo.bootstrap.DateSplitField
32539  * @extends Roo.bootstrap.Component
32540  * Bootstrap DateSplitField class
32541  * @cfg {string} fieldLabel - the label associated
32542  * @cfg {Number} labelWidth set the width of label (0-12)
32543  * @cfg {String} labelAlign (top|left)
32544  * @cfg {Boolean} dayAllowBlank (true|false) default false
32545  * @cfg {Boolean} monthAllowBlank (true|false) default false
32546  * @cfg {Boolean} yearAllowBlank (true|false) default false
32547  * @cfg {string} dayPlaceholder 
32548  * @cfg {string} monthPlaceholder
32549  * @cfg {string} yearPlaceholder
32550  * @cfg {string} dayFormat default 'd'
32551  * @cfg {string} monthFormat default 'm'
32552  * @cfg {string} yearFormat default 'Y'
32553  * @cfg {Number} labellg set the width of label (1-12)
32554  * @cfg {Number} labelmd set the width of label (1-12)
32555  * @cfg {Number} labelsm set the width of label (1-12)
32556  * @cfg {Number} labelxs set the width of label (1-12)
32557
32558  *     
32559  * @constructor
32560  * Create a new DateSplitField
32561  * @param {Object} config The config object
32562  */
32563
32564 Roo.bootstrap.DateSplitField = function(config){
32565     Roo.bootstrap.DateSplitField.superclass.constructor.call(this, config);
32566     
32567     this.addEvents({
32568         // raw events
32569          /**
32570          * @event years
32571          * getting the data of years
32572          * @param {Roo.bootstrap.DateSplitField} this
32573          * @param {Object} years
32574          */
32575         "years" : true,
32576         /**
32577          * @event days
32578          * getting the data of days
32579          * @param {Roo.bootstrap.DateSplitField} this
32580          * @param {Object} days
32581          */
32582         "days" : true,
32583         /**
32584          * @event invalid
32585          * Fires after the field has been marked as invalid.
32586          * @param {Roo.form.Field} this
32587          * @param {String} msg The validation message
32588          */
32589         invalid : true,
32590        /**
32591          * @event valid
32592          * Fires after the field has been validated with no errors.
32593          * @param {Roo.form.Field} this
32594          */
32595         valid : true
32596     });
32597 };
32598
32599 Roo.extend(Roo.bootstrap.DateSplitField, Roo.bootstrap.Component,  {
32600     
32601     fieldLabel : '',
32602     labelAlign : 'top',
32603     labelWidth : 3,
32604     dayAllowBlank : false,
32605     monthAllowBlank : false,
32606     yearAllowBlank : false,
32607     dayPlaceholder : '',
32608     monthPlaceholder : '',
32609     yearPlaceholder : '',
32610     dayFormat : 'd',
32611     monthFormat : 'm',
32612     yearFormat : 'Y',
32613     isFormField : true,
32614     labellg : 0,
32615     labelmd : 0,
32616     labelsm : 0,
32617     labelxs : 0,
32618     
32619     getAutoCreate : function()
32620     {
32621         var cfg = {
32622             tag : 'div',
32623             cls : 'row roo-date-split-field-group',
32624             cn : [
32625                 {
32626                     tag : 'input',
32627                     type : 'hidden',
32628                     cls : 'form-hidden-field roo-date-split-field-group-value',
32629                     name : this.name
32630                 }
32631             ]
32632         };
32633         
32634         var labelCls = 'col-md-12';
32635         var contentCls = 'col-md-4';
32636         
32637         if(this.fieldLabel){
32638             
32639             var label = {
32640                 tag : 'div',
32641                 cls : 'column roo-date-split-field-label col-md-' + ((this.labelAlign == 'top') ? '12' : this.labelWidth),
32642                 cn : [
32643                     {
32644                         tag : 'label',
32645                         html : this.fieldLabel
32646                     }
32647                 ]
32648             };
32649             
32650             if(this.labelAlign == 'left'){
32651             
32652                 if(this.labelWidth > 12){
32653                     label.style = "width: " + this.labelWidth + 'px';
32654                 }
32655
32656                 if(this.labelWidth < 13 && this.labelmd == 0){
32657                     this.labelmd = this.labelWidth;
32658                 }
32659
32660                 if(this.labellg > 0){
32661                     labelCls = ' col-lg-' + this.labellg;
32662                     contentCls = ' col-lg-' + ((12 - this.labellg) / 3);
32663                 }
32664
32665                 if(this.labelmd > 0){
32666                     labelCls = ' col-md-' + this.labelmd;
32667                     contentCls = ' col-md-' + ((12 - this.labelmd) / 3);
32668                 }
32669
32670                 if(this.labelsm > 0){
32671                     labelCls = ' col-sm-' + this.labelsm;
32672                     contentCls = ' col-sm-' + ((12 - this.labelsm) / 3);
32673                 }
32674
32675                 if(this.labelxs > 0){
32676                     labelCls = ' col-xs-' + this.labelxs;
32677                     contentCls = ' col-xs-' + ((12 - this.labelxs) / 3);
32678                 }
32679             }
32680             
32681             label.cls += ' ' + labelCls;
32682             
32683             cfg.cn.push(label);
32684         }
32685         
32686         Roo.each(['day', 'month', 'year'], function(t){
32687             cfg.cn.push({
32688                 tag : 'div',
32689                 cls : 'column roo-date-split-field-' + t + ' ' + contentCls
32690             });
32691         }, this);
32692         
32693         return cfg;
32694     },
32695     
32696     inputEl: function ()
32697     {
32698         return this.el.select('.roo-date-split-field-group-value', true).first();
32699     },
32700     
32701     onRender : function(ct, position) 
32702     {
32703         var _this = this;
32704         
32705         Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
32706         
32707         this.inputEl = this.el.select('.roo-date-split-field-group-value', true).first();
32708         
32709         this.dayField = new Roo.bootstrap.ComboBox({
32710             allowBlank : this.dayAllowBlank,
32711             alwaysQuery : true,
32712             displayField : 'value',
32713             editable : false,
32714             fieldLabel : '',
32715             forceSelection : true,
32716             mode : 'local',
32717             placeholder : this.dayPlaceholder,
32718             selectOnFocus : true,
32719             tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
32720             triggerAction : 'all',
32721             typeAhead : true,
32722             valueField : 'value',
32723             store : new Roo.data.SimpleStore({
32724                 data : (function() {    
32725                     var days = [];
32726                     _this.fireEvent('days', _this, days);
32727                     return days;
32728                 })(),
32729                 fields : [ 'value' ]
32730             }),
32731             listeners : {
32732                 select : function (_self, record, index)
32733                 {
32734                     _this.setValue(_this.getValue());
32735                 }
32736             }
32737         });
32738
32739         this.dayField.render(this.el.select('.roo-date-split-field-day', true).first(), null);
32740         
32741         this.monthField = new Roo.bootstrap.MonthField({
32742             after : '<i class=\"fa fa-calendar\"></i>',
32743             allowBlank : this.monthAllowBlank,
32744             placeholder : this.monthPlaceholder,
32745             readOnly : true,
32746             listeners : {
32747                 render : function (_self)
32748                 {
32749                     this.el.select('span.input-group-addon', true).first().on('click', function(e){
32750                         e.preventDefault();
32751                         _self.focus();
32752                     });
32753                 },
32754                 select : function (_self, oldvalue, newvalue)
32755                 {
32756                     _this.setValue(_this.getValue());
32757                 }
32758             }
32759         });
32760         
32761         this.monthField.render(this.el.select('.roo-date-split-field-month', true).first(), null);
32762         
32763         this.yearField = new Roo.bootstrap.ComboBox({
32764             allowBlank : this.yearAllowBlank,
32765             alwaysQuery : true,
32766             displayField : 'value',
32767             editable : false,
32768             fieldLabel : '',
32769             forceSelection : true,
32770             mode : 'local',
32771             placeholder : this.yearPlaceholder,
32772             selectOnFocus : true,
32773             tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
32774             triggerAction : 'all',
32775             typeAhead : true,
32776             valueField : 'value',
32777             store : new Roo.data.SimpleStore({
32778                 data : (function() {
32779                     var years = [];
32780                     _this.fireEvent('years', _this, years);
32781                     return years;
32782                 })(),
32783                 fields : [ 'value' ]
32784             }),
32785             listeners : {
32786                 select : function (_self, record, index)
32787                 {
32788                     _this.setValue(_this.getValue());
32789                 }
32790             }
32791         });
32792
32793         this.yearField.render(this.el.select('.roo-date-split-field-year', true).first(), null);
32794     },
32795     
32796     setValue : function(v, format)
32797     {
32798         this.inputEl.dom.value = v;
32799         
32800         var f = format || (this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat);
32801         
32802         var d = Date.parseDate(v, f);
32803         
32804         if(!d){
32805             this.validate();
32806             return;
32807         }
32808         
32809         this.setDay(d.format(this.dayFormat));
32810         this.setMonth(d.format(this.monthFormat));
32811         this.setYear(d.format(this.yearFormat));
32812         
32813         this.validate();
32814         
32815         return;
32816     },
32817     
32818     setDay : function(v)
32819     {
32820         this.dayField.setValue(v);
32821         this.inputEl.dom.value = this.getValue();
32822         this.validate();
32823         return;
32824     },
32825     
32826     setMonth : function(v)
32827     {
32828         this.monthField.setValue(v, true);
32829         this.inputEl.dom.value = this.getValue();
32830         this.validate();
32831         return;
32832     },
32833     
32834     setYear : function(v)
32835     {
32836         this.yearField.setValue(v);
32837         this.inputEl.dom.value = this.getValue();
32838         this.validate();
32839         return;
32840     },
32841     
32842     getDay : function()
32843     {
32844         return this.dayField.getValue();
32845     },
32846     
32847     getMonth : function()
32848     {
32849         return this.monthField.getValue();
32850     },
32851     
32852     getYear : function()
32853     {
32854         return this.yearField.getValue();
32855     },
32856     
32857     getValue : function()
32858     {
32859         var f = this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat;
32860         
32861         var date = this.yearField.getValue() + '-' + this.monthField.getValue() + '-' + this.dayField.getValue();
32862         
32863         return date;
32864     },
32865     
32866     reset : function()
32867     {
32868         this.setDay('');
32869         this.setMonth('');
32870         this.setYear('');
32871         this.inputEl.dom.value = '';
32872         this.validate();
32873         return;
32874     },
32875     
32876     validate : function()
32877     {
32878         var d = this.dayField.validate();
32879         var m = this.monthField.validate();
32880         var y = this.yearField.validate();
32881         
32882         var valid = true;
32883         
32884         if(
32885                 (!this.dayAllowBlank && !d) ||
32886                 (!this.monthAllowBlank && !m) ||
32887                 (!this.yearAllowBlank && !y)
32888         ){
32889             valid = false;
32890         }
32891         
32892         if(this.dayAllowBlank && this.monthAllowBlank && this.yearAllowBlank){
32893             return valid;
32894         }
32895         
32896         if(valid){
32897             this.markValid();
32898             return valid;
32899         }
32900         
32901         this.markInvalid();
32902         
32903         return valid;
32904     },
32905     
32906     markValid : function()
32907     {
32908         
32909         var label = this.el.select('label', true).first();
32910         var icon = this.el.select('i.fa-star', true).first();
32911
32912         if(label && icon){
32913             icon.remove();
32914         }
32915         
32916         this.fireEvent('valid', this);
32917     },
32918     
32919      /**
32920      * Mark this field as invalid
32921      * @param {String} msg The validation message
32922      */
32923     markInvalid : function(msg)
32924     {
32925         
32926         var label = this.el.select('label', true).first();
32927         var icon = this.el.select('i.fa-star', true).first();
32928
32929         if(label && !icon){
32930             this.el.select('.roo-date-split-field-label', true).createChild({
32931                 tag : 'i',
32932                 cls : 'text-danger fa fa-lg fa-star',
32933                 tooltip : 'This field is required',
32934                 style : 'margin-right:5px;'
32935             }, label, true);
32936         }
32937         
32938         this.fireEvent('invalid', this, msg);
32939     },
32940     
32941     clearInvalid : function()
32942     {
32943         var label = this.el.select('label', true).first();
32944         var icon = this.el.select('i.fa-star', true).first();
32945
32946         if(label && icon){
32947             icon.remove();
32948         }
32949         
32950         this.fireEvent('valid', this);
32951     },
32952     
32953     getName: function()
32954     {
32955         return this.name;
32956     }
32957     
32958 });
32959
32960  /**
32961  *
32962  * This is based on 
32963  * http://masonry.desandro.com
32964  *
32965  * The idea is to render all the bricks based on vertical width...
32966  *
32967  * The original code extends 'outlayer' - we might need to use that....
32968  * 
32969  */
32970
32971
32972 /**
32973  * @class Roo.bootstrap.LayoutMasonry
32974  * @extends Roo.bootstrap.Component
32975  * Bootstrap Layout Masonry class
32976  * 
32977  * @constructor
32978  * Create a new Element
32979  * @param {Object} config The config object
32980  */
32981
32982 Roo.bootstrap.LayoutMasonry = function(config){
32983     
32984     Roo.bootstrap.LayoutMasonry.superclass.constructor.call(this, config);
32985     
32986     this.bricks = [];
32987     
32988     Roo.bootstrap.LayoutMasonry.register(this);
32989     
32990     this.addEvents({
32991         // raw events
32992         /**
32993          * @event layout
32994          * Fire after layout the items
32995          * @param {Roo.bootstrap.LayoutMasonry} this
32996          * @param {Roo.EventObject} e
32997          */
32998         "layout" : true
32999     });
33000     
33001 };
33002
33003 Roo.extend(Roo.bootstrap.LayoutMasonry, Roo.bootstrap.Component,  {
33004     
33005     /**
33006      * @cfg {Boolean} isLayoutInstant = no animation?
33007      */   
33008     isLayoutInstant : false, // needed?
33009    
33010     /**
33011      * @cfg {Number} boxWidth  width of the columns
33012      */   
33013     boxWidth : 450,
33014     
33015       /**
33016      * @cfg {Number} boxHeight  - 0 for square, or fix it at a certian height
33017      */   
33018     boxHeight : 0,
33019     
33020     /**
33021      * @cfg {Number} padWidth padding below box..
33022      */   
33023     padWidth : 10, 
33024     
33025     /**
33026      * @cfg {Number} gutter gutter width..
33027      */   
33028     gutter : 10,
33029     
33030      /**
33031      * @cfg {Number} maxCols maximum number of columns
33032      */   
33033     
33034     maxCols: 0,
33035     
33036     /**
33037      * @cfg {Boolean} isAutoInitial defalut true
33038      */   
33039     isAutoInitial : true, 
33040     
33041     containerWidth: 0,
33042     
33043     /**
33044      * @cfg {Boolean} isHorizontal defalut false
33045      */   
33046     isHorizontal : false, 
33047
33048     currentSize : null,
33049     
33050     tag: 'div',
33051     
33052     cls: '',
33053     
33054     bricks: null, //CompositeElement
33055     
33056     cols : 1,
33057     
33058     _isLayoutInited : false,
33059     
33060 //    isAlternative : false, // only use for vertical layout...
33061     
33062     /**
33063      * @cfg {Number} alternativePadWidth padding below box..
33064      */   
33065     alternativePadWidth : 50,
33066     
33067     selectedBrick : [],
33068     
33069     getAutoCreate : function(){
33070         
33071         var cfg = Roo.apply({}, Roo.bootstrap.LayoutMasonry.superclass.getAutoCreate.call(this));
33072         
33073         var cfg = {
33074             tag: this.tag,
33075             cls: 'blog-masonary-wrapper ' + this.cls,
33076             cn : {
33077                 cls : 'mas-boxes masonary'
33078             }
33079         };
33080         
33081         return cfg;
33082     },
33083     
33084     getChildContainer: function( )
33085     {
33086         if (this.boxesEl) {
33087             return this.boxesEl;
33088         }
33089         
33090         this.boxesEl = this.el.select('.mas-boxes').first();
33091         
33092         return this.boxesEl;
33093     },
33094     
33095     
33096     initEvents : function()
33097     {
33098         var _this = this;
33099         
33100         if(this.isAutoInitial){
33101             Roo.log('hook children rendered');
33102             this.on('childrenrendered', function() {
33103                 Roo.log('children rendered');
33104                 _this.initial();
33105             } ,this);
33106         }
33107     },
33108     
33109     initial : function()
33110     {
33111         this.selectedBrick = [];
33112         
33113         this.currentSize = this.el.getBox(true);
33114         
33115         Roo.EventManager.onWindowResize(this.resize, this); 
33116
33117         if(!this.isAutoInitial){
33118             this.layout();
33119             return;
33120         }
33121         
33122         this.layout();
33123         
33124         return;
33125         //this.layout.defer(500,this);
33126         
33127     },
33128     
33129     resize : function()
33130     {
33131         var cs = this.el.getBox(true);
33132         
33133         if (
33134                 this.currentSize.width == cs.width && 
33135                 this.currentSize.x == cs.x && 
33136                 this.currentSize.height == cs.height && 
33137                 this.currentSize.y == cs.y 
33138         ) {
33139             Roo.log("no change in with or X or Y");
33140             return;
33141         }
33142         
33143         this.currentSize = cs;
33144         
33145         this.layout();
33146         
33147     },
33148     
33149     layout : function()
33150     {   
33151         this._resetLayout();
33152         
33153         var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
33154         
33155         this.layoutItems( isInstant );
33156       
33157         this._isLayoutInited = true;
33158         
33159         this.fireEvent('layout', this);
33160         
33161     },
33162     
33163     _resetLayout : function()
33164     {
33165         if(this.isHorizontal){
33166             this.horizontalMeasureColumns();
33167             return;
33168         }
33169         
33170         this.verticalMeasureColumns();
33171         
33172     },
33173     
33174     verticalMeasureColumns : function()
33175     {
33176         this.getContainerWidth();
33177         
33178 //        if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
33179 //            this.colWidth = Math.floor(this.containerWidth * 0.8);
33180 //            return;
33181 //        }
33182         
33183         var boxWidth = this.boxWidth + this.padWidth;
33184         
33185         if(this.containerWidth < this.boxWidth){
33186             boxWidth = this.containerWidth
33187         }
33188         
33189         var containerWidth = this.containerWidth;
33190         
33191         var cols = Math.floor(containerWidth / boxWidth);
33192         
33193         this.cols = Math.max( cols, 1 );
33194         
33195         this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
33196         
33197         var totalBoxWidth = this.cols * boxWidth - this.padWidth;
33198         
33199         var avail = Math.floor((containerWidth - totalBoxWidth) / this.cols);
33200         
33201         this.colWidth = boxWidth + avail - this.padWidth;
33202         
33203         this.unitWidth = Math.round((this.colWidth - (this.gutter * 2)) / 3);
33204         this.unitHeight = this.boxHeight > 0 ? this.boxHeight  : this.unitWidth;
33205     },
33206     
33207     horizontalMeasureColumns : function()
33208     {
33209         this.getContainerWidth();
33210         
33211         var boxWidth = this.boxWidth;
33212         
33213         if(this.containerWidth < boxWidth){
33214             boxWidth = this.containerWidth;
33215         }
33216         
33217         this.unitWidth = Math.floor((boxWidth - (this.gutter * 2)) / 3);
33218         
33219         this.el.setHeight(boxWidth);
33220         
33221     },
33222     
33223     getContainerWidth : function()
33224     {
33225         this.containerWidth = this.el.getBox(true).width;  //maybe use getComputedWidth
33226     },
33227     
33228     layoutItems : function( isInstant )
33229     {
33230         Roo.log(this.bricks);
33231         
33232         var items = Roo.apply([], this.bricks);
33233         
33234         if(this.isHorizontal){
33235             this._horizontalLayoutItems( items , isInstant );
33236             return;
33237         }
33238         
33239 //        if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
33240 //            this._verticalAlternativeLayoutItems( items , isInstant );
33241 //            return;
33242 //        }
33243         
33244         this._verticalLayoutItems( items , isInstant );
33245         
33246     },
33247     
33248     _verticalLayoutItems : function ( items , isInstant)
33249     {
33250         if ( !items || !items.length ) {
33251             return;
33252         }
33253         
33254         var standard = [
33255             ['xs', 'xs', 'xs', 'tall'],
33256             ['xs', 'xs', 'tall'],
33257             ['xs', 'xs', 'sm'],
33258             ['xs', 'xs', 'xs'],
33259             ['xs', 'tall'],
33260             ['xs', 'sm'],
33261             ['xs', 'xs'],
33262             ['xs'],
33263             
33264             ['sm', 'xs', 'xs'],
33265             ['sm', 'xs'],
33266             ['sm'],
33267             
33268             ['tall', 'xs', 'xs', 'xs'],
33269             ['tall', 'xs', 'xs'],
33270             ['tall', 'xs'],
33271             ['tall']
33272             
33273         ];
33274         
33275         var queue = [];
33276         
33277         var boxes = [];
33278         
33279         var box = [];
33280         
33281         Roo.each(items, function(item, k){
33282             
33283             switch (item.size) {
33284                 // these layouts take up a full box,
33285                 case 'md' :
33286                 case 'md-left' :
33287                 case 'md-right' :
33288                 case 'wide' :
33289                     
33290                     if(box.length){
33291                         boxes.push(box);
33292                         box = [];
33293                     }
33294                     
33295                     boxes.push([item]);
33296                     
33297                     break;
33298                     
33299                 case 'xs' :
33300                 case 'sm' :
33301                 case 'tall' :
33302                     
33303                     box.push(item);
33304                     
33305                     break;
33306                 default :
33307                     break;
33308                     
33309             }
33310             
33311         }, this);
33312         
33313         if(box.length){
33314             boxes.push(box);
33315             box = [];
33316         }
33317         
33318         var filterPattern = function(box, length)
33319         {
33320             if(!box.length){
33321                 return;
33322             }
33323             
33324             var match = false;
33325             
33326             var pattern = box.slice(0, length);
33327             
33328             var format = [];
33329             
33330             Roo.each(pattern, function(i){
33331                 format.push(i.size);
33332             }, this);
33333             
33334             Roo.each(standard, function(s){
33335                 
33336                 if(String(s) != String(format)){
33337                     return;
33338                 }
33339                 
33340                 match = true;
33341                 return false;
33342                 
33343             }, this);
33344             
33345             if(!match && length == 1){
33346                 return;
33347             }
33348             
33349             if(!match){
33350                 filterPattern(box, length - 1);
33351                 return;
33352             }
33353                 
33354             queue.push(pattern);
33355
33356             box = box.slice(length, box.length);
33357
33358             filterPattern(box, 4);
33359
33360             return;
33361             
33362         }
33363         
33364         Roo.each(boxes, function(box, k){
33365             
33366             if(!box.length){
33367                 return;
33368             }
33369             
33370             if(box.length == 1){
33371                 queue.push(box);
33372                 return;
33373             }
33374             
33375             filterPattern(box, 4);
33376             
33377         }, this);
33378         
33379         this._processVerticalLayoutQueue( queue, isInstant );
33380         
33381     },
33382     
33383 //    _verticalAlternativeLayoutItems : function( items , isInstant )
33384 //    {
33385 //        if ( !items || !items.length ) {
33386 //            return;
33387 //        }
33388 //
33389 //        this._processVerticalAlternativeLayoutQueue( items, isInstant );
33390 //        
33391 //    },
33392     
33393     _horizontalLayoutItems : function ( items , isInstant)
33394     {
33395         if ( !items || !items.length || items.length < 3) {
33396             return;
33397         }
33398         
33399         items.reverse();
33400         
33401         var eItems = items.slice(0, 3);
33402         
33403         items = items.slice(3, items.length);
33404         
33405         var standard = [
33406             ['xs', 'xs', 'xs', 'wide'],
33407             ['xs', 'xs', 'wide'],
33408             ['xs', 'xs', 'sm'],
33409             ['xs', 'xs', 'xs'],
33410             ['xs', 'wide'],
33411             ['xs', 'sm'],
33412             ['xs', 'xs'],
33413             ['xs'],
33414             
33415             ['sm', 'xs', 'xs'],
33416             ['sm', 'xs'],
33417             ['sm'],
33418             
33419             ['wide', 'xs', 'xs', 'xs'],
33420             ['wide', 'xs', 'xs'],
33421             ['wide', 'xs'],
33422             ['wide'],
33423             
33424             ['wide-thin']
33425         ];
33426         
33427         var queue = [];
33428         
33429         var boxes = [];
33430         
33431         var box = [];
33432         
33433         Roo.each(items, function(item, k){
33434             
33435             switch (item.size) {
33436                 case 'md' :
33437                 case 'md-left' :
33438                 case 'md-right' :
33439                 case 'tall' :
33440                     
33441                     if(box.length){
33442                         boxes.push(box);
33443                         box = [];
33444                     }
33445                     
33446                     boxes.push([item]);
33447                     
33448                     break;
33449                     
33450                 case 'xs' :
33451                 case 'sm' :
33452                 case 'wide' :
33453                 case 'wide-thin' :
33454                     
33455                     box.push(item);
33456                     
33457                     break;
33458                 default :
33459                     break;
33460                     
33461             }
33462             
33463         }, this);
33464         
33465         if(box.length){
33466             boxes.push(box);
33467             box = [];
33468         }
33469         
33470         var filterPattern = function(box, length)
33471         {
33472             if(!box.length){
33473                 return;
33474             }
33475             
33476             var match = false;
33477             
33478             var pattern = box.slice(0, length);
33479             
33480             var format = [];
33481             
33482             Roo.each(pattern, function(i){
33483                 format.push(i.size);
33484             }, this);
33485             
33486             Roo.each(standard, function(s){
33487                 
33488                 if(String(s) != String(format)){
33489                     return;
33490                 }
33491                 
33492                 match = true;
33493                 return false;
33494                 
33495             }, this);
33496             
33497             if(!match && length == 1){
33498                 return;
33499             }
33500             
33501             if(!match){
33502                 filterPattern(box, length - 1);
33503                 return;
33504             }
33505                 
33506             queue.push(pattern);
33507
33508             box = box.slice(length, box.length);
33509
33510             filterPattern(box, 4);
33511
33512             return;
33513             
33514         }
33515         
33516         Roo.each(boxes, function(box, k){
33517             
33518             if(!box.length){
33519                 return;
33520             }
33521             
33522             if(box.length == 1){
33523                 queue.push(box);
33524                 return;
33525             }
33526             
33527             filterPattern(box, 4);
33528             
33529         }, this);
33530         
33531         
33532         var prune = [];
33533         
33534         var pos = this.el.getBox(true);
33535         
33536         var minX = pos.x;
33537         
33538         var maxX = pos.right - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
33539         
33540         var hit_end = false;
33541         
33542         Roo.each(queue, function(box){
33543             
33544             if(hit_end){
33545                 
33546                 Roo.each(box, function(b){
33547                 
33548                     b.el.setVisibilityMode(Roo.Element.DISPLAY);
33549                     b.el.hide();
33550
33551                 }, this);
33552
33553                 return;
33554             }
33555             
33556             var mx = 0;
33557             
33558             Roo.each(box, function(b){
33559                 
33560                 b.el.setVisibilityMode(Roo.Element.DISPLAY);
33561                 b.el.show();
33562
33563                 mx = Math.max(mx, b.x);
33564                 
33565             }, this);
33566             
33567             maxX = maxX - this.unitWidth * mx - this.gutter * (mx - 1) - this.padWidth;
33568             
33569             if(maxX < minX){
33570                 
33571                 Roo.each(box, function(b){
33572                 
33573                     b.el.setVisibilityMode(Roo.Element.DISPLAY);
33574                     b.el.hide();
33575                     
33576                 }, this);
33577                 
33578                 hit_end = true;
33579                 
33580                 return;
33581             }
33582             
33583             prune.push(box);
33584             
33585         }, this);
33586         
33587         this._processHorizontalLayoutQueue( prune, eItems, isInstant );
33588     },
33589     
33590     /** Sets position of item in DOM
33591     * @param {Element} item
33592     * @param {Number} x - horizontal position
33593     * @param {Number} y - vertical position
33594     * @param {Boolean} isInstant - disables transitions
33595     */
33596     _processVerticalLayoutQueue : function( queue, isInstant )
33597     {
33598         var pos = this.el.getBox(true);
33599         var x = pos.x;
33600         var y = pos.y;
33601         var maxY = [];
33602         
33603         for (var i = 0; i < this.cols; i++){
33604             maxY[i] = pos.y;
33605         }
33606         
33607         Roo.each(queue, function(box, k){
33608             
33609             var col = k % this.cols;
33610             
33611             Roo.each(box, function(b,kk){
33612                 
33613                 b.el.position('absolute');
33614                 
33615                 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
33616                 var height = Math.floor(this.unitHeight * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
33617                 
33618                 if(b.size == 'md-left' || b.size == 'md-right'){
33619                     width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
33620                     height = Math.floor(this.unitHeight * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
33621                 }
33622                 
33623                 b.el.setWidth(width);
33624                 b.el.setHeight(height);
33625                 // iframe?
33626                 b.el.select('iframe',true).setSize(width,height);
33627                 
33628             }, this);
33629             
33630             for (var i = 0; i < this.cols; i++){
33631                 
33632                 if(maxY[i] < maxY[col]){
33633                     col = i;
33634                     continue;
33635                 }
33636                 
33637                 col = Math.min(col, i);
33638                 
33639             }
33640             
33641             x = pos.x + col * (this.colWidth + this.padWidth);
33642             
33643             y = maxY[col];
33644             
33645             var positions = [];
33646             
33647             switch (box.length){
33648                 case 1 :
33649                     positions = this.getVerticalOneBoxColPositions(x, y, box);
33650                     break;
33651                 case 2 :
33652                     positions = this.getVerticalTwoBoxColPositions(x, y, box);
33653                     break;
33654                 case 3 :
33655                     positions = this.getVerticalThreeBoxColPositions(x, y, box);
33656                     break;
33657                 case 4 :
33658                     positions = this.getVerticalFourBoxColPositions(x, y, box);
33659                     break;
33660                 default :
33661                     break;
33662             }
33663             
33664             Roo.each(box, function(b,kk){
33665                 
33666                 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
33667                 
33668                 var sz = b.el.getSize();
33669                 
33670                 maxY[col] = Math.max(maxY[col], positions[kk].y + sz.height + this.padWidth);
33671                 
33672             }, this);
33673             
33674         }, this);
33675         
33676         var mY = 0;
33677         
33678         for (var i = 0; i < this.cols; i++){
33679             mY = Math.max(mY, maxY[i]);
33680         }
33681         
33682         this.el.setHeight(mY - pos.y);
33683         
33684     },
33685     
33686 //    _processVerticalAlternativeLayoutQueue : function( items, isInstant )
33687 //    {
33688 //        var pos = this.el.getBox(true);
33689 //        var x = pos.x;
33690 //        var y = pos.y;
33691 //        var maxX = pos.right;
33692 //        
33693 //        var maxHeight = 0;
33694 //        
33695 //        Roo.each(items, function(item, k){
33696 //            
33697 //            var c = k % 2;
33698 //            
33699 //            item.el.position('absolute');
33700 //                
33701 //            var width = Math.floor(this.colWidth + item.el.getPadding('lr'));
33702 //
33703 //            item.el.setWidth(width);
33704 //
33705 //            var height = Math.floor(this.colWidth * item.y / item.x + item.el.getPadding('tb'));
33706 //
33707 //            item.el.setHeight(height);
33708 //            
33709 //            if(c == 0){
33710 //                item.el.setXY([x, y], isInstant ? false : true);
33711 //            } else {
33712 //                item.el.setXY([maxX - width, y], isInstant ? false : true);
33713 //            }
33714 //            
33715 //            y = y + height + this.alternativePadWidth;
33716 //            
33717 //            maxHeight = maxHeight + height + this.alternativePadWidth;
33718 //            
33719 //        }, this);
33720 //        
33721 //        this.el.setHeight(maxHeight);
33722 //        
33723 //    },
33724     
33725     _processHorizontalLayoutQueue : function( queue, eItems, isInstant )
33726     {
33727         var pos = this.el.getBox(true);
33728         
33729         var minX = pos.x;
33730         var minY = pos.y;
33731         
33732         var maxX = pos.right;
33733         
33734         this._processHorizontalEndItem(eItems, maxX, minX, minY, isInstant);
33735         
33736         var maxX = maxX - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
33737         
33738         Roo.each(queue, function(box, k){
33739             
33740             Roo.each(box, function(b, kk){
33741                 
33742                 b.el.position('absolute');
33743                 
33744                 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
33745                 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
33746                 
33747                 if(b.size == 'md-left' || b.size == 'md-right'){
33748                     width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
33749                     height = Math.floor(this.unitWidth * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
33750                 }
33751                 
33752                 b.el.setWidth(width);
33753                 b.el.setHeight(height);
33754                 
33755             }, this);
33756             
33757             if(!box.length){
33758                 return;
33759             }
33760             
33761             var positions = [];
33762             
33763             switch (box.length){
33764                 case 1 :
33765                     positions = this.getHorizontalOneBoxColPositions(maxX, minY, box);
33766                     break;
33767                 case 2 :
33768                     positions = this.getHorizontalTwoBoxColPositions(maxX, minY, box);
33769                     break;
33770                 case 3 :
33771                     positions = this.getHorizontalThreeBoxColPositions(maxX, minY, box);
33772                     break;
33773                 case 4 :
33774                     positions = this.getHorizontalFourBoxColPositions(maxX, minY, box);
33775                     break;
33776                 default :
33777                     break;
33778             }
33779             
33780             Roo.each(box, function(b,kk){
33781                 
33782                 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
33783                 
33784                 maxX = Math.min(maxX, positions[kk].x - this.padWidth);
33785                 
33786             }, this);
33787             
33788         }, this);
33789         
33790     },
33791     
33792     _processHorizontalEndItem : function(eItems, maxX, minX, minY, isInstant)
33793     {
33794         Roo.each(eItems, function(b,k){
33795             
33796             b.size = (k == 0) ? 'sm' : 'xs';
33797             b.x = (k == 0) ? 2 : 1;
33798             b.y = (k == 0) ? 2 : 1;
33799             
33800             b.el.position('absolute');
33801             
33802             var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
33803                 
33804             b.el.setWidth(width);
33805             
33806             var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
33807             
33808             b.el.setHeight(height);
33809             
33810         }, this);
33811
33812         var positions = [];
33813         
33814         positions.push({
33815             x : maxX - this.unitWidth * 2 - this.gutter,
33816             y : minY
33817         });
33818         
33819         positions.push({
33820             x : maxX - this.unitWidth,
33821             y : minY + (this.unitWidth + this.gutter) * 2
33822         });
33823         
33824         positions.push({
33825             x : maxX - this.unitWidth * 3 - this.gutter * 2,
33826             y : minY
33827         });
33828         
33829         Roo.each(eItems, function(b,k){
33830             
33831             b.el.setXY([positions[k].x, positions[k].y], isInstant ? false : true);
33832
33833         }, this);
33834         
33835     },
33836     
33837     getVerticalOneBoxColPositions : function(x, y, box)
33838     {
33839         var pos = [];
33840         
33841         var rand = Math.floor(Math.random() * ((4 - box[0].x)));
33842         
33843         if(box[0].size == 'md-left'){
33844             rand = 0;
33845         }
33846         
33847         if(box[0].size == 'md-right'){
33848             rand = 1;
33849         }
33850         
33851         pos.push({
33852             x : x + (this.unitWidth + this.gutter) * rand,
33853             y : y
33854         });
33855         
33856         return pos;
33857     },
33858     
33859     getVerticalTwoBoxColPositions : function(x, y, box)
33860     {
33861         var pos = [];
33862         
33863         if(box[0].size == 'xs'){
33864             
33865             pos.push({
33866                 x : x,
33867                 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[1].y))
33868             });
33869
33870             pos.push({
33871                 x : x + (this.unitWidth + this.gutter) * (3 - box[1].x),
33872                 y : y
33873             });
33874             
33875             return pos;
33876             
33877         }
33878         
33879         pos.push({
33880             x : x,
33881             y : y
33882         });
33883
33884         pos.push({
33885             x : x + (this.unitWidth + this.gutter) * 2,
33886             y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[0].y))
33887         });
33888         
33889         return pos;
33890         
33891     },
33892     
33893     getVerticalThreeBoxColPositions : function(x, y, box)
33894     {
33895         var pos = [];
33896         
33897         if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
33898             
33899             pos.push({
33900                 x : x,
33901                 y : y
33902             });
33903
33904             pos.push({
33905                 x : x + (this.unitWidth + this.gutter) * 1,
33906                 y : y
33907             });
33908             
33909             pos.push({
33910                 x : x + (this.unitWidth + this.gutter) * 2,
33911                 y : y
33912             });
33913             
33914             return pos;
33915             
33916         }
33917         
33918         if(box[0].size == 'xs' && box[1].size == 'xs'){
33919             
33920             pos.push({
33921                 x : x,
33922                 y : y
33923             });
33924
33925             pos.push({
33926                 x : x,
33927                 y : y + ((this.unitHeight + this.gutter) * (box[2].y - 1))
33928             });
33929             
33930             pos.push({
33931                 x : x + (this.unitWidth + this.gutter) * 1,
33932                 y : y
33933             });
33934             
33935             return pos;
33936             
33937         }
33938         
33939         pos.push({
33940             x : x,
33941             y : y
33942         });
33943
33944         pos.push({
33945             x : x + (this.unitWidth + this.gutter) * 2,
33946             y : y
33947         });
33948
33949         pos.push({
33950             x : x + (this.unitWidth + this.gutter) * 2,
33951             y : y + (this.unitHeight + this.gutter) * (box[0].y - 1)
33952         });
33953             
33954         return pos;
33955         
33956     },
33957     
33958     getVerticalFourBoxColPositions : function(x, y, box)
33959     {
33960         var pos = [];
33961         
33962         if(box[0].size == 'xs'){
33963             
33964             pos.push({
33965                 x : x,
33966                 y : y
33967             });
33968
33969             pos.push({
33970                 x : x,
33971                 y : y + (this.unitHeight + this.gutter) * 1
33972             });
33973             
33974             pos.push({
33975                 x : x,
33976                 y : y + (this.unitHeight + this.gutter) * 2
33977             });
33978             
33979             pos.push({
33980                 x : x + (this.unitWidth + this.gutter) * 1,
33981                 y : y
33982             });
33983             
33984             return pos;
33985             
33986         }
33987         
33988         pos.push({
33989             x : x,
33990             y : y
33991         });
33992
33993         pos.push({
33994             x : x + (this.unitWidth + this.gutter) * 2,
33995             y : y
33996         });
33997
33998         pos.push({
33999             x : x + (this.unitHeightunitWidth + this.gutter) * 2,
34000             y : y + (this.unitHeight + this.gutter) * 1
34001         });
34002
34003         pos.push({
34004             x : x + (this.unitWidth + this.gutter) * 2,
34005             y : y + (this.unitWidth + this.gutter) * 2
34006         });
34007
34008         return pos;
34009         
34010     },
34011     
34012     getHorizontalOneBoxColPositions : function(maxX, minY, box)
34013     {
34014         var pos = [];
34015         
34016         if(box[0].size == 'md-left'){
34017             pos.push({
34018                 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
34019                 y : minY
34020             });
34021             
34022             return pos;
34023         }
34024         
34025         if(box[0].size == 'md-right'){
34026             pos.push({
34027                 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
34028                 y : minY + (this.unitWidth + this.gutter) * 1
34029             });
34030             
34031             return pos;
34032         }
34033         
34034         var rand = Math.floor(Math.random() * (4 - box[0].y));
34035         
34036         pos.push({
34037             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34038             y : minY + (this.unitWidth + this.gutter) * rand
34039         });
34040         
34041         return pos;
34042         
34043     },
34044     
34045     getHorizontalTwoBoxColPositions : function(maxX, minY, box)
34046     {
34047         var pos = [];
34048         
34049         if(box[0].size == 'xs'){
34050             
34051             pos.push({
34052                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34053                 y : minY
34054             });
34055
34056             pos.push({
34057                 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34058                 y : minY + (this.unitWidth + this.gutter) * (3 - box[1].y)
34059             });
34060             
34061             return pos;
34062             
34063         }
34064         
34065         pos.push({
34066             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34067             y : minY
34068         });
34069
34070         pos.push({
34071             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34072             y : minY + (this.unitWidth + this.gutter) * 2
34073         });
34074         
34075         return pos;
34076         
34077     },
34078     
34079     getHorizontalThreeBoxColPositions : function(maxX, minY, box)
34080     {
34081         var pos = [];
34082         
34083         if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
34084             
34085             pos.push({
34086                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34087                 y : minY
34088             });
34089
34090             pos.push({
34091                 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34092                 y : minY + (this.unitWidth + this.gutter) * 1
34093             });
34094             
34095             pos.push({
34096                 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
34097                 y : minY + (this.unitWidth + this.gutter) * 2
34098             });
34099             
34100             return pos;
34101             
34102         }
34103         
34104         if(box[0].size == 'xs' && box[1].size == 'xs'){
34105             
34106             pos.push({
34107                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34108                 y : minY
34109             });
34110
34111             pos.push({
34112                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34113                 y : minY
34114             });
34115             
34116             pos.push({
34117                 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
34118                 y : minY + (this.unitWidth + this.gutter) * 1
34119             });
34120             
34121             return pos;
34122             
34123         }
34124         
34125         pos.push({
34126             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34127             y : minY
34128         });
34129
34130         pos.push({
34131             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34132             y : minY + (this.unitWidth + this.gutter) * 2
34133         });
34134
34135         pos.push({
34136             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
34137             y : minY + (this.unitWidth + this.gutter) * 2
34138         });
34139             
34140         return pos;
34141         
34142     },
34143     
34144     getHorizontalFourBoxColPositions : function(maxX, minY, box)
34145     {
34146         var pos = [];
34147         
34148         if(box[0].size == 'xs'){
34149             
34150             pos.push({
34151                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34152                 y : minY
34153             });
34154
34155             pos.push({
34156                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34157                 y : minY
34158             });
34159             
34160             pos.push({
34161                 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),
34162                 y : minY
34163             });
34164             
34165             pos.push({
34166                 x : maxX - this.unitWidth * box[3].x - this.gutter * (box[3].x - 1),
34167                 y : minY + (this.unitWidth + this.gutter) * 1
34168             });
34169             
34170             return pos;
34171             
34172         }
34173         
34174         pos.push({
34175             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34176             y : minY
34177         });
34178         
34179         pos.push({
34180             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34181             y : minY + (this.unitWidth + this.gutter) * 2
34182         });
34183         
34184         pos.push({
34185             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
34186             y : minY + (this.unitWidth + this.gutter) * 2
34187         });
34188         
34189         pos.push({
34190             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),
34191             y : minY + (this.unitWidth + this.gutter) * 2
34192         });
34193
34194         return pos;
34195         
34196     },
34197     
34198     /**
34199     * remove a Masonry Brick
34200     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to remove
34201     */
34202     removeBrick : function(brick_id)
34203     {
34204         if (!brick_id) {
34205             return;
34206         }
34207         
34208         for (var i = 0; i<this.bricks.length; i++) {
34209             if (this.bricks[i].id == brick_id) {
34210                 this.bricks.splice(i,1);
34211                 this.el.dom.removeChild(Roo.get(brick_id).dom);
34212                 this.initial();
34213             }
34214         }
34215     },
34216     
34217     /**
34218     * adds a Masonry Brick
34219     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
34220     */
34221     addBrick : function(cfg)
34222     {
34223         var cn = new Roo.bootstrap.MasonryBrick(cfg);
34224         //this.register(cn);
34225         cn.parentId = this.id;
34226         cn.render(this.el);
34227         return cn;
34228     },
34229     
34230     /**
34231     * register a Masonry Brick
34232     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
34233     */
34234     
34235     register : function(brick)
34236     {
34237         this.bricks.push(brick);
34238         brick.masonryId = this.id;
34239     },
34240     
34241     /**
34242     * clear all the Masonry Brick
34243     */
34244     clearAll : function()
34245     {
34246         this.bricks = [];
34247         //this.getChildContainer().dom.innerHTML = "";
34248         this.el.dom.innerHTML = '';
34249     },
34250     
34251     getSelected : function()
34252     {
34253         if (!this.selectedBrick) {
34254             return false;
34255         }
34256         
34257         return this.selectedBrick;
34258     }
34259 });
34260
34261 Roo.apply(Roo.bootstrap.LayoutMasonry, {
34262     
34263     groups: {},
34264      /**
34265     * register a Masonry Layout
34266     * @param {Roo.bootstrap.LayoutMasonry} the masonry layout to add
34267     */
34268     
34269     register : function(layout)
34270     {
34271         this.groups[layout.id] = layout;
34272     },
34273     /**
34274     * fetch a  Masonry Layout based on the masonry layout ID
34275     * @param {string} the masonry layout to add
34276     * @returns {Roo.bootstrap.LayoutMasonry} the masonry layout
34277     */
34278     
34279     get: function(layout_id) {
34280         if (typeof(this.groups[layout_id]) == 'undefined') {
34281             return false;
34282         }
34283         return this.groups[layout_id] ;
34284     }
34285     
34286     
34287     
34288 });
34289
34290  
34291
34292  /**
34293  *
34294  * This is based on 
34295  * http://masonry.desandro.com
34296  *
34297  * The idea is to render all the bricks based on vertical width...
34298  *
34299  * The original code extends 'outlayer' - we might need to use that....
34300  * 
34301  */
34302
34303
34304 /**
34305  * @class Roo.bootstrap.LayoutMasonryAuto
34306  * @extends Roo.bootstrap.Component
34307  * Bootstrap Layout Masonry class
34308  * 
34309  * @constructor
34310  * Create a new Element
34311  * @param {Object} config The config object
34312  */
34313
34314 Roo.bootstrap.LayoutMasonryAuto = function(config){
34315     Roo.bootstrap.LayoutMasonryAuto.superclass.constructor.call(this, config);
34316 };
34317
34318 Roo.extend(Roo.bootstrap.LayoutMasonryAuto, Roo.bootstrap.Component,  {
34319     
34320       /**
34321      * @cfg {Boolean} isFitWidth  - resize the width..
34322      */   
34323     isFitWidth : false,  // options..
34324     /**
34325      * @cfg {Boolean} isOriginLeft = left align?
34326      */   
34327     isOriginLeft : true,
34328     /**
34329      * @cfg {Boolean} isOriginTop = top align?
34330      */   
34331     isOriginTop : false,
34332     /**
34333      * @cfg {Boolean} isLayoutInstant = no animation?
34334      */   
34335     isLayoutInstant : false, // needed?
34336     /**
34337      * @cfg {Boolean} isResizingContainer = not sure if this is used..
34338      */   
34339     isResizingContainer : true,
34340     /**
34341      * @cfg {Number} columnWidth  width of the columns 
34342      */   
34343     
34344     columnWidth : 0,
34345     
34346     /**
34347      * @cfg {Number} maxCols maximum number of columns
34348      */   
34349     
34350     maxCols: 0,
34351     /**
34352      * @cfg {Number} padHeight padding below box..
34353      */   
34354     
34355     padHeight : 10, 
34356     
34357     /**
34358      * @cfg {Boolean} isAutoInitial defalut true
34359      */   
34360     
34361     isAutoInitial : true, 
34362     
34363     // private?
34364     gutter : 0,
34365     
34366     containerWidth: 0,
34367     initialColumnWidth : 0,
34368     currentSize : null,
34369     
34370     colYs : null, // array.
34371     maxY : 0,
34372     padWidth: 10,
34373     
34374     
34375     tag: 'div',
34376     cls: '',
34377     bricks: null, //CompositeElement
34378     cols : 0, // array?
34379     // element : null, // wrapped now this.el
34380     _isLayoutInited : null, 
34381     
34382     
34383     getAutoCreate : function(){
34384         
34385         var cfg = {
34386             tag: this.tag,
34387             cls: 'blog-masonary-wrapper ' + this.cls,
34388             cn : {
34389                 cls : 'mas-boxes masonary'
34390             }
34391         };
34392         
34393         return cfg;
34394     },
34395     
34396     getChildContainer: function( )
34397     {
34398         if (this.boxesEl) {
34399             return this.boxesEl;
34400         }
34401         
34402         this.boxesEl = this.el.select('.mas-boxes').first();
34403         
34404         return this.boxesEl;
34405     },
34406     
34407     
34408     initEvents : function()
34409     {
34410         var _this = this;
34411         
34412         if(this.isAutoInitial){
34413             Roo.log('hook children rendered');
34414             this.on('childrenrendered', function() {
34415                 Roo.log('children rendered');
34416                 _this.initial();
34417             } ,this);
34418         }
34419         
34420     },
34421     
34422     initial : function()
34423     {
34424         this.reloadItems();
34425
34426         this.currentSize = this.el.getBox(true);
34427
34428         /// was window resize... - let's see if this works..
34429         Roo.EventManager.onWindowResize(this.resize, this); 
34430
34431         if(!this.isAutoInitial){
34432             this.layout();
34433             return;
34434         }
34435         
34436         this.layout.defer(500,this);
34437     },
34438     
34439     reloadItems: function()
34440     {
34441         this.bricks = this.el.select('.masonry-brick', true);
34442         
34443         this.bricks.each(function(b) {
34444             //Roo.log(b.getSize());
34445             if (!b.attr('originalwidth')) {
34446                 b.attr('originalwidth',  b.getSize().width);
34447             }
34448             
34449         });
34450         
34451         Roo.log(this.bricks.elements.length);
34452     },
34453     
34454     resize : function()
34455     {
34456         Roo.log('resize');
34457         var cs = this.el.getBox(true);
34458         
34459         if (this.currentSize.width == cs.width && this.currentSize.x == cs.x ) {
34460             Roo.log("no change in with or X");
34461             return;
34462         }
34463         this.currentSize = cs;
34464         this.layout();
34465     },
34466     
34467     layout : function()
34468     {
34469          Roo.log('layout');
34470         this._resetLayout();
34471         //this._manageStamps();
34472       
34473         // don't animate first layout
34474         var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
34475         this.layoutItems( isInstant );
34476       
34477         // flag for initalized
34478         this._isLayoutInited = true;
34479     },
34480     
34481     layoutItems : function( isInstant )
34482     {
34483         //var items = this._getItemsForLayout( this.items );
34484         // original code supports filtering layout items.. we just ignore it..
34485         
34486         this._layoutItems( this.bricks , isInstant );
34487       
34488         this._postLayout();
34489     },
34490     _layoutItems : function ( items , isInstant)
34491     {
34492        //this.fireEvent( 'layout', this, items );
34493     
34494
34495         if ( !items || !items.elements.length ) {
34496           // no items, emit event with empty array
34497             return;
34498         }
34499
34500         var queue = [];
34501         items.each(function(item) {
34502             Roo.log("layout item");
34503             Roo.log(item);
34504             // get x/y object from method
34505             var position = this._getItemLayoutPosition( item );
34506             // enqueue
34507             position.item = item;
34508             position.isInstant = isInstant; // || item.isLayoutInstant; << not set yet...
34509             queue.push( position );
34510         }, this);
34511       
34512         this._processLayoutQueue( queue );
34513     },
34514     /** Sets position of item in DOM
34515     * @param {Element} item
34516     * @param {Number} x - horizontal position
34517     * @param {Number} y - vertical position
34518     * @param {Boolean} isInstant - disables transitions
34519     */
34520     _processLayoutQueue : function( queue )
34521     {
34522         for ( var i=0, len = queue.length; i < len; i++ ) {
34523             var obj = queue[i];
34524             obj.item.position('absolute');
34525             obj.item.setXY([obj.x,obj.y], obj.isInstant ? false : true);
34526         }
34527     },
34528       
34529     
34530     /**
34531     * Any logic you want to do after each layout,
34532     * i.e. size the container
34533     */
34534     _postLayout : function()
34535     {
34536         this.resizeContainer();
34537     },
34538     
34539     resizeContainer : function()
34540     {
34541         if ( !this.isResizingContainer ) {
34542             return;
34543         }
34544         var size = this._getContainerSize();
34545         if ( size ) {
34546             this.el.setSize(size.width,size.height);
34547             this.boxesEl.setSize(size.width,size.height);
34548         }
34549     },
34550     
34551     
34552     
34553     _resetLayout : function()
34554     {
34555         //this.getSize();  // -- does not really do anything.. it probably applies left/right etc. to obuject but not used
34556         this.colWidth = this.el.getWidth();
34557         //this.gutter = this.el.getWidth(); 
34558         
34559         this.measureColumns();
34560
34561         // reset column Y
34562         var i = this.cols;
34563         this.colYs = [];
34564         while (i--) {
34565             this.colYs.push( 0 );
34566         }
34567     
34568         this.maxY = 0;
34569     },
34570
34571     measureColumns : function()
34572     {
34573         this.getContainerWidth();
34574       // if columnWidth is 0, default to outerWidth of first item
34575         if ( !this.columnWidth ) {
34576             var firstItem = this.bricks.first();
34577             Roo.log(firstItem);
34578             this.columnWidth  = this.containerWidth;
34579             if (firstItem && firstItem.attr('originalwidth') ) {
34580                 this.columnWidth = 1* (firstItem.attr('originalwidth') || firstItem.getWidth());
34581             }
34582             // columnWidth fall back to item of first element
34583             Roo.log("set column width?");
34584                         this.initialColumnWidth = this.columnWidth  ;
34585
34586             // if first elem has no width, default to size of container
34587             
34588         }
34589         
34590         
34591         if (this.initialColumnWidth) {
34592             this.columnWidth = this.initialColumnWidth;
34593         }
34594         
34595         
34596             
34597         // column width is fixed at the top - however if container width get's smaller we should
34598         // reduce it...
34599         
34600         // this bit calcs how man columns..
34601             
34602         var columnWidth = this.columnWidth += this.gutter;
34603       
34604         // calculate columns
34605         var containerWidth = this.containerWidth + this.gutter;
34606         
34607         var cols = (containerWidth - this.padWidth) / (columnWidth - this.padWidth);
34608         // fix rounding errors, typically with gutters
34609         var excess = columnWidth - containerWidth % columnWidth;
34610         
34611         
34612         // if overshoot is less than a pixel, round up, otherwise floor it
34613         var mathMethod = excess && excess < 1 ? 'round' : 'floor';
34614         cols = Math[ mathMethod ]( cols );
34615         this.cols = Math.max( cols, 1 );
34616         this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
34617         
34618          // padding positioning..
34619         var totalColWidth = this.cols * this.columnWidth;
34620         var padavail = this.containerWidth - totalColWidth;
34621         // so for 2 columns - we need 3 'pads'
34622         
34623         var padNeeded = (1+this.cols) * this.padWidth;
34624         
34625         var padExtra = Math.floor((padavail - padNeeded) / this.cols);
34626         
34627         this.columnWidth += padExtra
34628         //this.padWidth = Math.floor(padavail /  ( this.cols));
34629         
34630         // adjust colum width so that padding is fixed??
34631         
34632         // we have 3 columns ... total = width * 3
34633         // we have X left over... that should be used by 
34634         
34635         //if (this.expandC) {
34636             
34637         //}
34638         
34639         
34640         
34641     },
34642     
34643     getContainerWidth : function()
34644     {
34645        /* // container is parent if fit width
34646         var container = this.isFitWidth ? this.element.parentNode : this.element;
34647         // check that this.size and size are there
34648         // IE8 triggers resize on body size change, so they might not be
34649         
34650         var size = getSize( container );  //FIXME
34651         this.containerWidth = size && size.innerWidth; //FIXME
34652         */
34653          
34654         this.containerWidth = this.el.getBox(true).width;  //maybe use getComputedWidth
34655         
34656     },
34657     
34658     _getItemLayoutPosition : function( item )  // what is item?
34659     {
34660         // we resize the item to our columnWidth..
34661       
34662         item.setWidth(this.columnWidth);
34663         item.autoBoxAdjust  = false;
34664         
34665         var sz = item.getSize();
34666  
34667         // how many columns does this brick span
34668         var remainder = this.containerWidth % this.columnWidth;
34669         
34670         var mathMethod = remainder && remainder < 1 ? 'round' : 'ceil';
34671         // round if off by 1 pixel, otherwise use ceil
34672         var colSpan = Math[ mathMethod ]( sz.width  / this.columnWidth );
34673         colSpan = Math.min( colSpan, this.cols );
34674         
34675         // normally this should be '1' as we dont' currently allow multi width columns..
34676         
34677         var colGroup = this._getColGroup( colSpan );
34678         // get the minimum Y value from the columns
34679         var minimumY = Math.min.apply( Math, colGroup );
34680         Roo.log([ 'setHeight',  minimumY, sz.height, setHeight ]);
34681         
34682         var shortColIndex = colGroup.indexOf(  minimumY ); // broken on ie8..?? probably...
34683          
34684         // position the brick
34685         var position = {
34686             x: this.currentSize.x + (this.padWidth /2) + ((this.columnWidth + this.padWidth )* shortColIndex),
34687             y: this.currentSize.y + minimumY + this.padHeight
34688         };
34689         
34690         Roo.log(position);
34691         // apply setHeight to necessary columns
34692         var setHeight = minimumY + sz.height + this.padHeight;
34693         //Roo.log([ 'setHeight',  minimumY, sz.height, setHeight ]);
34694         
34695         var setSpan = this.cols + 1 - colGroup.length;
34696         for ( var i = 0; i < setSpan; i++ ) {
34697           this.colYs[ shortColIndex + i ] = setHeight ;
34698         }
34699       
34700         return position;
34701     },
34702     
34703     /**
34704      * @param {Number} colSpan - number of columns the element spans
34705      * @returns {Array} colGroup
34706      */
34707     _getColGroup : function( colSpan )
34708     {
34709         if ( colSpan < 2 ) {
34710           // if brick spans only one column, use all the column Ys
34711           return this.colYs;
34712         }
34713       
34714         var colGroup = [];
34715         // how many different places could this brick fit horizontally
34716         var groupCount = this.cols + 1 - colSpan;
34717         // for each group potential horizontal position
34718         for ( var i = 0; i < groupCount; i++ ) {
34719           // make an array of colY values for that one group
34720           var groupColYs = this.colYs.slice( i, i + colSpan );
34721           // and get the max value of the array
34722           colGroup[i] = Math.max.apply( Math, groupColYs );
34723         }
34724         return colGroup;
34725     },
34726     /*
34727     _manageStamp : function( stamp )
34728     {
34729         var stampSize =  stamp.getSize();
34730         var offset = stamp.getBox();
34731         // get the columns that this stamp affects
34732         var firstX = this.isOriginLeft ? offset.x : offset.right;
34733         var lastX = firstX + stampSize.width;
34734         var firstCol = Math.floor( firstX / this.columnWidth );
34735         firstCol = Math.max( 0, firstCol );
34736         
34737         var lastCol = Math.floor( lastX / this.columnWidth );
34738         // lastCol should not go over if multiple of columnWidth #425
34739         lastCol -= lastX % this.columnWidth ? 0 : 1;
34740         lastCol = Math.min( this.cols - 1, lastCol );
34741         
34742         // set colYs to bottom of the stamp
34743         var stampMaxY = ( this.isOriginTop ? offset.y : offset.bottom ) +
34744             stampSize.height;
34745             
34746         for ( var i = firstCol; i <= lastCol; i++ ) {
34747           this.colYs[i] = Math.max( stampMaxY, this.colYs[i] );
34748         }
34749     },
34750     */
34751     
34752     _getContainerSize : function()
34753     {
34754         this.maxY = Math.max.apply( Math, this.colYs );
34755         var size = {
34756             height: this.maxY
34757         };
34758       
34759         if ( this.isFitWidth ) {
34760             size.width = this._getContainerFitWidth();
34761         }
34762       
34763         return size;
34764     },
34765     
34766     _getContainerFitWidth : function()
34767     {
34768         var unusedCols = 0;
34769         // count unused columns
34770         var i = this.cols;
34771         while ( --i ) {
34772           if ( this.colYs[i] !== 0 ) {
34773             break;
34774           }
34775           unusedCols++;
34776         }
34777         // fit container to columns that have been used
34778         return ( this.cols - unusedCols ) * this.columnWidth - this.gutter;
34779     },
34780     
34781     needsResizeLayout : function()
34782     {
34783         var previousWidth = this.containerWidth;
34784         this.getContainerWidth();
34785         return previousWidth !== this.containerWidth;
34786     }
34787  
34788 });
34789
34790  
34791
34792  /*
34793  * - LGPL
34794  *
34795  * element
34796  * 
34797  */
34798
34799 /**
34800  * @class Roo.bootstrap.MasonryBrick
34801  * @extends Roo.bootstrap.Component
34802  * Bootstrap MasonryBrick class
34803  * 
34804  * @constructor
34805  * Create a new MasonryBrick
34806  * @param {Object} config The config object
34807  */
34808
34809 Roo.bootstrap.MasonryBrick = function(config){
34810     
34811     Roo.bootstrap.MasonryBrick.superclass.constructor.call(this, config);
34812     
34813     Roo.bootstrap.MasonryBrick.register(this);
34814     
34815     this.addEvents({
34816         // raw events
34817         /**
34818          * @event click
34819          * When a MasonryBrick is clcik
34820          * @param {Roo.bootstrap.MasonryBrick} this
34821          * @param {Roo.EventObject} e
34822          */
34823         "click" : true
34824     });
34825 };
34826
34827 Roo.extend(Roo.bootstrap.MasonryBrick, Roo.bootstrap.Component,  {
34828     
34829     /**
34830      * @cfg {String} title
34831      */   
34832     title : '',
34833     /**
34834      * @cfg {String} html
34835      */   
34836     html : '',
34837     /**
34838      * @cfg {String} bgimage
34839      */   
34840     bgimage : '',
34841     /**
34842      * @cfg {String} videourl
34843      */   
34844     videourl : '',
34845     /**
34846      * @cfg {String} cls
34847      */   
34848     cls : '',
34849     /**
34850      * @cfg {String} href
34851      */   
34852     href : '',
34853     /**
34854      * @cfg {String} size (xs|sm|md|md-left|md-right|tall|wide)
34855      */   
34856     size : 'xs',
34857     
34858     /**
34859      * @cfg {String} placetitle (center|bottom)
34860      */   
34861     placetitle : '',
34862     
34863     /**
34864      * @cfg {Boolean} isFitContainer defalut true
34865      */   
34866     isFitContainer : true, 
34867     
34868     /**
34869      * @cfg {Boolean} preventDefault defalut false
34870      */   
34871     preventDefault : false, 
34872     
34873     /**
34874      * @cfg {Boolean} inverse defalut false
34875      */   
34876     maskInverse : false, 
34877     
34878     getAutoCreate : function()
34879     {
34880         if(!this.isFitContainer){
34881             return this.getSplitAutoCreate();
34882         }
34883         
34884         var cls = 'masonry-brick masonry-brick-full';
34885         
34886         if(this.href.length){
34887             cls += ' masonry-brick-link';
34888         }
34889         
34890         if(this.bgimage.length){
34891             cls += ' masonry-brick-image';
34892         }
34893         
34894         if(this.maskInverse){
34895             cls += ' mask-inverse';
34896         }
34897         
34898         if(!this.html.length && !this.maskInverse && !this.videourl.length){
34899             cls += ' enable-mask';
34900         }
34901         
34902         if(this.size){
34903             cls += ' masonry-' + this.size + '-brick';
34904         }
34905         
34906         if(this.placetitle.length){
34907             
34908             switch (this.placetitle) {
34909                 case 'center' :
34910                     cls += ' masonry-center-title';
34911                     break;
34912                 case 'bottom' :
34913                     cls += ' masonry-bottom-title';
34914                     break;
34915                 default:
34916                     break;
34917             }
34918             
34919         } else {
34920             if(!this.html.length && !this.bgimage.length){
34921                 cls += ' masonry-center-title';
34922             }
34923
34924             if(!this.html.length && this.bgimage.length){
34925                 cls += ' masonry-bottom-title';
34926             }
34927         }
34928         
34929         if(this.cls){
34930             cls += ' ' + this.cls;
34931         }
34932         
34933         var cfg = {
34934             tag: (this.href.length) ? 'a' : 'div',
34935             cls: cls,
34936             cn: [
34937                 {
34938                     tag: 'div',
34939                     cls: 'masonry-brick-mask'
34940                 },
34941                 {
34942                     tag: 'div',
34943                     cls: 'masonry-brick-paragraph',
34944                     cn: []
34945                 }
34946             ]
34947         };
34948         
34949         if(this.href.length){
34950             cfg.href = this.href;
34951         }
34952         
34953         var cn = cfg.cn[1].cn;
34954         
34955         if(this.title.length){
34956             cn.push({
34957                 tag: 'h4',
34958                 cls: 'masonry-brick-title',
34959                 html: this.title
34960             });
34961         }
34962         
34963         if(this.html.length){
34964             cn.push({
34965                 tag: 'p',
34966                 cls: 'masonry-brick-text',
34967                 html: this.html
34968             });
34969         }
34970         
34971         if (!this.title.length && !this.html.length) {
34972             cfg.cn[1].cls += ' hide';
34973         }
34974         
34975         if(this.bgimage.length){
34976             cfg.cn.push({
34977                 tag: 'img',
34978                 cls: 'masonry-brick-image-view',
34979                 src: this.bgimage
34980             });
34981         }
34982         
34983         if(this.videourl.length){
34984             var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
34985             // youtube support only?
34986             cfg.cn.push({
34987                 tag: 'iframe',
34988                 cls: 'masonry-brick-image-view',
34989                 src: vurl,
34990                 frameborder : 0,
34991                 allowfullscreen : true
34992             });
34993         }
34994         
34995         return cfg;
34996         
34997     },
34998     
34999     getSplitAutoCreate : function()
35000     {
35001         var cls = 'masonry-brick masonry-brick-split';
35002         
35003         if(this.href.length){
35004             cls += ' masonry-brick-link';
35005         }
35006         
35007         if(this.bgimage.length){
35008             cls += ' masonry-brick-image';
35009         }
35010         
35011         if(this.size){
35012             cls += ' masonry-' + this.size + '-brick';
35013         }
35014         
35015         switch (this.placetitle) {
35016             case 'center' :
35017                 cls += ' masonry-center-title';
35018                 break;
35019             case 'bottom' :
35020                 cls += ' masonry-bottom-title';
35021                 break;
35022             default:
35023                 if(!this.bgimage.length){
35024                     cls += ' masonry-center-title';
35025                 }
35026
35027                 if(this.bgimage.length){
35028                     cls += ' masonry-bottom-title';
35029                 }
35030                 break;
35031         }
35032         
35033         if(this.cls){
35034             cls += ' ' + this.cls;
35035         }
35036         
35037         var cfg = {
35038             tag: (this.href.length) ? 'a' : 'div',
35039             cls: cls,
35040             cn: [
35041                 {
35042                     tag: 'div',
35043                     cls: 'masonry-brick-split-head',
35044                     cn: [
35045                         {
35046                             tag: 'div',
35047                             cls: 'masonry-brick-paragraph',
35048                             cn: []
35049                         }
35050                     ]
35051                 },
35052                 {
35053                     tag: 'div',
35054                     cls: 'masonry-brick-split-body',
35055                     cn: []
35056                 }
35057             ]
35058         };
35059         
35060         if(this.href.length){
35061             cfg.href = this.href;
35062         }
35063         
35064         if(this.title.length){
35065             cfg.cn[0].cn[0].cn.push({
35066                 tag: 'h4',
35067                 cls: 'masonry-brick-title',
35068                 html: this.title
35069             });
35070         }
35071         
35072         if(this.html.length){
35073             cfg.cn[1].cn.push({
35074                 tag: 'p',
35075                 cls: 'masonry-brick-text',
35076                 html: this.html
35077             });
35078         }
35079
35080         if(this.bgimage.length){
35081             cfg.cn[0].cn.push({
35082                 tag: 'img',
35083                 cls: 'masonry-brick-image-view',
35084                 src: this.bgimage
35085             });
35086         }
35087         
35088         if(this.videourl.length){
35089             var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
35090             // youtube support only?
35091             cfg.cn[0].cn.cn.push({
35092                 tag: 'iframe',
35093                 cls: 'masonry-brick-image-view',
35094                 src: vurl,
35095                 frameborder : 0,
35096                 allowfullscreen : true
35097             });
35098         }
35099         
35100         return cfg;
35101     },
35102     
35103     initEvents: function() 
35104     {
35105         switch (this.size) {
35106             case 'xs' :
35107                 this.x = 1;
35108                 this.y = 1;
35109                 break;
35110             case 'sm' :
35111                 this.x = 2;
35112                 this.y = 2;
35113                 break;
35114             case 'md' :
35115             case 'md-left' :
35116             case 'md-right' :
35117                 this.x = 3;
35118                 this.y = 3;
35119                 break;
35120             case 'tall' :
35121                 this.x = 2;
35122                 this.y = 3;
35123                 break;
35124             case 'wide' :
35125                 this.x = 3;
35126                 this.y = 2;
35127                 break;
35128             case 'wide-thin' :
35129                 this.x = 3;
35130                 this.y = 1;
35131                 break;
35132                         
35133             default :
35134                 break;
35135         }
35136         
35137         if(Roo.isTouch){
35138             this.el.on('touchstart', this.onTouchStart, this);
35139             this.el.on('touchmove', this.onTouchMove, this);
35140             this.el.on('touchend', this.onTouchEnd, this);
35141             this.el.on('contextmenu', this.onContextMenu, this);
35142         } else {
35143             this.el.on('mouseenter'  ,this.enter, this);
35144             this.el.on('mouseleave', this.leave, this);
35145             this.el.on('click', this.onClick, this);
35146         }
35147         
35148         if (typeof(this.parent().bricks) == 'object' && this.parent().bricks != null) {
35149             this.parent().bricks.push(this);   
35150         }
35151         
35152     },
35153     
35154     onClick: function(e, el)
35155     {
35156         var time = this.endTimer - this.startTimer;
35157         // Roo.log(e.preventDefault());
35158         if(Roo.isTouch){
35159             if(time > 1000){
35160                 e.preventDefault();
35161                 return;
35162             }
35163         }
35164         
35165         if(!this.preventDefault){
35166             return;
35167         }
35168         
35169         e.preventDefault();
35170         
35171         if (this.activeClass != '') {
35172             this.selectBrick();
35173         }
35174         
35175         this.fireEvent('click', this, e);
35176     },
35177     
35178     enter: function(e, el)
35179     {
35180         e.preventDefault();
35181         
35182         if(!this.isFitContainer || this.maskInverse || this.videourl.length){
35183             return;
35184         }
35185         
35186         if(this.bgimage.length && this.html.length){
35187             this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
35188         }
35189     },
35190     
35191     leave: function(e, el)
35192     {
35193         e.preventDefault();
35194         
35195         if(!this.isFitContainer || this.maskInverse  || this.videourl.length){
35196             return;
35197         }
35198         
35199         if(this.bgimage.length && this.html.length){
35200             this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
35201         }
35202     },
35203     
35204     onTouchStart: function(e, el)
35205     {
35206 //        e.preventDefault();
35207         
35208         this.touchmoved = false;
35209         
35210         if(!this.isFitContainer){
35211             return;
35212         }
35213         
35214         if(!this.bgimage.length || !this.html.length){
35215             return;
35216         }
35217         
35218         this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
35219         
35220         this.timer = new Date().getTime();
35221         
35222     },
35223     
35224     onTouchMove: function(e, el)
35225     {
35226         this.touchmoved = true;
35227     },
35228     
35229     onContextMenu : function(e,el)
35230     {
35231         e.preventDefault();
35232         e.stopPropagation();
35233         return false;
35234     },
35235     
35236     onTouchEnd: function(e, el)
35237     {
35238 //        e.preventDefault();
35239         
35240         if((new Date().getTime() - this.timer > 1000) || !this.href.length || this.touchmoved){
35241         
35242             this.leave(e,el);
35243             
35244             return;
35245         }
35246         
35247         if(!this.bgimage.length || !this.html.length){
35248             
35249             if(this.href.length){
35250                 window.location.href = this.href;
35251             }
35252             
35253             return;
35254         }
35255         
35256         if(!this.isFitContainer){
35257             return;
35258         }
35259         
35260         this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
35261         
35262         window.location.href = this.href;
35263     },
35264     
35265     //selection on single brick only
35266     selectBrick : function() {
35267         
35268         if (!this.parentId) {
35269             return;
35270         }
35271         
35272         var m = Roo.bootstrap.LayoutMasonry.get(this.parentId);
35273         var index = m.selectedBrick.indexOf(this.id);
35274         
35275         if ( index > -1) {
35276             m.selectedBrick.splice(index,1);
35277             this.el.removeClass(this.activeClass);
35278             return;
35279         }
35280         
35281         for(var i = 0; i < m.selectedBrick.length; i++) {
35282             var b = Roo.bootstrap.MasonryBrick.get(m.selectedBrick[i]);
35283             b.el.removeClass(b.activeClass);
35284         }
35285         
35286         m.selectedBrick = [];
35287         
35288         m.selectedBrick.push(this.id);
35289         this.el.addClass(this.activeClass);
35290         return;
35291     },
35292     
35293     isSelected : function(){
35294         return this.el.hasClass(this.activeClass);
35295         
35296     }
35297 });
35298
35299 Roo.apply(Roo.bootstrap.MasonryBrick, {
35300     
35301     //groups: {},
35302     groups : new Roo.util.MixedCollection(false, function(o) { return o.el.id; }),
35303      /**
35304     * register a Masonry Brick
35305     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
35306     */
35307     
35308     register : function(brick)
35309     {
35310         //this.groups[brick.id] = brick;
35311         this.groups.add(brick.id, brick);
35312     },
35313     /**
35314     * fetch a  masonry brick based on the masonry brick ID
35315     * @param {string} the masonry brick to add
35316     * @returns {Roo.bootstrap.MasonryBrick} the masonry brick
35317     */
35318     
35319     get: function(brick_id) 
35320     {
35321         // if (typeof(this.groups[brick_id]) == 'undefined') {
35322         //     return false;
35323         // }
35324         // return this.groups[brick_id] ;
35325         
35326         if(this.groups.key(brick_id)) {
35327             return this.groups.key(brick_id);
35328         }
35329         
35330         return false;
35331     }
35332     
35333     
35334     
35335 });
35336
35337  /*
35338  * - LGPL
35339  *
35340  * element
35341  * 
35342  */
35343
35344 /**
35345  * @class Roo.bootstrap.Brick
35346  * @extends Roo.bootstrap.Component
35347  * Bootstrap Brick class
35348  * 
35349  * @constructor
35350  * Create a new Brick
35351  * @param {Object} config The config object
35352  */
35353
35354 Roo.bootstrap.Brick = function(config){
35355     Roo.bootstrap.Brick.superclass.constructor.call(this, config);
35356     
35357     this.addEvents({
35358         // raw events
35359         /**
35360          * @event click
35361          * When a Brick is click
35362          * @param {Roo.bootstrap.Brick} this
35363          * @param {Roo.EventObject} e
35364          */
35365         "click" : true
35366     });
35367 };
35368
35369 Roo.extend(Roo.bootstrap.Brick, Roo.bootstrap.Component,  {
35370     
35371     /**
35372      * @cfg {String} title
35373      */   
35374     title : '',
35375     /**
35376      * @cfg {String} html
35377      */   
35378     html : '',
35379     /**
35380      * @cfg {String} bgimage
35381      */   
35382     bgimage : '',
35383     /**
35384      * @cfg {String} cls
35385      */   
35386     cls : '',
35387     /**
35388      * @cfg {String} href
35389      */   
35390     href : '',
35391     /**
35392      * @cfg {String} video
35393      */   
35394     video : '',
35395     /**
35396      * @cfg {Boolean} square
35397      */   
35398     square : true,
35399     
35400     getAutoCreate : function()
35401     {
35402         var cls = 'roo-brick';
35403         
35404         if(this.href.length){
35405             cls += ' roo-brick-link';
35406         }
35407         
35408         if(this.bgimage.length){
35409             cls += ' roo-brick-image';
35410         }
35411         
35412         if(!this.html.length && !this.bgimage.length){
35413             cls += ' roo-brick-center-title';
35414         }
35415         
35416         if(!this.html.length && this.bgimage.length){
35417             cls += ' roo-brick-bottom-title';
35418         }
35419         
35420         if(this.cls){
35421             cls += ' ' + this.cls;
35422         }
35423         
35424         var cfg = {
35425             tag: (this.href.length) ? 'a' : 'div',
35426             cls: cls,
35427             cn: [
35428                 {
35429                     tag: 'div',
35430                     cls: 'roo-brick-paragraph',
35431                     cn: []
35432                 }
35433             ]
35434         };
35435         
35436         if(this.href.length){
35437             cfg.href = this.href;
35438         }
35439         
35440         var cn = cfg.cn[0].cn;
35441         
35442         if(this.title.length){
35443             cn.push({
35444                 tag: 'h4',
35445                 cls: 'roo-brick-title',
35446                 html: this.title
35447             });
35448         }
35449         
35450         if(this.html.length){
35451             cn.push({
35452                 tag: 'p',
35453                 cls: 'roo-brick-text',
35454                 html: this.html
35455             });
35456         } else {
35457             cn.cls += ' hide';
35458         }
35459         
35460         if(this.bgimage.length){
35461             cfg.cn.push({
35462                 tag: 'img',
35463                 cls: 'roo-brick-image-view',
35464                 src: this.bgimage
35465             });
35466         }
35467         
35468         return cfg;
35469     },
35470     
35471     initEvents: function() 
35472     {
35473         if(this.title.length || this.html.length){
35474             this.el.on('mouseenter'  ,this.enter, this);
35475             this.el.on('mouseleave', this.leave, this);
35476         }
35477         
35478         Roo.EventManager.onWindowResize(this.resize, this); 
35479         
35480         if(this.bgimage.length){
35481             this.imageEl = this.el.select('.roo-brick-image-view', true).first();
35482             this.imageEl.on('load', this.onImageLoad, this);
35483             return;
35484         }
35485         
35486         this.resize();
35487     },
35488     
35489     onImageLoad : function()
35490     {
35491         this.resize();
35492     },
35493     
35494     resize : function()
35495     {
35496         var paragraph = this.el.select('.roo-brick-paragraph', true).first();
35497         
35498         paragraph.setHeight(paragraph.getWidth() + paragraph.getPadding('tb'));
35499         
35500         if(this.bgimage.length){
35501             var image = this.el.select('.roo-brick-image-view', true).first();
35502             
35503             image.setWidth(paragraph.getWidth());
35504             
35505             if(this.square){
35506                 image.setHeight(paragraph.getWidth());
35507             }
35508             
35509             this.el.setHeight(image.getHeight());
35510             paragraph.setHeight(image.getHeight());
35511             
35512         }
35513         
35514     },
35515     
35516     enter: function(e, el)
35517     {
35518         e.preventDefault();
35519         
35520         if(this.bgimage.length){
35521             this.el.select('.roo-brick-paragraph', true).first().setOpacity(0.9, true);
35522             this.el.select('.roo-brick-image-view', true).first().setOpacity(0.1, true);
35523         }
35524     },
35525     
35526     leave: function(e, el)
35527     {
35528         e.preventDefault();
35529         
35530         if(this.bgimage.length){
35531             this.el.select('.roo-brick-paragraph', true).first().setOpacity(0, true);
35532             this.el.select('.roo-brick-image-view', true).first().setOpacity(1, true);
35533         }
35534     }
35535     
35536 });
35537
35538  
35539
35540  /*
35541  * - LGPL
35542  *
35543  * Number field 
35544  */
35545
35546 /**
35547  * @class Roo.bootstrap.NumberField
35548  * @extends Roo.bootstrap.Input
35549  * Bootstrap NumberField class
35550  * 
35551  * 
35552  * 
35553  * 
35554  * @constructor
35555  * Create a new NumberField
35556  * @param {Object} config The config object
35557  */
35558
35559 Roo.bootstrap.NumberField = function(config){
35560     Roo.bootstrap.NumberField.superclass.constructor.call(this, config);
35561 };
35562
35563 Roo.extend(Roo.bootstrap.NumberField, Roo.bootstrap.Input, {
35564     
35565     /**
35566      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
35567      */
35568     allowDecimals : true,
35569     /**
35570      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
35571      */
35572     decimalSeparator : ".",
35573     /**
35574      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
35575      */
35576     decimalPrecision : 2,
35577     /**
35578      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
35579      */
35580     allowNegative : true,
35581     
35582     /**
35583      * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
35584      */
35585     allowZero: true,
35586     /**
35587      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
35588      */
35589     minValue : Number.NEGATIVE_INFINITY,
35590     /**
35591      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
35592      */
35593     maxValue : Number.MAX_VALUE,
35594     /**
35595      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
35596      */
35597     minText : "The minimum value for this field is {0}",
35598     /**
35599      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
35600      */
35601     maxText : "The maximum value for this field is {0}",
35602     /**
35603      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
35604      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
35605      */
35606     nanText : "{0} is not a valid number",
35607     /**
35608      * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
35609      */
35610     thousandsDelimiter : false,
35611     /**
35612      * @cfg {String} valueAlign alignment of value
35613      */
35614     valueAlign : "left",
35615
35616     getAutoCreate : function()
35617     {
35618         var hiddenInput = {
35619             tag: 'input',
35620             type: 'hidden',
35621             id: Roo.id(),
35622             cls: 'hidden-number-input'
35623         };
35624         
35625         if (this.name) {
35626             hiddenInput.name = this.name;
35627         }
35628         
35629         this.name = '';
35630         
35631         var cfg = Roo.bootstrap.NumberField.superclass.getAutoCreate.call(this);
35632         
35633         this.name = hiddenInput.name;
35634         
35635         if(cfg.cn.length > 0) {
35636             cfg.cn.push(hiddenInput);
35637         }
35638         
35639         return cfg;
35640     },
35641
35642     // private
35643     initEvents : function()
35644     {   
35645         Roo.bootstrap.NumberField.superclass.initEvents.call(this);
35646         
35647         var allowed = "0123456789";
35648         
35649         if(this.allowDecimals){
35650             allowed += this.decimalSeparator;
35651         }
35652         
35653         if(this.allowNegative){
35654             allowed += "-";
35655         }
35656         
35657         if(this.thousandsDelimiter) {
35658             allowed += ",";
35659         }
35660         
35661         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
35662         
35663         var keyPress = function(e){
35664             
35665             var k = e.getKey();
35666             
35667             var c = e.getCharCode();
35668             
35669             if(
35670                     (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
35671                     allowed.indexOf(String.fromCharCode(c)) === -1
35672             ){
35673                 e.stopEvent();
35674                 return;
35675             }
35676             
35677             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
35678                 return;
35679             }
35680             
35681             if(allowed.indexOf(String.fromCharCode(c)) === -1){
35682                 e.stopEvent();
35683             }
35684         };
35685         
35686         this.el.on("keypress", keyPress, this);
35687     },
35688     
35689     validateValue : function(value)
35690     {
35691         
35692         if(!Roo.bootstrap.NumberField.superclass.validateValue.call(this, value)){
35693             return false;
35694         }
35695         
35696         var num = this.parseValue(value);
35697         
35698         if(isNaN(num)){
35699             this.markInvalid(String.format(this.nanText, value));
35700             return false;
35701         }
35702         
35703         if(num < this.minValue){
35704             this.markInvalid(String.format(this.minText, this.minValue));
35705             return false;
35706         }
35707         
35708         if(num > this.maxValue){
35709             this.markInvalid(String.format(this.maxText, this.maxValue));
35710             return false;
35711         }
35712         
35713         return true;
35714     },
35715
35716     getValue : function()
35717     {
35718         var v = this.hiddenEl().getValue();
35719         
35720         return this.fixPrecision(this.parseValue(v));
35721     },
35722
35723     parseValue : function(value)
35724     {
35725         if(this.thousandsDelimiter) {
35726             value += "";
35727             r = new RegExp(",", "g");
35728             value = value.replace(r, "");
35729         }
35730         
35731         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
35732         return isNaN(value) ? '' : value;
35733     },
35734
35735     fixPrecision : function(value)
35736     {
35737         if(this.thousandsDelimiter) {
35738             value += "";
35739             r = new RegExp(",", "g");
35740             value = value.replace(r, "");
35741         }
35742         
35743         var nan = isNaN(value);
35744         
35745         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
35746             return nan ? '' : value;
35747         }
35748         return parseFloat(value).toFixed(this.decimalPrecision);
35749     },
35750
35751     setValue : function(v)
35752     {
35753         v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
35754         
35755         this.value = v;
35756         
35757         if(this.rendered){
35758             
35759             this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
35760             
35761             this.inputEl().dom.value = (v == '') ? '' :
35762                 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
35763             
35764             if(!this.allowZero && v === '0') {
35765                 this.hiddenEl().dom.value = '';
35766                 this.inputEl().dom.value = '';
35767             }
35768             
35769             this.validate();
35770         }
35771     },
35772
35773     decimalPrecisionFcn : function(v)
35774     {
35775         return Math.floor(v);
35776     },
35777
35778     beforeBlur : function()
35779     {
35780         var v = this.parseValue(this.getRawValue());
35781         
35782         if(v || v === 0 || v === ''){
35783             this.setValue(v);
35784         }
35785     },
35786     
35787     hiddenEl : function()
35788     {
35789         return this.el.select('input.hidden-number-input',true).first();
35790     }
35791     
35792 });
35793
35794  
35795
35796 /*
35797 * Licence: LGPL
35798 */
35799
35800 /**
35801  * @class Roo.bootstrap.DocumentSlider
35802  * @extends Roo.bootstrap.Component
35803  * Bootstrap DocumentSlider class
35804  * 
35805  * @constructor
35806  * Create a new DocumentViewer
35807  * @param {Object} config The config object
35808  */
35809
35810 Roo.bootstrap.DocumentSlider = function(config){
35811     Roo.bootstrap.DocumentSlider.superclass.constructor.call(this, config);
35812     
35813     this.files = [];
35814     
35815     this.addEvents({
35816         /**
35817          * @event initial
35818          * Fire after initEvent
35819          * @param {Roo.bootstrap.DocumentSlider} this
35820          */
35821         "initial" : true,
35822         /**
35823          * @event update
35824          * Fire after update
35825          * @param {Roo.bootstrap.DocumentSlider} this
35826          */
35827         "update" : true,
35828         /**
35829          * @event click
35830          * Fire after click
35831          * @param {Roo.bootstrap.DocumentSlider} this
35832          */
35833         "click" : true
35834     });
35835 };
35836
35837 Roo.extend(Roo.bootstrap.DocumentSlider, Roo.bootstrap.Component,  {
35838     
35839     files : false,
35840     
35841     indicator : 0,
35842     
35843     getAutoCreate : function()
35844     {
35845         var cfg = {
35846             tag : 'div',
35847             cls : 'roo-document-slider',
35848             cn : [
35849                 {
35850                     tag : 'div',
35851                     cls : 'roo-document-slider-header',
35852                     cn : [
35853                         {
35854                             tag : 'div',
35855                             cls : 'roo-document-slider-header-title'
35856                         }
35857                     ]
35858                 },
35859                 {
35860                     tag : 'div',
35861                     cls : 'roo-document-slider-body',
35862                     cn : [
35863                         {
35864                             tag : 'div',
35865                             cls : 'roo-document-slider-prev',
35866                             cn : [
35867                                 {
35868                                     tag : 'i',
35869                                     cls : 'fa fa-chevron-left'
35870                                 }
35871                             ]
35872                         },
35873                         {
35874                             tag : 'div',
35875                             cls : 'roo-document-slider-thumb',
35876                             cn : [
35877                                 {
35878                                     tag : 'img',
35879                                     cls : 'roo-document-slider-image'
35880                                 }
35881                             ]
35882                         },
35883                         {
35884                             tag : 'div',
35885                             cls : 'roo-document-slider-next',
35886                             cn : [
35887                                 {
35888                                     tag : 'i',
35889                                     cls : 'fa fa-chevron-right'
35890                                 }
35891                             ]
35892                         }
35893                     ]
35894                 }
35895             ]
35896         };
35897         
35898         return cfg;
35899     },
35900     
35901     initEvents : function()
35902     {
35903         this.headerEl = this.el.select('.roo-document-slider-header', true).first();
35904         this.headerEl.setVisibilityMode(Roo.Element.DISPLAY);
35905         
35906         this.titleEl = this.el.select('.roo-document-slider-header .roo-document-slider-header-title', true).first();
35907         this.titleEl.setVisibilityMode(Roo.Element.DISPLAY);
35908         
35909         this.bodyEl = this.el.select('.roo-document-slider-body', true).first();
35910         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
35911         
35912         this.thumbEl = this.el.select('.roo-document-slider-thumb', true).first();
35913         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
35914         
35915         this.imageEl = this.el.select('.roo-document-slider-image', true).first();
35916         this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
35917         
35918         this.prevIndicator = this.el.select('.roo-document-slider-prev i', true).first();
35919         this.prevIndicator.setVisibilityMode(Roo.Element.DISPLAY);
35920         
35921         this.nextIndicator = this.el.select('.roo-document-slider-next i', true).first();
35922         this.nextIndicator.setVisibilityMode(Roo.Element.DISPLAY);
35923         
35924         this.thumbEl.on('click', this.onClick, this);
35925         
35926         this.prevIndicator.on('click', this.prev, this);
35927         
35928         this.nextIndicator.on('click', this.next, this);
35929         
35930     },
35931     
35932     initial : function()
35933     {
35934         if(this.files.length){
35935             this.indicator = 1;
35936             this.update()
35937         }
35938         
35939         this.fireEvent('initial', this);
35940     },
35941     
35942     update : function()
35943     {
35944         this.imageEl.attr('src', this.files[this.indicator - 1]);
35945         
35946         this.titleEl.dom.innerHTML = String.format('{0} / {1}', this.indicator, this.files.length);
35947         
35948         this.prevIndicator.show();
35949         
35950         if(this.indicator == 1){
35951             this.prevIndicator.hide();
35952         }
35953         
35954         this.nextIndicator.show();
35955         
35956         if(this.indicator == this.files.length){
35957             this.nextIndicator.hide();
35958         }
35959         
35960         this.thumbEl.scrollTo('top');
35961         
35962         this.fireEvent('update', this);
35963     },
35964     
35965     onClick : function(e)
35966     {
35967         e.preventDefault();
35968         
35969         this.fireEvent('click', this);
35970     },
35971     
35972     prev : function(e)
35973     {
35974         e.preventDefault();
35975         
35976         this.indicator = Math.max(1, this.indicator - 1);
35977         
35978         this.update();
35979     },
35980     
35981     next : function(e)
35982     {
35983         e.preventDefault();
35984         
35985         this.indicator = Math.min(this.files.length, this.indicator + 1);
35986         
35987         this.update();
35988     }
35989 });
35990 /*
35991  * - LGPL
35992  *
35993  * RadioSet
35994  *
35995  *
35996  */
35997
35998 /**
35999  * @class Roo.bootstrap.RadioSet
36000  * @extends Roo.bootstrap.Input
36001  * Bootstrap RadioSet class
36002  * @cfg {String} indicatorpos (left|right) default left
36003  * @cfg {Boolean} inline (true|false) inline the element (default true)
36004  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the radio
36005  * @constructor
36006  * Create a new RadioSet
36007  * @param {Object} config The config object
36008  */
36009
36010 Roo.bootstrap.RadioSet = function(config){
36011     
36012     Roo.bootstrap.RadioSet.superclass.constructor.call(this, config);
36013     
36014     this.radioes = [];
36015     
36016     Roo.bootstrap.RadioSet.register(this);
36017     
36018     this.addEvents({
36019         /**
36020         * @event check
36021         * Fires when the element is checked or unchecked.
36022         * @param {Roo.bootstrap.RadioSet} this This radio
36023         * @param {Roo.bootstrap.Radio} item The checked item
36024         */
36025        check : true,
36026        /**
36027         * @event click
36028         * Fires when the element is click.
36029         * @param {Roo.bootstrap.RadioSet} this This radio set
36030         * @param {Roo.bootstrap.Radio} item The checked item
36031         * @param {Roo.EventObject} e The event object
36032         */
36033        click : true
36034     });
36035     
36036 };
36037
36038 Roo.extend(Roo.bootstrap.RadioSet, Roo.bootstrap.Input,  {
36039
36040     radioes : false,
36041     
36042     inline : true,
36043     
36044     weight : '',
36045     
36046     indicatorpos : 'left',
36047     
36048     getAutoCreate : function()
36049     {
36050         var label = {
36051             tag : 'label',
36052             cls : 'roo-radio-set-label',
36053             cn : [
36054                 {
36055                     tag : 'span',
36056                     html : this.fieldLabel
36057                 }
36058             ]
36059         };
36060         if (Roo.bootstrap.version == 3) {
36061             
36062             
36063             if(this.indicatorpos == 'left'){
36064                 label.cn.unshift({
36065                     tag : 'i',
36066                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
36067                     tooltip : 'This field is required'
36068                 });
36069             } else {
36070                 label.cn.push({
36071                     tag : 'i',
36072                     cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
36073                     tooltip : 'This field is required'
36074                 });
36075             }
36076         }
36077         var items = {
36078             tag : 'div',
36079             cls : 'roo-radio-set-items'
36080         };
36081         
36082         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
36083         
36084         if (align === 'left' && this.fieldLabel.length) {
36085             
36086             items = {
36087                 cls : "roo-radio-set-right", 
36088                 cn: [
36089                     items
36090                 ]
36091             };
36092             
36093             if(this.labelWidth > 12){
36094                 label.style = "width: " + this.labelWidth + 'px';
36095             }
36096             
36097             if(this.labelWidth < 13 && this.labelmd == 0){
36098                 this.labelmd = this.labelWidth;
36099             }
36100             
36101             if(this.labellg > 0){
36102                 label.cls += ' col-lg-' + this.labellg;
36103                 items.cls += ' col-lg-' + (12 - this.labellg);
36104             }
36105             
36106             if(this.labelmd > 0){
36107                 label.cls += ' col-md-' + this.labelmd;
36108                 items.cls += ' col-md-' + (12 - this.labelmd);
36109             }
36110             
36111             if(this.labelsm > 0){
36112                 label.cls += ' col-sm-' + this.labelsm;
36113                 items.cls += ' col-sm-' + (12 - this.labelsm);
36114             }
36115             
36116             if(this.labelxs > 0){
36117                 label.cls += ' col-xs-' + this.labelxs;
36118                 items.cls += ' col-xs-' + (12 - this.labelxs);
36119             }
36120         }
36121         
36122         var cfg = {
36123             tag : 'div',
36124             cls : 'roo-radio-set',
36125             cn : [
36126                 {
36127                     tag : 'input',
36128                     cls : 'roo-radio-set-input',
36129                     type : 'hidden',
36130                     name : this.name,
36131                     value : this.value ? this.value :  ''
36132                 },
36133                 label,
36134                 items
36135             ]
36136         };
36137         
36138         if(this.weight.length){
36139             cfg.cls += ' roo-radio-' + this.weight;
36140         }
36141         
36142         if(this.inline) {
36143             cfg.cls += ' roo-radio-set-inline';
36144         }
36145         
36146         var settings=this;
36147         ['xs','sm','md','lg'].map(function(size){
36148             if (settings[size]) {
36149                 cfg.cls += ' col-' + size + '-' + settings[size];
36150             }
36151         });
36152         
36153         return cfg;
36154         
36155     },
36156
36157     initEvents : function()
36158     {
36159         this.labelEl = this.el.select('.roo-radio-set-label', true).first();
36160         this.labelEl.setVisibilityMode(Roo.Element.DISPLAY);
36161         
36162         if(!this.fieldLabel.length){
36163             this.labelEl.hide();
36164         }
36165         
36166         this.itemsEl = this.el.select('.roo-radio-set-items', true).first();
36167         this.itemsEl.setVisibilityMode(Roo.Element.DISPLAY);
36168         
36169         this.indicator = this.indicatorEl();
36170         
36171         if(this.indicator){
36172             this.indicator.addClass('invisible');
36173         }
36174         
36175         this.originalValue = this.getValue();
36176         
36177     },
36178     
36179     inputEl: function ()
36180     {
36181         return this.el.select('.roo-radio-set-input', true).first();
36182     },
36183     
36184     getChildContainer : function()
36185     {
36186         return this.itemsEl;
36187     },
36188     
36189     register : function(item)
36190     {
36191         this.radioes.push(item);
36192         
36193     },
36194     
36195     validate : function()
36196     {   
36197         if(this.getVisibilityEl().hasClass('hidden')){
36198             return true;
36199         }
36200         
36201         var valid = false;
36202         
36203         Roo.each(this.radioes, function(i){
36204             if(!i.checked){
36205                 return;
36206             }
36207             
36208             valid = true;
36209             return false;
36210         });
36211         
36212         if(this.allowBlank) {
36213             return true;
36214         }
36215         
36216         if(this.disabled || valid){
36217             this.markValid();
36218             return true;
36219         }
36220         
36221         this.markInvalid();
36222         return false;
36223         
36224     },
36225     
36226     markValid : function()
36227     {
36228         if(this.labelEl.isVisible(true) && this.indicatorEl()){
36229             this.indicatorEl().removeClass('visible');
36230             this.indicatorEl().addClass('invisible');
36231         }
36232         
36233         
36234         if (Roo.bootstrap.version == 3) {
36235             this.el.removeClass([this.invalidClass, this.validClass]);
36236             this.el.addClass(this.validClass);
36237         } else {
36238             this.el.removeClass(['is-invalid','is-valid']);
36239             this.el.addClass(['is-valid']);
36240         }
36241         this.fireEvent('valid', this);
36242     },
36243     
36244     markInvalid : function(msg)
36245     {
36246         if(this.allowBlank || this.disabled){
36247             return;
36248         }
36249         
36250         if(this.labelEl.isVisible(true) && this.indicatorEl()){
36251             this.indicatorEl().removeClass('invisible');
36252             this.indicatorEl().addClass('visible');
36253         }
36254         if (Roo.bootstrap.version == 3) {
36255             this.el.removeClass([this.invalidClass, this.validClass]);
36256             this.el.addClass(this.invalidClass);
36257         } else {
36258             this.el.removeClass(['is-invalid','is-valid']);
36259             this.el.addClass(['is-invalid']);
36260         }
36261         
36262         this.fireEvent('invalid', this, msg);
36263         
36264     },
36265     
36266     setValue : function(v, suppressEvent)
36267     {   
36268         if(this.value === v){
36269             return;
36270         }
36271         
36272         this.value = v;
36273         
36274         if(this.rendered){
36275             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
36276         }
36277         
36278         Roo.each(this.radioes, function(i){
36279             i.checked = false;
36280             i.el.removeClass('checked');
36281         });
36282         
36283         Roo.each(this.radioes, function(i){
36284             
36285             if(i.value === v || i.value.toString() === v.toString()){
36286                 i.checked = true;
36287                 i.el.addClass('checked');
36288                 
36289                 if(suppressEvent !== true){
36290                     this.fireEvent('check', this, i);
36291                 }
36292                 
36293                 return false;
36294             }
36295             
36296         }, this);
36297         
36298         this.validate();
36299     },
36300     
36301     clearInvalid : function(){
36302         
36303         if(!this.el || this.preventMark){
36304             return;
36305         }
36306         
36307         this.el.removeClass([this.invalidClass]);
36308         
36309         this.fireEvent('valid', this);
36310     }
36311     
36312 });
36313
36314 Roo.apply(Roo.bootstrap.RadioSet, {
36315     
36316     groups: {},
36317     
36318     register : function(set)
36319     {
36320         this.groups[set.name] = set;
36321     },
36322     
36323     get: function(name) 
36324     {
36325         if (typeof(this.groups[name]) == 'undefined') {
36326             return false;
36327         }
36328         
36329         return this.groups[name] ;
36330     }
36331     
36332 });
36333 /*
36334  * Based on:
36335  * Ext JS Library 1.1.1
36336  * Copyright(c) 2006-2007, Ext JS, LLC.
36337  *
36338  * Originally Released Under LGPL - original licence link has changed is not relivant.
36339  *
36340  * Fork - LGPL
36341  * <script type="text/javascript">
36342  */
36343
36344
36345 /**
36346  * @class Roo.bootstrap.SplitBar
36347  * @extends Roo.util.Observable
36348  * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
36349  * <br><br>
36350  * Usage:
36351  * <pre><code>
36352 var split = new Roo.bootstrap.SplitBar("elementToDrag", "elementToSize",
36353                    Roo.bootstrap.SplitBar.HORIZONTAL, Roo.bootstrap.SplitBar.LEFT);
36354 split.setAdapter(new Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter("container"));
36355 split.minSize = 100;
36356 split.maxSize = 600;
36357 split.animate = true;
36358 split.on('moved', splitterMoved);
36359 </code></pre>
36360  * @constructor
36361  * Create a new SplitBar
36362  * @config {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar. 
36363  * @config {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged 
36364  * @config {Number} orientation (optional) Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
36365  * @config {Number} placement (optional) Either Roo.bootstrap.SplitBar.LEFT or Roo.bootstrap.SplitBar.RIGHT for horizontal or  
36366                         Roo.bootstrap.SplitBar.TOP or Roo.bootstrap.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
36367                         position of the SplitBar).
36368  */
36369 Roo.bootstrap.SplitBar = function(cfg){
36370     
36371     /** @private */
36372     
36373     //{
36374     //  dragElement : elm
36375     //  resizingElement: el,
36376         // optional..
36377     //    orientation : Either Roo.bootstrap.SplitBar.HORIZONTAL
36378     //    placement : Roo.bootstrap.SplitBar.LEFT  ,
36379         // existingProxy ???
36380     //}
36381     
36382     this.el = Roo.get(cfg.dragElement, true);
36383     this.el.dom.unselectable = "on";
36384     /** @private */
36385     this.resizingEl = Roo.get(cfg.resizingElement, true);
36386
36387     /**
36388      * @private
36389      * The orientation of the split. Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
36390      * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
36391      * @type Number
36392      */
36393     this.orientation = cfg.orientation || Roo.bootstrap.SplitBar.HORIZONTAL;
36394     
36395     /**
36396      * The minimum size of the resizing element. (Defaults to 0)
36397      * @type Number
36398      */
36399     this.minSize = 0;
36400     
36401     /**
36402      * The maximum size of the resizing element. (Defaults to 2000)
36403      * @type Number
36404      */
36405     this.maxSize = 2000;
36406     
36407     /**
36408      * Whether to animate the transition to the new size
36409      * @type Boolean
36410      */
36411     this.animate = false;
36412     
36413     /**
36414      * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
36415      * @type Boolean
36416      */
36417     this.useShim = false;
36418     
36419     /** @private */
36420     this.shim = null;
36421     
36422     if(!cfg.existingProxy){
36423         /** @private */
36424         this.proxy = Roo.bootstrap.SplitBar.createProxy(this.orientation);
36425     }else{
36426         this.proxy = Roo.get(cfg.existingProxy).dom;
36427     }
36428     /** @private */
36429     this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
36430     
36431     /** @private */
36432     this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
36433     
36434     /** @private */
36435     this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
36436     
36437     /** @private */
36438     this.dragSpecs = {};
36439     
36440     /**
36441      * @private The adapter to use to positon and resize elements
36442      */
36443     this.adapter = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
36444     this.adapter.init(this);
36445     
36446     if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
36447         /** @private */
36448         this.placement = cfg.placement || (this.el.getX() > this.resizingEl.getX() ? Roo.bootstrap.SplitBar.LEFT : Roo.bootstrap.SplitBar.RIGHT);
36449         this.el.addClass("roo-splitbar-h");
36450     }else{
36451         /** @private */
36452         this.placement = cfg.placement || (this.el.getY() > this.resizingEl.getY() ? Roo.bootstrap.SplitBar.TOP : Roo.bootstrap.SplitBar.BOTTOM);
36453         this.el.addClass("roo-splitbar-v");
36454     }
36455     
36456     this.addEvents({
36457         /**
36458          * @event resize
36459          * Fires when the splitter is moved (alias for {@link #event-moved})
36460          * @param {Roo.bootstrap.SplitBar} this
36461          * @param {Number} newSize the new width or height
36462          */
36463         "resize" : true,
36464         /**
36465          * @event moved
36466          * Fires when the splitter is moved
36467          * @param {Roo.bootstrap.SplitBar} this
36468          * @param {Number} newSize the new width or height
36469          */
36470         "moved" : true,
36471         /**
36472          * @event beforeresize
36473          * Fires before the splitter is dragged
36474          * @param {Roo.bootstrap.SplitBar} this
36475          */
36476         "beforeresize" : true,
36477
36478         "beforeapply" : true
36479     });
36480
36481     Roo.util.Observable.call(this);
36482 };
36483
36484 Roo.extend(Roo.bootstrap.SplitBar, Roo.util.Observable, {
36485     onStartProxyDrag : function(x, y){
36486         this.fireEvent("beforeresize", this);
36487         if(!this.overlay){
36488             var o = Roo.DomHelper.insertFirst(document.body,  {cls: "roo-drag-overlay", html: "&#160;"}, true);
36489             o.unselectable();
36490             o.enableDisplayMode("block");
36491             // all splitbars share the same overlay
36492             Roo.bootstrap.SplitBar.prototype.overlay = o;
36493         }
36494         this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
36495         this.overlay.show();
36496         Roo.get(this.proxy).setDisplayed("block");
36497         var size = this.adapter.getElementSize(this);
36498         this.activeMinSize = this.getMinimumSize();;
36499         this.activeMaxSize = this.getMaximumSize();;
36500         var c1 = size - this.activeMinSize;
36501         var c2 = Math.max(this.activeMaxSize - size, 0);
36502         if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
36503             this.dd.resetConstraints();
36504             this.dd.setXConstraint(
36505                 this.placement == Roo.bootstrap.SplitBar.LEFT ? c1 : c2, 
36506                 this.placement == Roo.bootstrap.SplitBar.LEFT ? c2 : c1
36507             );
36508             this.dd.setYConstraint(0, 0);
36509         }else{
36510             this.dd.resetConstraints();
36511             this.dd.setXConstraint(0, 0);
36512             this.dd.setYConstraint(
36513                 this.placement == Roo.bootstrap.SplitBar.TOP ? c1 : c2, 
36514                 this.placement == Roo.bootstrap.SplitBar.TOP ? c2 : c1
36515             );
36516          }
36517         this.dragSpecs.startSize = size;
36518         this.dragSpecs.startPoint = [x, y];
36519         Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
36520     },
36521     
36522     /** 
36523      * @private Called after the drag operation by the DDProxy
36524      */
36525     onEndProxyDrag : function(e){
36526         Roo.get(this.proxy).setDisplayed(false);
36527         var endPoint = Roo.lib.Event.getXY(e);
36528         if(this.overlay){
36529             this.overlay.hide();
36530         }
36531         var newSize;
36532         if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
36533             newSize = this.dragSpecs.startSize + 
36534                 (this.placement == Roo.bootstrap.SplitBar.LEFT ?
36535                     endPoint[0] - this.dragSpecs.startPoint[0] :
36536                     this.dragSpecs.startPoint[0] - endPoint[0]
36537                 );
36538         }else{
36539             newSize = this.dragSpecs.startSize + 
36540                 (this.placement == Roo.bootstrap.SplitBar.TOP ?
36541                     endPoint[1] - this.dragSpecs.startPoint[1] :
36542                     this.dragSpecs.startPoint[1] - endPoint[1]
36543                 );
36544         }
36545         newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
36546         if(newSize != this.dragSpecs.startSize){
36547             if(this.fireEvent('beforeapply', this, newSize) !== false){
36548                 this.adapter.setElementSize(this, newSize);
36549                 this.fireEvent("moved", this, newSize);
36550                 this.fireEvent("resize", this, newSize);
36551             }
36552         }
36553     },
36554     
36555     /**
36556      * Get the adapter this SplitBar uses
36557      * @return The adapter object
36558      */
36559     getAdapter : function(){
36560         return this.adapter;
36561     },
36562     
36563     /**
36564      * Set the adapter this SplitBar uses
36565      * @param {Object} adapter A SplitBar adapter object
36566      */
36567     setAdapter : function(adapter){
36568         this.adapter = adapter;
36569         this.adapter.init(this);
36570     },
36571     
36572     /**
36573      * Gets the minimum size for the resizing element
36574      * @return {Number} The minimum size
36575      */
36576     getMinimumSize : function(){
36577         return this.minSize;
36578     },
36579     
36580     /**
36581      * Sets the minimum size for the resizing element
36582      * @param {Number} minSize The minimum size
36583      */
36584     setMinimumSize : function(minSize){
36585         this.minSize = minSize;
36586     },
36587     
36588     /**
36589      * Gets the maximum size for the resizing element
36590      * @return {Number} The maximum size
36591      */
36592     getMaximumSize : function(){
36593         return this.maxSize;
36594     },
36595     
36596     /**
36597      * Sets the maximum size for the resizing element
36598      * @param {Number} maxSize The maximum size
36599      */
36600     setMaximumSize : function(maxSize){
36601         this.maxSize = maxSize;
36602     },
36603     
36604     /**
36605      * Sets the initialize size for the resizing element
36606      * @param {Number} size The initial size
36607      */
36608     setCurrentSize : function(size){
36609         var oldAnimate = this.animate;
36610         this.animate = false;
36611         this.adapter.setElementSize(this, size);
36612         this.animate = oldAnimate;
36613     },
36614     
36615     /**
36616      * Destroy this splitbar. 
36617      * @param {Boolean} removeEl True to remove the element
36618      */
36619     destroy : function(removeEl){
36620         if(this.shim){
36621             this.shim.remove();
36622         }
36623         this.dd.unreg();
36624         this.proxy.parentNode.removeChild(this.proxy);
36625         if(removeEl){
36626             this.el.remove();
36627         }
36628     }
36629 });
36630
36631 /**
36632  * @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.
36633  */
36634 Roo.bootstrap.SplitBar.createProxy = function(dir){
36635     var proxy = new Roo.Element(document.createElement("div"));
36636     proxy.unselectable();
36637     var cls = 'roo-splitbar-proxy';
36638     proxy.addClass(cls + ' ' + (dir == Roo.bootstrap.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
36639     document.body.appendChild(proxy.dom);
36640     return proxy.dom;
36641 };
36642
36643 /** 
36644  * @class Roo.bootstrap.SplitBar.BasicLayoutAdapter
36645  * Default Adapter. It assumes the splitter and resizing element are not positioned
36646  * elements and only gets/sets the width of the element. Generally used for table based layouts.
36647  */
36648 Roo.bootstrap.SplitBar.BasicLayoutAdapter = function(){
36649 };
36650
36651 Roo.bootstrap.SplitBar.BasicLayoutAdapter.prototype = {
36652     // do nothing for now
36653     init : function(s){
36654     
36655     },
36656     /**
36657      * Called before drag operations to get the current size of the resizing element. 
36658      * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
36659      */
36660      getElementSize : function(s){
36661         if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
36662             return s.resizingEl.getWidth();
36663         }else{
36664             return s.resizingEl.getHeight();
36665         }
36666     },
36667     
36668     /**
36669      * Called after drag operations to set the size of the resizing element.
36670      * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
36671      * @param {Number} newSize The new size to set
36672      * @param {Function} onComplete A function to be invoked when resizing is complete
36673      */
36674     setElementSize : function(s, newSize, onComplete){
36675         if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
36676             if(!s.animate){
36677                 s.resizingEl.setWidth(newSize);
36678                 if(onComplete){
36679                     onComplete(s, newSize);
36680                 }
36681             }else{
36682                 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
36683             }
36684         }else{
36685             
36686             if(!s.animate){
36687                 s.resizingEl.setHeight(newSize);
36688                 if(onComplete){
36689                     onComplete(s, newSize);
36690                 }
36691             }else{
36692                 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
36693             }
36694         }
36695     }
36696 };
36697
36698 /** 
36699  *@class Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter
36700  * @extends Roo.bootstrap.SplitBar.BasicLayoutAdapter
36701  * Adapter that  moves the splitter element to align with the resized sizing element. 
36702  * Used with an absolute positioned SplitBar.
36703  * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
36704  * document.body, make sure you assign an id to the body element.
36705  */
36706 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter = function(container){
36707     this.basic = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
36708     this.container = Roo.get(container);
36709 };
36710
36711 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter.prototype = {
36712     init : function(s){
36713         this.basic.init(s);
36714     },
36715     
36716     getElementSize : function(s){
36717         return this.basic.getElementSize(s);
36718     },
36719     
36720     setElementSize : function(s, newSize, onComplete){
36721         this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
36722     },
36723     
36724     moveSplitter : function(s){
36725         var yes = Roo.bootstrap.SplitBar;
36726         switch(s.placement){
36727             case yes.LEFT:
36728                 s.el.setX(s.resizingEl.getRight());
36729                 break;
36730             case yes.RIGHT:
36731                 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
36732                 break;
36733             case yes.TOP:
36734                 s.el.setY(s.resizingEl.getBottom());
36735                 break;
36736             case yes.BOTTOM:
36737                 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
36738                 break;
36739         }
36740     }
36741 };
36742
36743 /**
36744  * Orientation constant - Create a vertical SplitBar
36745  * @static
36746  * @type Number
36747  */
36748 Roo.bootstrap.SplitBar.VERTICAL = 1;
36749
36750 /**
36751  * Orientation constant - Create a horizontal SplitBar
36752  * @static
36753  * @type Number
36754  */
36755 Roo.bootstrap.SplitBar.HORIZONTAL = 2;
36756
36757 /**
36758  * Placement constant - The resizing element is to the left of the splitter element
36759  * @static
36760  * @type Number
36761  */
36762 Roo.bootstrap.SplitBar.LEFT = 1;
36763
36764 /**
36765  * Placement constant - The resizing element is to the right of the splitter element
36766  * @static
36767  * @type Number
36768  */
36769 Roo.bootstrap.SplitBar.RIGHT = 2;
36770
36771 /**
36772  * Placement constant - The resizing element is positioned above the splitter element
36773  * @static
36774  * @type Number
36775  */
36776 Roo.bootstrap.SplitBar.TOP = 3;
36777
36778 /**
36779  * Placement constant - The resizing element is positioned under splitter element
36780  * @static
36781  * @type Number
36782  */
36783 Roo.bootstrap.SplitBar.BOTTOM = 4;
36784 Roo.namespace("Roo.bootstrap.layout");/*
36785  * Based on:
36786  * Ext JS Library 1.1.1
36787  * Copyright(c) 2006-2007, Ext JS, LLC.
36788  *
36789  * Originally Released Under LGPL - original licence link has changed is not relivant.
36790  *
36791  * Fork - LGPL
36792  * <script type="text/javascript">
36793  */
36794
36795 /**
36796  * @class Roo.bootstrap.layout.Manager
36797  * @extends Roo.bootstrap.Component
36798  * Base class for layout managers.
36799  */
36800 Roo.bootstrap.layout.Manager = function(config)
36801 {
36802     Roo.bootstrap.layout.Manager.superclass.constructor.call(this,config);
36803
36804
36805
36806
36807
36808     /** false to disable window resize monitoring @type Boolean */
36809     this.monitorWindowResize = true;
36810     this.regions = {};
36811     this.addEvents({
36812         /**
36813          * @event layout
36814          * Fires when a layout is performed.
36815          * @param {Roo.LayoutManager} this
36816          */
36817         "layout" : true,
36818         /**
36819          * @event regionresized
36820          * Fires when the user resizes a region.
36821          * @param {Roo.LayoutRegion} region The resized region
36822          * @param {Number} newSize The new size (width for east/west, height for north/south)
36823          */
36824         "regionresized" : true,
36825         /**
36826          * @event regioncollapsed
36827          * Fires when a region is collapsed.
36828          * @param {Roo.LayoutRegion} region The collapsed region
36829          */
36830         "regioncollapsed" : true,
36831         /**
36832          * @event regionexpanded
36833          * Fires when a region is expanded.
36834          * @param {Roo.LayoutRegion} region The expanded region
36835          */
36836         "regionexpanded" : true
36837     });
36838     this.updating = false;
36839
36840     if (config.el) {
36841         this.el = Roo.get(config.el);
36842         this.initEvents();
36843     }
36844
36845 };
36846
36847 Roo.extend(Roo.bootstrap.layout.Manager, Roo.bootstrap.Component, {
36848
36849
36850     regions : null,
36851
36852     monitorWindowResize : true,
36853
36854
36855     updating : false,
36856
36857
36858     onRender : function(ct, position)
36859     {
36860         if(!this.el){
36861             this.el = Roo.get(ct);
36862             this.initEvents();
36863         }
36864         //this.fireEvent('render',this);
36865     },
36866
36867
36868     initEvents: function()
36869     {
36870
36871
36872         // ie scrollbar fix
36873         if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
36874             document.body.scroll = "no";
36875         }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
36876             this.el.position('relative');
36877         }
36878         this.id = this.el.id;
36879         this.el.addClass("roo-layout-container");
36880         Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
36881         if(this.el.dom != document.body ) {
36882             this.el.on('resize', this.layout,this);
36883             this.el.on('show', this.layout,this);
36884         }
36885
36886     },
36887
36888     /**
36889      * Returns true if this layout is currently being updated
36890      * @return {Boolean}
36891      */
36892     isUpdating : function(){
36893         return this.updating;
36894     },
36895
36896     /**
36897      * Suspend the LayoutManager from doing auto-layouts while
36898      * making multiple add or remove calls
36899      */
36900     beginUpdate : function(){
36901         this.updating = true;
36902     },
36903
36904     /**
36905      * Restore auto-layouts and optionally disable the manager from performing a layout
36906      * @param {Boolean} noLayout true to disable a layout update
36907      */
36908     endUpdate : function(noLayout){
36909         this.updating = false;
36910         if(!noLayout){
36911             this.layout();
36912         }
36913     },
36914
36915     layout: function(){
36916         // abstract...
36917     },
36918
36919     onRegionResized : function(region, newSize){
36920         this.fireEvent("regionresized", region, newSize);
36921         this.layout();
36922     },
36923
36924     onRegionCollapsed : function(region){
36925         this.fireEvent("regioncollapsed", region);
36926     },
36927
36928     onRegionExpanded : function(region){
36929         this.fireEvent("regionexpanded", region);
36930     },
36931
36932     /**
36933      * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
36934      * performs box-model adjustments.
36935      * @return {Object} The size as an object {width: (the width), height: (the height)}
36936      */
36937     getViewSize : function()
36938     {
36939         var size;
36940         if(this.el.dom != document.body){
36941             size = this.el.getSize();
36942         }else{
36943             size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
36944         }
36945         size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
36946         size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
36947         return size;
36948     },
36949
36950     /**
36951      * Returns the Element this layout is bound to.
36952      * @return {Roo.Element}
36953      */
36954     getEl : function(){
36955         return this.el;
36956     },
36957
36958     /**
36959      * Returns the specified region.
36960      * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
36961      * @return {Roo.LayoutRegion}
36962      */
36963     getRegion : function(target){
36964         return this.regions[target.toLowerCase()];
36965     },
36966
36967     onWindowResize : function(){
36968         if(this.monitorWindowResize){
36969             this.layout();
36970         }
36971     }
36972 });
36973 /*
36974  * Based on:
36975  * Ext JS Library 1.1.1
36976  * Copyright(c) 2006-2007, Ext JS, LLC.
36977  *
36978  * Originally Released Under LGPL - original licence link has changed is not relivant.
36979  *
36980  * Fork - LGPL
36981  * <script type="text/javascript">
36982  */
36983 /**
36984  * @class Roo.bootstrap.layout.Border
36985  * @extends Roo.bootstrap.layout.Manager
36986  * This class represents a common layout manager used in desktop applications. For screenshots and more details,
36987  * please see: examples/bootstrap/nested.html<br><br>
36988  
36989 <b>The container the layout is rendered into can be either the body element or any other element.
36990 If it is not the body element, the container needs to either be an absolute positioned element,
36991 or you will need to add "position:relative" to the css of the container.  You will also need to specify
36992 the container size if it is not the body element.</b>
36993
36994 * @constructor
36995 * Create a new Border
36996 * @param {Object} config Configuration options
36997  */
36998 Roo.bootstrap.layout.Border = function(config){
36999     config = config || {};
37000     Roo.bootstrap.layout.Border.superclass.constructor.call(this, config);
37001     
37002     
37003     
37004     Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
37005         if(config[region]){
37006             config[region].region = region;
37007             this.addRegion(config[region]);
37008         }
37009     },this);
37010     
37011 };
37012
37013 Roo.bootstrap.layout.Border.regions =  ["north","south","east","west","center"];
37014
37015 Roo.extend(Roo.bootstrap.layout.Border, Roo.bootstrap.layout.Manager, {
37016     
37017     parent : false, // this might point to a 'nest' or a ???
37018     
37019     /**
37020      * Creates and adds a new region if it doesn't already exist.
37021      * @param {String} target The target region key (north, south, east, west or center).
37022      * @param {Object} config The regions config object
37023      * @return {BorderLayoutRegion} The new region
37024      */
37025     addRegion : function(config)
37026     {
37027         if(!this.regions[config.region]){
37028             var r = this.factory(config);
37029             this.bindRegion(r);
37030         }
37031         return this.regions[config.region];
37032     },
37033
37034     // private (kinda)
37035     bindRegion : function(r){
37036         this.regions[r.config.region] = r;
37037         
37038         r.on("visibilitychange",    this.layout, this);
37039         r.on("paneladded",          this.layout, this);
37040         r.on("panelremoved",        this.layout, this);
37041         r.on("invalidated",         this.layout, this);
37042         r.on("resized",             this.onRegionResized, this);
37043         r.on("collapsed",           this.onRegionCollapsed, this);
37044         r.on("expanded",            this.onRegionExpanded, this);
37045     },
37046
37047     /**
37048      * Performs a layout update.
37049      */
37050     layout : function()
37051     {
37052         if(this.updating) {
37053             return;
37054         }
37055         
37056         // render all the rebions if they have not been done alreayd?
37057         Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
37058             if(this.regions[region] && !this.regions[region].bodyEl){
37059                 this.regions[region].onRender(this.el)
37060             }
37061         },this);
37062         
37063         var size = this.getViewSize();
37064         var w = size.width;
37065         var h = size.height;
37066         var centerW = w;
37067         var centerH = h;
37068         var centerY = 0;
37069         var centerX = 0;
37070         //var x = 0, y = 0;
37071
37072         var rs = this.regions;
37073         var north = rs["north"];
37074         var south = rs["south"]; 
37075         var west = rs["west"];
37076         var east = rs["east"];
37077         var center = rs["center"];
37078         //if(this.hideOnLayout){ // not supported anymore
37079             //c.el.setStyle("display", "none");
37080         //}
37081         if(north && north.isVisible()){
37082             var b = north.getBox();
37083             var m = north.getMargins();
37084             b.width = w - (m.left+m.right);
37085             b.x = m.left;
37086             b.y = m.top;
37087             centerY = b.height + b.y + m.bottom;
37088             centerH -= centerY;
37089             north.updateBox(this.safeBox(b));
37090         }
37091         if(south && south.isVisible()){
37092             var b = south.getBox();
37093             var m = south.getMargins();
37094             b.width = w - (m.left+m.right);
37095             b.x = m.left;
37096             var totalHeight = (b.height + m.top + m.bottom);
37097             b.y = h - totalHeight + m.top;
37098             centerH -= totalHeight;
37099             south.updateBox(this.safeBox(b));
37100         }
37101         if(west && west.isVisible()){
37102             var b = west.getBox();
37103             var m = west.getMargins();
37104             b.height = centerH - (m.top+m.bottom);
37105             b.x = m.left;
37106             b.y = centerY + m.top;
37107             var totalWidth = (b.width + m.left + m.right);
37108             centerX += totalWidth;
37109             centerW -= totalWidth;
37110             west.updateBox(this.safeBox(b));
37111         }
37112         if(east && east.isVisible()){
37113             var b = east.getBox();
37114             var m = east.getMargins();
37115             b.height = centerH - (m.top+m.bottom);
37116             var totalWidth = (b.width + m.left + m.right);
37117             b.x = w - totalWidth + m.left;
37118             b.y = centerY + m.top;
37119             centerW -= totalWidth;
37120             east.updateBox(this.safeBox(b));
37121         }
37122         if(center){
37123             var m = center.getMargins();
37124             var centerBox = {
37125                 x: centerX + m.left,
37126                 y: centerY + m.top,
37127                 width: centerW - (m.left+m.right),
37128                 height: centerH - (m.top+m.bottom)
37129             };
37130             //if(this.hideOnLayout){
37131                 //center.el.setStyle("display", "block");
37132             //}
37133             center.updateBox(this.safeBox(centerBox));
37134         }
37135         this.el.repaint();
37136         this.fireEvent("layout", this);
37137     },
37138
37139     // private
37140     safeBox : function(box){
37141         box.width = Math.max(0, box.width);
37142         box.height = Math.max(0, box.height);
37143         return box;
37144     },
37145
37146     /**
37147      * Adds a ContentPanel (or subclass) to this layout.
37148      * @param {String} target The target region key (north, south, east, west or center).
37149      * @param {Roo.ContentPanel} panel The panel to add
37150      * @return {Roo.ContentPanel} The added panel
37151      */
37152     add : function(target, panel){
37153          
37154         target = target.toLowerCase();
37155         return this.regions[target].add(panel);
37156     },
37157
37158     /**
37159      * Remove a ContentPanel (or subclass) to this layout.
37160      * @param {String} target The target region key (north, south, east, west or center).
37161      * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
37162      * @return {Roo.ContentPanel} The removed panel
37163      */
37164     remove : function(target, panel){
37165         target = target.toLowerCase();
37166         return this.regions[target].remove(panel);
37167     },
37168
37169     /**
37170      * Searches all regions for a panel with the specified id
37171      * @param {String} panelId
37172      * @return {Roo.ContentPanel} The panel or null if it wasn't found
37173      */
37174     findPanel : function(panelId){
37175         var rs = this.regions;
37176         for(var target in rs){
37177             if(typeof rs[target] != "function"){
37178                 var p = rs[target].getPanel(panelId);
37179                 if(p){
37180                     return p;
37181                 }
37182             }
37183         }
37184         return null;
37185     },
37186
37187     /**
37188      * Searches all regions for a panel with the specified id and activates (shows) it.
37189      * @param {String/ContentPanel} panelId The panels id or the panel itself
37190      * @return {Roo.ContentPanel} The shown panel or null
37191      */
37192     showPanel : function(panelId) {
37193       var rs = this.regions;
37194       for(var target in rs){
37195          var r = rs[target];
37196          if(typeof r != "function"){
37197             if(r.hasPanel(panelId)){
37198                return r.showPanel(panelId);
37199             }
37200          }
37201       }
37202       return null;
37203    },
37204
37205    /**
37206      * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
37207      * @param {Roo.state.Provider} provider (optional) An alternate state provider
37208      */
37209    /*
37210     restoreState : function(provider){
37211         if(!provider){
37212             provider = Roo.state.Manager;
37213         }
37214         var sm = new Roo.LayoutStateManager();
37215         sm.init(this, provider);
37216     },
37217 */
37218  
37219  
37220     /**
37221      * Adds a xtype elements to the layout.
37222      * <pre><code>
37223
37224 layout.addxtype({
37225        xtype : 'ContentPanel',
37226        region: 'west',
37227        items: [ .... ]
37228    }
37229 );
37230
37231 layout.addxtype({
37232         xtype : 'NestedLayoutPanel',
37233         region: 'west',
37234         layout: {
37235            center: { },
37236            west: { }   
37237         },
37238         items : [ ... list of content panels or nested layout panels.. ]
37239    }
37240 );
37241 </code></pre>
37242      * @param {Object} cfg Xtype definition of item to add.
37243      */
37244     addxtype : function(cfg)
37245     {
37246         // basically accepts a pannel...
37247         // can accept a layout region..!?!?
37248         //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
37249         
37250         
37251         // theory?  children can only be panels??
37252         
37253         //if (!cfg.xtype.match(/Panel$/)) {
37254         //    return false;
37255         //}
37256         var ret = false;
37257         
37258         if (typeof(cfg.region) == 'undefined') {
37259             Roo.log("Failed to add Panel, region was not set");
37260             Roo.log(cfg);
37261             return false;
37262         }
37263         var region = cfg.region;
37264         delete cfg.region;
37265         
37266           
37267         var xitems = [];
37268         if (cfg.items) {
37269             xitems = cfg.items;
37270             delete cfg.items;
37271         }
37272         var nb = false;
37273         
37274         if ( region == 'center') {
37275             Roo.log("Center: " + cfg.title);
37276         }
37277         
37278         
37279         switch(cfg.xtype) 
37280         {
37281             case 'Content':  // ContentPanel (el, cfg)
37282             case 'Scroll':  // ContentPanel (el, cfg)
37283             case 'View': 
37284                 cfg.autoCreate = cfg.autoCreate || true;
37285                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
37286                 //} else {
37287                 //    var el = this.el.createChild();
37288                 //    ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
37289                 //}
37290                 
37291                 this.add(region, ret);
37292                 break;
37293             
37294             /*
37295             case 'TreePanel': // our new panel!
37296                 cfg.el = this.el.createChild();
37297                 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
37298                 this.add(region, ret);
37299                 break;
37300             */
37301             
37302             case 'Nest': 
37303                 // create a new Layout (which is  a Border Layout...
37304                 
37305                 var clayout = cfg.layout;
37306                 clayout.el  = this.el.createChild();
37307                 clayout.items   = clayout.items  || [];
37308                 
37309                 delete cfg.layout;
37310                 
37311                 // replace this exitems with the clayout ones..
37312                 xitems = clayout.items;
37313                  
37314                 // force background off if it's in center...
37315                 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
37316                     cfg.background = false;
37317                 }
37318                 cfg.layout  = new Roo.bootstrap.layout.Border(clayout);
37319                 
37320                 
37321                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
37322                 //console.log('adding nested layout panel '  + cfg.toSource());
37323                 this.add(region, ret);
37324                 nb = {}; /// find first...
37325                 break;
37326             
37327             case 'Grid':
37328                 
37329                 // needs grid and region
37330                 
37331                 //var el = this.getRegion(region).el.createChild();
37332                 /*
37333                  *var el = this.el.createChild();
37334                 // create the grid first...
37335                 cfg.grid.container = el;
37336                 cfg.grid = new cfg.grid.xns[cfg.grid.xtype](cfg.grid);
37337                 */
37338                 
37339                 if (region == 'center' && this.active ) {
37340                     cfg.background = false;
37341                 }
37342                 
37343                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
37344                 
37345                 this.add(region, ret);
37346                 /*
37347                 if (cfg.background) {
37348                     // render grid on panel activation (if panel background)
37349                     ret.on('activate', function(gp) {
37350                         if (!gp.grid.rendered) {
37351                     //        gp.grid.render(el);
37352                         }
37353                     });
37354                 } else {
37355                   //  cfg.grid.render(el);
37356                 }
37357                 */
37358                 break;
37359            
37360            
37361             case 'Border': // it can get called on it'self... - might need to check if this is fixed?
37362                 // it was the old xcomponent building that caused this before.
37363                 // espeically if border is the top element in the tree.
37364                 ret = this;
37365                 break; 
37366                 
37367                     
37368                 
37369                 
37370                 
37371             default:
37372                 /*
37373                 if (typeof(Roo[cfg.xtype]) != 'undefined') {
37374                     
37375                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
37376                     this.add(region, ret);
37377                 } else {
37378                 */
37379                     Roo.log(cfg);
37380                     throw "Can not add '" + cfg.xtype + "' to Border";
37381                     return null;
37382              
37383                                 
37384              
37385         }
37386         this.beginUpdate();
37387         // add children..
37388         var region = '';
37389         var abn = {};
37390         Roo.each(xitems, function(i)  {
37391             region = nb && i.region ? i.region : false;
37392             
37393             var add = ret.addxtype(i);
37394            
37395             if (region) {
37396                 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
37397                 if (!i.background) {
37398                     abn[region] = nb[region] ;
37399                 }
37400             }
37401             
37402         });
37403         this.endUpdate();
37404
37405         // make the last non-background panel active..
37406         //if (nb) { Roo.log(abn); }
37407         if (nb) {
37408             
37409             for(var r in abn) {
37410                 region = this.getRegion(r);
37411                 if (region) {
37412                     // tried using nb[r], but it does not work..
37413                      
37414                     region.showPanel(abn[r]);
37415                    
37416                 }
37417             }
37418         }
37419         return ret;
37420         
37421     },
37422     
37423     
37424 // private
37425     factory : function(cfg)
37426     {
37427         
37428         var validRegions = Roo.bootstrap.layout.Border.regions;
37429
37430         var target = cfg.region;
37431         cfg.mgr = this;
37432         
37433         var r = Roo.bootstrap.layout;
37434         Roo.log(target);
37435         switch(target){
37436             case "north":
37437                 return new r.North(cfg);
37438             case "south":
37439                 return new r.South(cfg);
37440             case "east":
37441                 return new r.East(cfg);
37442             case "west":
37443                 return new r.West(cfg);
37444             case "center":
37445                 return new r.Center(cfg);
37446         }
37447         throw 'Layout region "'+target+'" not supported.';
37448     }
37449     
37450     
37451 });
37452  /*
37453  * Based on:
37454  * Ext JS Library 1.1.1
37455  * Copyright(c) 2006-2007, Ext JS, LLC.
37456  *
37457  * Originally Released Under LGPL - original licence link has changed is not relivant.
37458  *
37459  * Fork - LGPL
37460  * <script type="text/javascript">
37461  */
37462  
37463 /**
37464  * @class Roo.bootstrap.layout.Basic
37465  * @extends Roo.util.Observable
37466  * This class represents a lightweight region in a layout manager. This region does not move dom nodes
37467  * and does not have a titlebar, tabs or any other features. All it does is size and position 
37468  * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
37469  * @cfg {Roo.bootstrap.layout.Manager}   mgr The manager
37470  * @cfg {string}   region  the region that it inhabits..
37471  * @cfg {bool}   skipConfig skip config?
37472  * 
37473
37474  */
37475 Roo.bootstrap.layout.Basic = function(config){
37476     
37477     this.mgr = config.mgr;
37478     
37479     this.position = config.region;
37480     
37481     var skipConfig = config.skipConfig;
37482     
37483     this.events = {
37484         /**
37485          * @scope Roo.BasicLayoutRegion
37486          */
37487         
37488         /**
37489          * @event beforeremove
37490          * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
37491          * @param {Roo.LayoutRegion} this
37492          * @param {Roo.ContentPanel} panel The panel
37493          * @param {Object} e The cancel event object
37494          */
37495         "beforeremove" : true,
37496         /**
37497          * @event invalidated
37498          * Fires when the layout for this region is changed.
37499          * @param {Roo.LayoutRegion} this
37500          */
37501         "invalidated" : true,
37502         /**
37503          * @event visibilitychange
37504          * Fires when this region is shown or hidden 
37505          * @param {Roo.LayoutRegion} this
37506          * @param {Boolean} visibility true or false
37507          */
37508         "visibilitychange" : true,
37509         /**
37510          * @event paneladded
37511          * Fires when a panel is added. 
37512          * @param {Roo.LayoutRegion} this
37513          * @param {Roo.ContentPanel} panel The panel
37514          */
37515         "paneladded" : true,
37516         /**
37517          * @event panelremoved
37518          * Fires when a panel is removed. 
37519          * @param {Roo.LayoutRegion} this
37520          * @param {Roo.ContentPanel} panel The panel
37521          */
37522         "panelremoved" : true,
37523         /**
37524          * @event beforecollapse
37525          * Fires when this region before collapse.
37526          * @param {Roo.LayoutRegion} this
37527          */
37528         "beforecollapse" : true,
37529         /**
37530          * @event collapsed
37531          * Fires when this region is collapsed.
37532          * @param {Roo.LayoutRegion} this
37533          */
37534         "collapsed" : true,
37535         /**
37536          * @event expanded
37537          * Fires when this region is expanded.
37538          * @param {Roo.LayoutRegion} this
37539          */
37540         "expanded" : true,
37541         /**
37542          * @event slideshow
37543          * Fires when this region is slid into view.
37544          * @param {Roo.LayoutRegion} this
37545          */
37546         "slideshow" : true,
37547         /**
37548          * @event slidehide
37549          * Fires when this region slides out of view. 
37550          * @param {Roo.LayoutRegion} this
37551          */
37552         "slidehide" : true,
37553         /**
37554          * @event panelactivated
37555          * Fires when a panel is activated. 
37556          * @param {Roo.LayoutRegion} this
37557          * @param {Roo.ContentPanel} panel The activated panel
37558          */
37559         "panelactivated" : true,
37560         /**
37561          * @event resized
37562          * Fires when the user resizes this region. 
37563          * @param {Roo.LayoutRegion} this
37564          * @param {Number} newSize The new size (width for east/west, height for north/south)
37565          */
37566         "resized" : true
37567     };
37568     /** A collection of panels in this region. @type Roo.util.MixedCollection */
37569     this.panels = new Roo.util.MixedCollection();
37570     this.panels.getKey = this.getPanelId.createDelegate(this);
37571     this.box = null;
37572     this.activePanel = null;
37573     // ensure listeners are added...
37574     
37575     if (config.listeners || config.events) {
37576         Roo.bootstrap.layout.Basic.superclass.constructor.call(this, {
37577             listeners : config.listeners || {},
37578             events : config.events || {}
37579         });
37580     }
37581     
37582     if(skipConfig !== true){
37583         this.applyConfig(config);
37584     }
37585 };
37586
37587 Roo.extend(Roo.bootstrap.layout.Basic, Roo.util.Observable,
37588 {
37589     getPanelId : function(p){
37590         return p.getId();
37591     },
37592     
37593     applyConfig : function(config){
37594         this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
37595         this.config = config;
37596         
37597     },
37598     
37599     /**
37600      * Resizes the region to the specified size. For vertical regions (west, east) this adjusts 
37601      * the width, for horizontal (north, south) the height.
37602      * @param {Number} newSize The new width or height
37603      */
37604     resizeTo : function(newSize){
37605         var el = this.el ? this.el :
37606                  (this.activePanel ? this.activePanel.getEl() : null);
37607         if(el){
37608             switch(this.position){
37609                 case "east":
37610                 case "west":
37611                     el.setWidth(newSize);
37612                     this.fireEvent("resized", this, newSize);
37613                 break;
37614                 case "north":
37615                 case "south":
37616                     el.setHeight(newSize);
37617                     this.fireEvent("resized", this, newSize);
37618                 break;                
37619             }
37620         }
37621     },
37622     
37623     getBox : function(){
37624         return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
37625     },
37626     
37627     getMargins : function(){
37628         return this.margins;
37629     },
37630     
37631     updateBox : function(box){
37632         this.box = box;
37633         var el = this.activePanel.getEl();
37634         el.dom.style.left = box.x + "px";
37635         el.dom.style.top = box.y + "px";
37636         this.activePanel.setSize(box.width, box.height);
37637     },
37638     
37639     /**
37640      * Returns the container element for this region.
37641      * @return {Roo.Element}
37642      */
37643     getEl : function(){
37644         return this.activePanel;
37645     },
37646     
37647     /**
37648      * Returns true if this region is currently visible.
37649      * @return {Boolean}
37650      */
37651     isVisible : function(){
37652         return this.activePanel ? true : false;
37653     },
37654     
37655     setActivePanel : function(panel){
37656         panel = this.getPanel(panel);
37657         if(this.activePanel && this.activePanel != panel){
37658             this.activePanel.setActiveState(false);
37659             this.activePanel.getEl().setLeftTop(-10000,-10000);
37660         }
37661         this.activePanel = panel;
37662         panel.setActiveState(true);
37663         if(this.box){
37664             panel.setSize(this.box.width, this.box.height);
37665         }
37666         this.fireEvent("panelactivated", this, panel);
37667         this.fireEvent("invalidated");
37668     },
37669     
37670     /**
37671      * Show the specified panel.
37672      * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
37673      * @return {Roo.ContentPanel} The shown panel or null
37674      */
37675     showPanel : function(panel){
37676         panel = this.getPanel(panel);
37677         if(panel){
37678             this.setActivePanel(panel);
37679         }
37680         return panel;
37681     },
37682     
37683     /**
37684      * Get the active panel for this region.
37685      * @return {Roo.ContentPanel} The active panel or null
37686      */
37687     getActivePanel : function(){
37688         return this.activePanel;
37689     },
37690     
37691     /**
37692      * Add the passed ContentPanel(s)
37693      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
37694      * @return {Roo.ContentPanel} The panel added (if only one was added)
37695      */
37696     add : function(panel){
37697         if(arguments.length > 1){
37698             for(var i = 0, len = arguments.length; i < len; i++) {
37699                 this.add(arguments[i]);
37700             }
37701             return null;
37702         }
37703         if(this.hasPanel(panel)){
37704             this.showPanel(panel);
37705             return panel;
37706         }
37707         var el = panel.getEl();
37708         if(el.dom.parentNode != this.mgr.el.dom){
37709             this.mgr.el.dom.appendChild(el.dom);
37710         }
37711         if(panel.setRegion){
37712             panel.setRegion(this);
37713         }
37714         this.panels.add(panel);
37715         el.setStyle("position", "absolute");
37716         if(!panel.background){
37717             this.setActivePanel(panel);
37718             if(this.config.initialSize && this.panels.getCount()==1){
37719                 this.resizeTo(this.config.initialSize);
37720             }
37721         }
37722         this.fireEvent("paneladded", this, panel);
37723         return panel;
37724     },
37725     
37726     /**
37727      * Returns true if the panel is in this region.
37728      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
37729      * @return {Boolean}
37730      */
37731     hasPanel : function(panel){
37732         if(typeof panel == "object"){ // must be panel obj
37733             panel = panel.getId();
37734         }
37735         return this.getPanel(panel) ? true : false;
37736     },
37737     
37738     /**
37739      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
37740      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
37741      * @param {Boolean} preservePanel Overrides the config preservePanel option
37742      * @return {Roo.ContentPanel} The panel that was removed
37743      */
37744     remove : function(panel, preservePanel){
37745         panel = this.getPanel(panel);
37746         if(!panel){
37747             return null;
37748         }
37749         var e = {};
37750         this.fireEvent("beforeremove", this, panel, e);
37751         if(e.cancel === true){
37752             return null;
37753         }
37754         var panelId = panel.getId();
37755         this.panels.removeKey(panelId);
37756         return panel;
37757     },
37758     
37759     /**
37760      * Returns the panel specified or null if it's not in this region.
37761      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
37762      * @return {Roo.ContentPanel}
37763      */
37764     getPanel : function(id){
37765         if(typeof id == "object"){ // must be panel obj
37766             return id;
37767         }
37768         return this.panels.get(id);
37769     },
37770     
37771     /**
37772      * Returns this regions position (north/south/east/west/center).
37773      * @return {String} 
37774      */
37775     getPosition: function(){
37776         return this.position;    
37777     }
37778 });/*
37779  * Based on:
37780  * Ext JS Library 1.1.1
37781  * Copyright(c) 2006-2007, Ext JS, LLC.
37782  *
37783  * Originally Released Under LGPL - original licence link has changed is not relivant.
37784  *
37785  * Fork - LGPL
37786  * <script type="text/javascript">
37787  */
37788  
37789 /**
37790  * @class Roo.bootstrap.layout.Region
37791  * @extends Roo.bootstrap.layout.Basic
37792  * This class represents a region in a layout manager.
37793  
37794  * @cfg {Object}    margins         Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
37795  * @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})
37796  * @cfg {String}    tabPosition     (top|bottom) "top" or "bottom" (defaults to "bottom")
37797  * @cfg {Boolean}   alwaysShowTabs  True to always display tabs even when there is only 1 panel (defaults to false)
37798  * @cfg {Boolean}   autoScroll      True to enable overflow scrolling (defaults to false)
37799  * @cfg {Boolean}   titlebar        True to display a title bar (defaults to true)
37800  * @cfg {String}    title           The title for the region (overrides panel titles)
37801  * @cfg {Boolean}   animate         True to animate expand/collapse (defaults to false)
37802  * @cfg {Boolean}   autoHide        False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
37803  * @cfg {Boolean}   preservePanels  True to preserve removed panels so they can be readded later (defaults to false)
37804  * @cfg {Boolean}   closeOnTab      True to place the close icon on the tabs instead of the region titlebar (defaults to false)
37805  * @cfg {Boolean}   hideTabs        True to hide the tab strip (defaults to false)
37806  * @cfg {Boolean}   resizeTabs      True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
37807  *                      the space available, similar to FireFox 1.5 tabs (defaults to false)
37808  * @cfg {Number}    minTabWidth     The minimum tab width (defaults to 40)
37809  * @cfg {Number}    preferredTabWidth The preferred tab width (defaults to 150)
37810  * @cfg {String}    overflow       (hidden|visible) if you have menus in the region, then you need to set this to visible.
37811
37812  * @cfg {Boolean}   hidden          True to start the region hidden (defaults to false)
37813  * @cfg {Boolean}   hideWhenEmpty   True to hide the region when it has no panels
37814  * @cfg {Boolean}   disableTabTips  True to disable tab tooltips
37815  * @cfg {Number}    width           For East/West panels
37816  * @cfg {Number}    height          For North/South panels
37817  * @cfg {Boolean}   split           To show the splitter
37818  * @cfg {Boolean}   toolbar         xtype configuration for a toolbar - shows on right of tabbar
37819  * 
37820  * @cfg {string}   cls             Extra CSS classes to add to region
37821  * 
37822  * @cfg {Roo.bootstrap.layout.Manager}   mgr The manager
37823  * @cfg {string}   region  the region that it inhabits..
37824  *
37825
37826  * @xxxcfg {Boolean}   collapsible     DISABLED False to disable collapsing (defaults to true)
37827  * @xxxcfg {Boolean}   collapsed       DISABLED True to set the initial display to collapsed (defaults to false)
37828
37829  * @xxxcfg {String}    collapsedTitle  DISABLED Optional string message to display in the collapsed block of a north or south region
37830  * @xxxxcfg {Boolean}   floatable       DISABLED False to disable floating (defaults to true)
37831  * @xxxxcfg {Boolean}   showPin         True to show a pin button NOT SUPPORTED YET
37832  */
37833 Roo.bootstrap.layout.Region = function(config)
37834 {
37835     this.applyConfig(config);
37836
37837     var mgr = config.mgr;
37838     var pos = config.region;
37839     config.skipConfig = true;
37840     Roo.bootstrap.layout.Region.superclass.constructor.call(this, config);
37841     
37842     if (mgr.el) {
37843         this.onRender(mgr.el);   
37844     }
37845      
37846     this.visible = true;
37847     this.collapsed = false;
37848     this.unrendered_panels = [];
37849 };
37850
37851 Roo.extend(Roo.bootstrap.layout.Region, Roo.bootstrap.layout.Basic, {
37852
37853     position: '', // set by wrapper (eg. north/south etc..)
37854     unrendered_panels : null,  // unrendered panels.
37855     
37856     tabPosition : false,
37857     
37858     mgr: false, // points to 'Border'
37859     
37860     
37861     createBody : function(){
37862         /** This region's body element 
37863         * @type Roo.Element */
37864         this.bodyEl = this.el.createChild({
37865                 tag: "div",
37866                 cls: "roo-layout-panel-body tab-content" // bootstrap added...
37867         });
37868     },
37869
37870     onRender: function(ctr, pos)
37871     {
37872         var dh = Roo.DomHelper;
37873         /** This region's container element 
37874         * @type Roo.Element */
37875         this.el = dh.append(ctr.dom, {
37876                 tag: "div",
37877                 cls: (this.config.cls || '') + " roo-layout-region roo-layout-panel roo-layout-panel-" + this.position
37878             }, true);
37879         /** This region's title element 
37880         * @type Roo.Element */
37881     
37882         this.titleEl = dh.append(this.el.dom,  {
37883                 tag: "div",
37884                 unselectable: "on",
37885                 cls: "roo-unselectable roo-layout-panel-hd breadcrumb roo-layout-title-" + this.position,
37886                 children:[
37887                     {tag: "span", cls: "roo-unselectable roo-layout-panel-hd-text", unselectable: "on", html: "&#160;"},
37888                     {tag: "div", cls: "roo-unselectable roo-layout-panel-hd-tools", unselectable: "on"}
37889                 ]
37890             }, true);
37891         
37892         this.titleEl.enableDisplayMode();
37893         /** This region's title text element 
37894         * @type HTMLElement */
37895         this.titleTextEl = this.titleEl.dom.firstChild;
37896         this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
37897         /*
37898         this.closeBtn = this.createTool(this.tools.dom, "roo-layout-close");
37899         this.closeBtn.enableDisplayMode();
37900         this.closeBtn.on("click", this.closeClicked, this);
37901         this.closeBtn.hide();
37902     */
37903         this.createBody(this.config);
37904         if(this.config.hideWhenEmpty){
37905             this.hide();
37906             this.on("paneladded", this.validateVisibility, this);
37907             this.on("panelremoved", this.validateVisibility, this);
37908         }
37909         if(this.autoScroll){
37910             this.bodyEl.setStyle("overflow", "auto");
37911         }else{
37912             this.bodyEl.setStyle("overflow", this.config.overflow || 'hidden');
37913         }
37914         //if(c.titlebar !== false){
37915             if((!this.config.titlebar && !this.config.title) || this.config.titlebar === false){
37916                 this.titleEl.hide();
37917             }else{
37918                 this.titleEl.show();
37919                 if(this.config.title){
37920                     this.titleTextEl.innerHTML = this.config.title;
37921                 }
37922             }
37923         //}
37924         if(this.config.collapsed){
37925             this.collapse(true);
37926         }
37927         if(this.config.hidden){
37928             this.hide();
37929         }
37930         
37931         if (this.unrendered_panels && this.unrendered_panels.length) {
37932             for (var i =0;i< this.unrendered_panels.length; i++) {
37933                 this.add(this.unrendered_panels[i]);
37934             }
37935             this.unrendered_panels = null;
37936             
37937         }
37938         
37939     },
37940     
37941     applyConfig : function(c)
37942     {
37943         /*
37944          *if(c.collapsible && this.position != "center" && !this.collapsedEl){
37945             var dh = Roo.DomHelper;
37946             if(c.titlebar !== false){
37947                 this.collapseBtn = this.createTool(this.tools.dom, "roo-layout-collapse-"+this.position);
37948                 this.collapseBtn.on("click", this.collapse, this);
37949                 this.collapseBtn.enableDisplayMode();
37950                 /*
37951                 if(c.showPin === true || this.showPin){
37952                     this.stickBtn = this.createTool(this.tools.dom, "roo-layout-stick");
37953                     this.stickBtn.enableDisplayMode();
37954                     this.stickBtn.on("click", this.expand, this);
37955                     this.stickBtn.hide();
37956                 }
37957                 
37958             }
37959             */
37960             /** This region's collapsed element
37961             * @type Roo.Element */
37962             /*
37963              *
37964             this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
37965                 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
37966             ]}, true);
37967             
37968             if(c.floatable !== false){
37969                this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
37970                this.collapsedEl.on("click", this.collapseClick, this);
37971             }
37972
37973             if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
37974                 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
37975                    id: "message", unselectable: "on", style:{"float":"left"}});
37976                this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
37977              }
37978             this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
37979             this.expandBtn.on("click", this.expand, this);
37980             
37981         }
37982         
37983         if(this.collapseBtn){
37984             this.collapseBtn.setVisible(c.collapsible == true);
37985         }
37986         
37987         this.cmargins = c.cmargins || this.cmargins ||
37988                          (this.position == "west" || this.position == "east" ?
37989                              {top: 0, left: 2, right:2, bottom: 0} :
37990                              {top: 2, left: 0, right:0, bottom: 2});
37991         */
37992         this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
37993         
37994         
37995         this.tabPosition = [ 'top','bottom', 'west'].indexOf(c.tabPosition) > -1 ? c.tabPosition : "top";
37996         
37997         this.autoScroll = c.autoScroll || false;
37998         
37999         
38000        
38001         
38002         this.duration = c.duration || .30;
38003         this.slideDuration = c.slideDuration || .45;
38004         this.config = c;
38005        
38006     },
38007     /**
38008      * Returns true if this region is currently visible.
38009      * @return {Boolean}
38010      */
38011     isVisible : function(){
38012         return this.visible;
38013     },
38014
38015     /**
38016      * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
38017      * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&amp;#160;")
38018      */
38019     //setCollapsedTitle : function(title){
38020     //    title = title || "&#160;";
38021      //   if(this.collapsedTitleTextEl){
38022       //      this.collapsedTitleTextEl.innerHTML = title;
38023        // }
38024     //},
38025
38026     getBox : function(){
38027         var b;
38028       //  if(!this.collapsed){
38029             b = this.el.getBox(false, true);
38030        // }else{
38031           //  b = this.collapsedEl.getBox(false, true);
38032         //}
38033         return b;
38034     },
38035
38036     getMargins : function(){
38037         return this.margins;
38038         //return this.collapsed ? this.cmargins : this.margins;
38039     },
38040 /*
38041     highlight : function(){
38042         this.el.addClass("x-layout-panel-dragover");
38043     },
38044
38045     unhighlight : function(){
38046         this.el.removeClass("x-layout-panel-dragover");
38047     },
38048 */
38049     updateBox : function(box)
38050     {
38051         if (!this.bodyEl) {
38052             return; // not rendered yet..
38053         }
38054         
38055         this.box = box;
38056         if(!this.collapsed){
38057             this.el.dom.style.left = box.x + "px";
38058             this.el.dom.style.top = box.y + "px";
38059             this.updateBody(box.width, box.height);
38060         }else{
38061             this.collapsedEl.dom.style.left = box.x + "px";
38062             this.collapsedEl.dom.style.top = box.y + "px";
38063             this.collapsedEl.setSize(box.width, box.height);
38064         }
38065         if(this.tabs){
38066             this.tabs.autoSizeTabs();
38067         }
38068     },
38069
38070     updateBody : function(w, h)
38071     {
38072         if(w !== null){
38073             this.el.setWidth(w);
38074             w -= this.el.getBorderWidth("rl");
38075             if(this.config.adjustments){
38076                 w += this.config.adjustments[0];
38077             }
38078         }
38079         if(h !== null && h > 0){
38080             this.el.setHeight(h);
38081             h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
38082             h -= this.el.getBorderWidth("tb");
38083             if(this.config.adjustments){
38084                 h += this.config.adjustments[1];
38085             }
38086             this.bodyEl.setHeight(h);
38087             if(this.tabs){
38088                 h = this.tabs.syncHeight(h);
38089             }
38090         }
38091         if(this.panelSize){
38092             w = w !== null ? w : this.panelSize.width;
38093             h = h !== null ? h : this.panelSize.height;
38094         }
38095         if(this.activePanel){
38096             var el = this.activePanel.getEl();
38097             w = w !== null ? w : el.getWidth();
38098             h = h !== null ? h : el.getHeight();
38099             this.panelSize = {width: w, height: h};
38100             this.activePanel.setSize(w, h);
38101         }
38102         if(Roo.isIE && this.tabs){
38103             this.tabs.el.repaint();
38104         }
38105     },
38106
38107     /**
38108      * Returns the container element for this region.
38109      * @return {Roo.Element}
38110      */
38111     getEl : function(){
38112         return this.el;
38113     },
38114
38115     /**
38116      * Hides this region.
38117      */
38118     hide : function(){
38119         //if(!this.collapsed){
38120             this.el.dom.style.left = "-2000px";
38121             this.el.hide();
38122         //}else{
38123          //   this.collapsedEl.dom.style.left = "-2000px";
38124          //   this.collapsedEl.hide();
38125        // }
38126         this.visible = false;
38127         this.fireEvent("visibilitychange", this, false);
38128     },
38129
38130     /**
38131      * Shows this region if it was previously hidden.
38132      */
38133     show : function(){
38134         //if(!this.collapsed){
38135             this.el.show();
38136         //}else{
38137         //    this.collapsedEl.show();
38138        // }
38139         this.visible = true;
38140         this.fireEvent("visibilitychange", this, true);
38141     },
38142 /*
38143     closeClicked : function(){
38144         if(this.activePanel){
38145             this.remove(this.activePanel);
38146         }
38147     },
38148
38149     collapseClick : function(e){
38150         if(this.isSlid){
38151            e.stopPropagation();
38152            this.slideIn();
38153         }else{
38154            e.stopPropagation();
38155            this.slideOut();
38156         }
38157     },
38158 */
38159     /**
38160      * Collapses this region.
38161      * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
38162      */
38163     /*
38164     collapse : function(skipAnim, skipCheck = false){
38165         if(this.collapsed) {
38166             return;
38167         }
38168         
38169         if(skipCheck || this.fireEvent("beforecollapse", this) != false){
38170             
38171             this.collapsed = true;
38172             if(this.split){
38173                 this.split.el.hide();
38174             }
38175             if(this.config.animate && skipAnim !== true){
38176                 this.fireEvent("invalidated", this);
38177                 this.animateCollapse();
38178             }else{
38179                 this.el.setLocation(-20000,-20000);
38180                 this.el.hide();
38181                 this.collapsedEl.show();
38182                 this.fireEvent("collapsed", this);
38183                 this.fireEvent("invalidated", this);
38184             }
38185         }
38186         
38187     },
38188 */
38189     animateCollapse : function(){
38190         // overridden
38191     },
38192
38193     /**
38194      * Expands this region if it was previously collapsed.
38195      * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
38196      * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
38197      */
38198     /*
38199     expand : function(e, skipAnim){
38200         if(e) {
38201             e.stopPropagation();
38202         }
38203         if(!this.collapsed || this.el.hasActiveFx()) {
38204             return;
38205         }
38206         if(this.isSlid){
38207             this.afterSlideIn();
38208             skipAnim = true;
38209         }
38210         this.collapsed = false;
38211         if(this.config.animate && skipAnim !== true){
38212             this.animateExpand();
38213         }else{
38214             this.el.show();
38215             if(this.split){
38216                 this.split.el.show();
38217             }
38218             this.collapsedEl.setLocation(-2000,-2000);
38219             this.collapsedEl.hide();
38220             this.fireEvent("invalidated", this);
38221             this.fireEvent("expanded", this);
38222         }
38223     },
38224 */
38225     animateExpand : function(){
38226         // overridden
38227     },
38228
38229     initTabs : function()
38230     {
38231         //this.bodyEl.setStyle("overflow", "hidden"); -- this is set in render?
38232         
38233         var ts = new Roo.bootstrap.panel.Tabs({
38234             el: this.bodyEl.dom,
38235             region : this,
38236             tabPosition: this.tabPosition ? this.tabPosition  : 'top',
38237             disableTooltips: this.config.disableTabTips,
38238             toolbar : this.config.toolbar
38239         });
38240         
38241         if(this.config.hideTabs){
38242             ts.stripWrap.setDisplayed(false);
38243         }
38244         this.tabs = ts;
38245         ts.resizeTabs = this.config.resizeTabs === true;
38246         ts.minTabWidth = this.config.minTabWidth || 40;
38247         ts.maxTabWidth = this.config.maxTabWidth || 250;
38248         ts.preferredTabWidth = this.config.preferredTabWidth || 150;
38249         ts.monitorResize = false;
38250         //ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden"); // this is set in render?
38251         ts.bodyEl.addClass('roo-layout-tabs-body');
38252         this.panels.each(this.initPanelAsTab, this);
38253     },
38254
38255     initPanelAsTab : function(panel){
38256         var ti = this.tabs.addTab(
38257             panel.getEl().id,
38258             panel.getTitle(),
38259             null,
38260             this.config.closeOnTab && panel.isClosable(),
38261             panel.tpl
38262         );
38263         if(panel.tabTip !== undefined){
38264             ti.setTooltip(panel.tabTip);
38265         }
38266         ti.on("activate", function(){
38267               this.setActivePanel(panel);
38268         }, this);
38269         
38270         if(this.config.closeOnTab){
38271             ti.on("beforeclose", function(t, e){
38272                 e.cancel = true;
38273                 this.remove(panel);
38274             }, this);
38275         }
38276         
38277         panel.tabItem = ti;
38278         
38279         return ti;
38280     },
38281
38282     updatePanelTitle : function(panel, title)
38283     {
38284         if(this.activePanel == panel){
38285             this.updateTitle(title);
38286         }
38287         if(this.tabs){
38288             var ti = this.tabs.getTab(panel.getEl().id);
38289             ti.setText(title);
38290             if(panel.tabTip !== undefined){
38291                 ti.setTooltip(panel.tabTip);
38292             }
38293         }
38294     },
38295
38296     updateTitle : function(title){
38297         if(this.titleTextEl && !this.config.title){
38298             this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : "&#160;");
38299         }
38300     },
38301
38302     setActivePanel : function(panel)
38303     {
38304         panel = this.getPanel(panel);
38305         if(this.activePanel && this.activePanel != panel){
38306             if(this.activePanel.setActiveState(false) === false){
38307                 return;
38308             }
38309         }
38310         this.activePanel = panel;
38311         panel.setActiveState(true);
38312         if(this.panelSize){
38313             panel.setSize(this.panelSize.width, this.panelSize.height);
38314         }
38315         if(this.closeBtn){
38316             this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
38317         }
38318         this.updateTitle(panel.getTitle());
38319         if(this.tabs){
38320             this.fireEvent("invalidated", this);
38321         }
38322         this.fireEvent("panelactivated", this, panel);
38323     },
38324
38325     /**
38326      * Shows the specified panel.
38327      * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
38328      * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
38329      */
38330     showPanel : function(panel)
38331     {
38332         panel = this.getPanel(panel);
38333         if(panel){
38334             if(this.tabs){
38335                 var tab = this.tabs.getTab(panel.getEl().id);
38336                 if(tab.isHidden()){
38337                     this.tabs.unhideTab(tab.id);
38338                 }
38339                 tab.activate();
38340             }else{
38341                 this.setActivePanel(panel);
38342             }
38343         }
38344         return panel;
38345     },
38346
38347     /**
38348      * Get the active panel for this region.
38349      * @return {Roo.ContentPanel} The active panel or null
38350      */
38351     getActivePanel : function(){
38352         return this.activePanel;
38353     },
38354
38355     validateVisibility : function(){
38356         if(this.panels.getCount() < 1){
38357             this.updateTitle("&#160;");
38358             this.closeBtn.hide();
38359             this.hide();
38360         }else{
38361             if(!this.isVisible()){
38362                 this.show();
38363             }
38364         }
38365     },
38366
38367     /**
38368      * Adds the passed ContentPanel(s) to this region.
38369      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
38370      * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
38371      */
38372     add : function(panel)
38373     {
38374         if(arguments.length > 1){
38375             for(var i = 0, len = arguments.length; i < len; i++) {
38376                 this.add(arguments[i]);
38377             }
38378             return null;
38379         }
38380         
38381         // if we have not been rendered yet, then we can not really do much of this..
38382         if (!this.bodyEl) {
38383             this.unrendered_panels.push(panel);
38384             return panel;
38385         }
38386         
38387         
38388         
38389         
38390         if(this.hasPanel(panel)){
38391             this.showPanel(panel);
38392             return panel;
38393         }
38394         panel.setRegion(this);
38395         this.panels.add(panel);
38396        /* if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
38397             // sinle panel - no tab...?? would it not be better to render it with the tabs,
38398             // and hide them... ???
38399             this.bodyEl.dom.appendChild(panel.getEl().dom);
38400             if(panel.background !== true){
38401                 this.setActivePanel(panel);
38402             }
38403             this.fireEvent("paneladded", this, panel);
38404             return panel;
38405         }
38406         */
38407         if(!this.tabs){
38408             this.initTabs();
38409         }else{
38410             this.initPanelAsTab(panel);
38411         }
38412         
38413         
38414         if(panel.background !== true){
38415             this.tabs.activate(panel.getEl().id);
38416         }
38417         this.fireEvent("paneladded", this, panel);
38418         return panel;
38419     },
38420
38421     /**
38422      * Hides the tab for the specified panel.
38423      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
38424      */
38425     hidePanel : function(panel){
38426         if(this.tabs && (panel = this.getPanel(panel))){
38427             this.tabs.hideTab(panel.getEl().id);
38428         }
38429     },
38430
38431     /**
38432      * Unhides the tab for a previously hidden panel.
38433      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
38434      */
38435     unhidePanel : function(panel){
38436         if(this.tabs && (panel = this.getPanel(panel))){
38437             this.tabs.unhideTab(panel.getEl().id);
38438         }
38439     },
38440
38441     clearPanels : function(){
38442         while(this.panels.getCount() > 0){
38443              this.remove(this.panels.first());
38444         }
38445     },
38446
38447     /**
38448      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
38449      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
38450      * @param {Boolean} preservePanel Overrides the config preservePanel option
38451      * @return {Roo.ContentPanel} The panel that was removed
38452      */
38453     remove : function(panel, preservePanel)
38454     {
38455         panel = this.getPanel(panel);
38456         if(!panel){
38457             return null;
38458         }
38459         var e = {};
38460         this.fireEvent("beforeremove", this, panel, e);
38461         if(e.cancel === true){
38462             return null;
38463         }
38464         preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
38465         var panelId = panel.getId();
38466         this.panels.removeKey(panelId);
38467         if(preservePanel){
38468             document.body.appendChild(panel.getEl().dom);
38469         }
38470         if(this.tabs){
38471             this.tabs.removeTab(panel.getEl().id);
38472         }else if (!preservePanel){
38473             this.bodyEl.dom.removeChild(panel.getEl().dom);
38474         }
38475         if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
38476             var p = this.panels.first();
38477             var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
38478             tempEl.appendChild(p.getEl().dom);
38479             this.bodyEl.update("");
38480             this.bodyEl.dom.appendChild(p.getEl().dom);
38481             tempEl = null;
38482             this.updateTitle(p.getTitle());
38483             this.tabs = null;
38484             this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
38485             this.setActivePanel(p);
38486         }
38487         panel.setRegion(null);
38488         if(this.activePanel == panel){
38489             this.activePanel = null;
38490         }
38491         if(this.config.autoDestroy !== false && preservePanel !== true){
38492             try{panel.destroy();}catch(e){}
38493         }
38494         this.fireEvent("panelremoved", this, panel);
38495         return panel;
38496     },
38497
38498     /**
38499      * Returns the TabPanel component used by this region
38500      * @return {Roo.TabPanel}
38501      */
38502     getTabs : function(){
38503         return this.tabs;
38504     },
38505
38506     createTool : function(parentEl, className){
38507         var btn = Roo.DomHelper.append(parentEl, {
38508             tag: "div",
38509             cls: "x-layout-tools-button",
38510             children: [ {
38511                 tag: "div",
38512                 cls: "roo-layout-tools-button-inner " + className,
38513                 html: "&#160;"
38514             }]
38515         }, true);
38516         btn.addClassOnOver("roo-layout-tools-button-over");
38517         return btn;
38518     }
38519 });/*
38520  * Based on:
38521  * Ext JS Library 1.1.1
38522  * Copyright(c) 2006-2007, Ext JS, LLC.
38523  *
38524  * Originally Released Under LGPL - original licence link has changed is not relivant.
38525  *
38526  * Fork - LGPL
38527  * <script type="text/javascript">
38528  */
38529  
38530
38531
38532 /**
38533  * @class Roo.SplitLayoutRegion
38534  * @extends Roo.LayoutRegion
38535  * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
38536  */
38537 Roo.bootstrap.layout.Split = function(config){
38538     this.cursor = config.cursor;
38539     Roo.bootstrap.layout.Split.superclass.constructor.call(this, config);
38540 };
38541
38542 Roo.extend(Roo.bootstrap.layout.Split, Roo.bootstrap.layout.Region,
38543 {
38544     splitTip : "Drag to resize.",
38545     collapsibleSplitTip : "Drag to resize. Double click to hide.",
38546     useSplitTips : false,
38547
38548     applyConfig : function(config){
38549         Roo.bootstrap.layout.Split.superclass.applyConfig.call(this, config);
38550     },
38551     
38552     onRender : function(ctr,pos) {
38553         
38554         Roo.bootstrap.layout.Split.superclass.onRender.call(this, ctr,pos);
38555         if(!this.config.split){
38556             return;
38557         }
38558         if(!this.split){
38559             
38560             var splitEl = Roo.DomHelper.append(ctr.dom,  {
38561                             tag: "div",
38562                             id: this.el.id + "-split",
38563                             cls: "roo-layout-split roo-layout-split-"+this.position,
38564                             html: "&#160;"
38565             });
38566             /** The SplitBar for this region 
38567             * @type Roo.SplitBar */
38568             // does not exist yet...
38569             Roo.log([this.position, this.orientation]);
38570             
38571             this.split = new Roo.bootstrap.SplitBar({
38572                 dragElement : splitEl,
38573                 resizingElement: this.el,
38574                 orientation : this.orientation
38575             });
38576             
38577             this.split.on("moved", this.onSplitMove, this);
38578             this.split.useShim = this.config.useShim === true;
38579             this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
38580             if(this.useSplitTips){
38581                 this.split.el.dom.title = this.config.collapsible ? this.collapsibleSplitTip : this.splitTip;
38582             }
38583             //if(config.collapsible){
38584             //    this.split.el.on("dblclick", this.collapse,  this);
38585             //}
38586         }
38587         if(typeof this.config.minSize != "undefined"){
38588             this.split.minSize = this.config.minSize;
38589         }
38590         if(typeof this.config.maxSize != "undefined"){
38591             this.split.maxSize = this.config.maxSize;
38592         }
38593         if(this.config.hideWhenEmpty || this.config.hidden || this.config.collapsed){
38594             this.hideSplitter();
38595         }
38596         
38597     },
38598
38599     getHMaxSize : function(){
38600          var cmax = this.config.maxSize || 10000;
38601          var center = this.mgr.getRegion("center");
38602          return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
38603     },
38604
38605     getVMaxSize : function(){
38606          var cmax = this.config.maxSize || 10000;
38607          var center = this.mgr.getRegion("center");
38608          return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
38609     },
38610
38611     onSplitMove : function(split, newSize){
38612         this.fireEvent("resized", this, newSize);
38613     },
38614     
38615     /** 
38616      * Returns the {@link Roo.SplitBar} for this region.
38617      * @return {Roo.SplitBar}
38618      */
38619     getSplitBar : function(){
38620         return this.split;
38621     },
38622     
38623     hide : function(){
38624         this.hideSplitter();
38625         Roo.bootstrap.layout.Split.superclass.hide.call(this);
38626     },
38627
38628     hideSplitter : function(){
38629         if(this.split){
38630             this.split.el.setLocation(-2000,-2000);
38631             this.split.el.hide();
38632         }
38633     },
38634
38635     show : function(){
38636         if(this.split){
38637             this.split.el.show();
38638         }
38639         Roo.bootstrap.layout.Split.superclass.show.call(this);
38640     },
38641     
38642     beforeSlide: function(){
38643         if(Roo.isGecko){// firefox overflow auto bug workaround
38644             this.bodyEl.clip();
38645             if(this.tabs) {
38646                 this.tabs.bodyEl.clip();
38647             }
38648             if(this.activePanel){
38649                 this.activePanel.getEl().clip();
38650                 
38651                 if(this.activePanel.beforeSlide){
38652                     this.activePanel.beforeSlide();
38653                 }
38654             }
38655         }
38656     },
38657     
38658     afterSlide : function(){
38659         if(Roo.isGecko){// firefox overflow auto bug workaround
38660             this.bodyEl.unclip();
38661             if(this.tabs) {
38662                 this.tabs.bodyEl.unclip();
38663             }
38664             if(this.activePanel){
38665                 this.activePanel.getEl().unclip();
38666                 if(this.activePanel.afterSlide){
38667                     this.activePanel.afterSlide();
38668                 }
38669             }
38670         }
38671     },
38672
38673     initAutoHide : function(){
38674         if(this.autoHide !== false){
38675             if(!this.autoHideHd){
38676                 var st = new Roo.util.DelayedTask(this.slideIn, this);
38677                 this.autoHideHd = {
38678                     "mouseout": function(e){
38679                         if(!e.within(this.el, true)){
38680                             st.delay(500);
38681                         }
38682                     },
38683                     "mouseover" : function(e){
38684                         st.cancel();
38685                     },
38686                     scope : this
38687                 };
38688             }
38689             this.el.on(this.autoHideHd);
38690         }
38691     },
38692
38693     clearAutoHide : function(){
38694         if(this.autoHide !== false){
38695             this.el.un("mouseout", this.autoHideHd.mouseout);
38696             this.el.un("mouseover", this.autoHideHd.mouseover);
38697         }
38698     },
38699
38700     clearMonitor : function(){
38701         Roo.get(document).un("click", this.slideInIf, this);
38702     },
38703
38704     // these names are backwards but not changed for compat
38705     slideOut : function(){
38706         if(this.isSlid || this.el.hasActiveFx()){
38707             return;
38708         }
38709         this.isSlid = true;
38710         if(this.collapseBtn){
38711             this.collapseBtn.hide();
38712         }
38713         this.closeBtnState = this.closeBtn.getStyle('display');
38714         this.closeBtn.hide();
38715         if(this.stickBtn){
38716             this.stickBtn.show();
38717         }
38718         this.el.show();
38719         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
38720         this.beforeSlide();
38721         this.el.setStyle("z-index", 10001);
38722         this.el.slideIn(this.getSlideAnchor(), {
38723             callback: function(){
38724                 this.afterSlide();
38725                 this.initAutoHide();
38726                 Roo.get(document).on("click", this.slideInIf, this);
38727                 this.fireEvent("slideshow", this);
38728             },
38729             scope: this,
38730             block: true
38731         });
38732     },
38733
38734     afterSlideIn : function(){
38735         this.clearAutoHide();
38736         this.isSlid = false;
38737         this.clearMonitor();
38738         this.el.setStyle("z-index", "");
38739         if(this.collapseBtn){
38740             this.collapseBtn.show();
38741         }
38742         this.closeBtn.setStyle('display', this.closeBtnState);
38743         if(this.stickBtn){
38744             this.stickBtn.hide();
38745         }
38746         this.fireEvent("slidehide", this);
38747     },
38748
38749     slideIn : function(cb){
38750         if(!this.isSlid || this.el.hasActiveFx()){
38751             Roo.callback(cb);
38752             return;
38753         }
38754         this.isSlid = false;
38755         this.beforeSlide();
38756         this.el.slideOut(this.getSlideAnchor(), {
38757             callback: function(){
38758                 this.el.setLeftTop(-10000, -10000);
38759                 this.afterSlide();
38760                 this.afterSlideIn();
38761                 Roo.callback(cb);
38762             },
38763             scope: this,
38764             block: true
38765         });
38766     },
38767     
38768     slideInIf : function(e){
38769         if(!e.within(this.el)){
38770             this.slideIn();
38771         }
38772     },
38773
38774     animateCollapse : function(){
38775         this.beforeSlide();
38776         this.el.setStyle("z-index", 20000);
38777         var anchor = this.getSlideAnchor();
38778         this.el.slideOut(anchor, {
38779             callback : function(){
38780                 this.el.setStyle("z-index", "");
38781                 this.collapsedEl.slideIn(anchor, {duration:.3});
38782                 this.afterSlide();
38783                 this.el.setLocation(-10000,-10000);
38784                 this.el.hide();
38785                 this.fireEvent("collapsed", this);
38786             },
38787             scope: this,
38788             block: true
38789         });
38790     },
38791
38792     animateExpand : function(){
38793         this.beforeSlide();
38794         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
38795         this.el.setStyle("z-index", 20000);
38796         this.collapsedEl.hide({
38797             duration:.1
38798         });
38799         this.el.slideIn(this.getSlideAnchor(), {
38800             callback : function(){
38801                 this.el.setStyle("z-index", "");
38802                 this.afterSlide();
38803                 if(this.split){
38804                     this.split.el.show();
38805                 }
38806                 this.fireEvent("invalidated", this);
38807                 this.fireEvent("expanded", this);
38808             },
38809             scope: this,
38810             block: true
38811         });
38812     },
38813
38814     anchors : {
38815         "west" : "left",
38816         "east" : "right",
38817         "north" : "top",
38818         "south" : "bottom"
38819     },
38820
38821     sanchors : {
38822         "west" : "l",
38823         "east" : "r",
38824         "north" : "t",
38825         "south" : "b"
38826     },
38827
38828     canchors : {
38829         "west" : "tl-tr",
38830         "east" : "tr-tl",
38831         "north" : "tl-bl",
38832         "south" : "bl-tl"
38833     },
38834
38835     getAnchor : function(){
38836         return this.anchors[this.position];
38837     },
38838
38839     getCollapseAnchor : function(){
38840         return this.canchors[this.position];
38841     },
38842
38843     getSlideAnchor : function(){
38844         return this.sanchors[this.position];
38845     },
38846
38847     getAlignAdj : function(){
38848         var cm = this.cmargins;
38849         switch(this.position){
38850             case "west":
38851                 return [0, 0];
38852             break;
38853             case "east":
38854                 return [0, 0];
38855             break;
38856             case "north":
38857                 return [0, 0];
38858             break;
38859             case "south":
38860                 return [0, 0];
38861             break;
38862         }
38863     },
38864
38865     getExpandAdj : function(){
38866         var c = this.collapsedEl, cm = this.cmargins;
38867         switch(this.position){
38868             case "west":
38869                 return [-(cm.right+c.getWidth()+cm.left), 0];
38870             break;
38871             case "east":
38872                 return [cm.right+c.getWidth()+cm.left, 0];
38873             break;
38874             case "north":
38875                 return [0, -(cm.top+cm.bottom+c.getHeight())];
38876             break;
38877             case "south":
38878                 return [0, cm.top+cm.bottom+c.getHeight()];
38879             break;
38880         }
38881     }
38882 });/*
38883  * Based on:
38884  * Ext JS Library 1.1.1
38885  * Copyright(c) 2006-2007, Ext JS, LLC.
38886  *
38887  * Originally Released Under LGPL - original licence link has changed is not relivant.
38888  *
38889  * Fork - LGPL
38890  * <script type="text/javascript">
38891  */
38892 /*
38893  * These classes are private internal classes
38894  */
38895 Roo.bootstrap.layout.Center = function(config){
38896     config.region = "center";
38897     Roo.bootstrap.layout.Region.call(this, config);
38898     this.visible = true;
38899     this.minWidth = config.minWidth || 20;
38900     this.minHeight = config.minHeight || 20;
38901 };
38902
38903 Roo.extend(Roo.bootstrap.layout.Center, Roo.bootstrap.layout.Region, {
38904     hide : function(){
38905         // center panel can't be hidden
38906     },
38907     
38908     show : function(){
38909         // center panel can't be hidden
38910     },
38911     
38912     getMinWidth: function(){
38913         return this.minWidth;
38914     },
38915     
38916     getMinHeight: function(){
38917         return this.minHeight;
38918     }
38919 });
38920
38921
38922
38923
38924  
38925
38926
38927
38928
38929
38930
38931 Roo.bootstrap.layout.North = function(config)
38932 {
38933     config.region = 'north';
38934     config.cursor = 'n-resize';
38935     
38936     Roo.bootstrap.layout.Split.call(this, config);
38937     
38938     
38939     if(this.split){
38940         this.split.placement = Roo.bootstrap.SplitBar.TOP;
38941         this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
38942         this.split.el.addClass("roo-layout-split-v");
38943     }
38944     var size = config.initialSize || config.height;
38945     if(typeof size != "undefined"){
38946         this.el.setHeight(size);
38947     }
38948 };
38949 Roo.extend(Roo.bootstrap.layout.North, Roo.bootstrap.layout.Split,
38950 {
38951     orientation: Roo.bootstrap.SplitBar.VERTICAL,
38952     
38953     
38954     
38955     getBox : function(){
38956         if(this.collapsed){
38957             return this.collapsedEl.getBox();
38958         }
38959         var box = this.el.getBox();
38960         if(this.split){
38961             box.height += this.split.el.getHeight();
38962         }
38963         return box;
38964     },
38965     
38966     updateBox : function(box){
38967         if(this.split && !this.collapsed){
38968             box.height -= this.split.el.getHeight();
38969             this.split.el.setLeft(box.x);
38970             this.split.el.setTop(box.y+box.height);
38971             this.split.el.setWidth(box.width);
38972         }
38973         if(this.collapsed){
38974             this.updateBody(box.width, null);
38975         }
38976         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
38977     }
38978 });
38979
38980
38981
38982
38983
38984 Roo.bootstrap.layout.South = function(config){
38985     config.region = 'south';
38986     config.cursor = 's-resize';
38987     Roo.bootstrap.layout.Split.call(this, config);
38988     if(this.split){
38989         this.split.placement = Roo.bootstrap.SplitBar.BOTTOM;
38990         this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
38991         this.split.el.addClass("roo-layout-split-v");
38992     }
38993     var size = config.initialSize || config.height;
38994     if(typeof size != "undefined"){
38995         this.el.setHeight(size);
38996     }
38997 };
38998
38999 Roo.extend(Roo.bootstrap.layout.South, Roo.bootstrap.layout.Split, {
39000     orientation: Roo.bootstrap.SplitBar.VERTICAL,
39001     getBox : function(){
39002         if(this.collapsed){
39003             return this.collapsedEl.getBox();
39004         }
39005         var box = this.el.getBox();
39006         if(this.split){
39007             var sh = this.split.el.getHeight();
39008             box.height += sh;
39009             box.y -= sh;
39010         }
39011         return box;
39012     },
39013     
39014     updateBox : function(box){
39015         if(this.split && !this.collapsed){
39016             var sh = this.split.el.getHeight();
39017             box.height -= sh;
39018             box.y += sh;
39019             this.split.el.setLeft(box.x);
39020             this.split.el.setTop(box.y-sh);
39021             this.split.el.setWidth(box.width);
39022         }
39023         if(this.collapsed){
39024             this.updateBody(box.width, null);
39025         }
39026         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
39027     }
39028 });
39029
39030 Roo.bootstrap.layout.East = function(config){
39031     config.region = "east";
39032     config.cursor = "e-resize";
39033     Roo.bootstrap.layout.Split.call(this, config);
39034     if(this.split){
39035         this.split.placement = Roo.bootstrap.SplitBar.RIGHT;
39036         this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
39037         this.split.el.addClass("roo-layout-split-h");
39038     }
39039     var size = config.initialSize || config.width;
39040     if(typeof size != "undefined"){
39041         this.el.setWidth(size);
39042     }
39043 };
39044 Roo.extend(Roo.bootstrap.layout.East, Roo.bootstrap.layout.Split, {
39045     orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
39046     getBox : function(){
39047         if(this.collapsed){
39048             return this.collapsedEl.getBox();
39049         }
39050         var box = this.el.getBox();
39051         if(this.split){
39052             var sw = this.split.el.getWidth();
39053             box.width += sw;
39054             box.x -= sw;
39055         }
39056         return box;
39057     },
39058
39059     updateBox : function(box){
39060         if(this.split && !this.collapsed){
39061             var sw = this.split.el.getWidth();
39062             box.width -= sw;
39063             this.split.el.setLeft(box.x);
39064             this.split.el.setTop(box.y);
39065             this.split.el.setHeight(box.height);
39066             box.x += sw;
39067         }
39068         if(this.collapsed){
39069             this.updateBody(null, box.height);
39070         }
39071         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
39072     }
39073 });
39074
39075 Roo.bootstrap.layout.West = function(config){
39076     config.region = "west";
39077     config.cursor = "w-resize";
39078     
39079     Roo.bootstrap.layout.Split.call(this, config);
39080     if(this.split){
39081         this.split.placement = Roo.bootstrap.SplitBar.LEFT;
39082         this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
39083         this.split.el.addClass("roo-layout-split-h");
39084     }
39085     
39086 };
39087 Roo.extend(Roo.bootstrap.layout.West, Roo.bootstrap.layout.Split, {
39088     orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
39089     
39090     onRender: function(ctr, pos)
39091     {
39092         Roo.bootstrap.layout.West.superclass.onRender.call(this, ctr,pos);
39093         var size = this.config.initialSize || this.config.width;
39094         if(typeof size != "undefined"){
39095             this.el.setWidth(size);
39096         }
39097     },
39098     
39099     getBox : function(){
39100         if(this.collapsed){
39101             return this.collapsedEl.getBox();
39102         }
39103         var box = this.el.getBox();
39104         if(this.split){
39105             box.width += this.split.el.getWidth();
39106         }
39107         return box;
39108     },
39109     
39110     updateBox : function(box){
39111         if(this.split && !this.collapsed){
39112             var sw = this.split.el.getWidth();
39113             box.width -= sw;
39114             this.split.el.setLeft(box.x+box.width);
39115             this.split.el.setTop(box.y);
39116             this.split.el.setHeight(box.height);
39117         }
39118         if(this.collapsed){
39119             this.updateBody(null, box.height);
39120         }
39121         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
39122     }
39123 });Roo.namespace("Roo.bootstrap.panel");/*
39124  * Based on:
39125  * Ext JS Library 1.1.1
39126  * Copyright(c) 2006-2007, Ext JS, LLC.
39127  *
39128  * Originally Released Under LGPL - original licence link has changed is not relivant.
39129  *
39130  * Fork - LGPL
39131  * <script type="text/javascript">
39132  */
39133 /**
39134  * @class Roo.ContentPanel
39135  * @extends Roo.util.Observable
39136  * A basic ContentPanel element.
39137  * @cfg {Boolean}   fitToFrame    True for this panel to adjust its size to fit when the region resizes  (defaults to false)
39138  * @cfg {Boolean}   fitContainer   When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container  (defaults to false)
39139  * @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
39140  * @cfg {Boolean}   closable      True if the panel can be closed/removed
39141  * @cfg {Boolean}   background    True if the panel should not be activated when it is added (defaults to false)
39142  * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
39143  * @cfg {Toolbar}   toolbar       A toolbar for this panel
39144  * @cfg {Boolean} autoScroll    True to scroll overflow in this panel (use with {@link #fitToFrame})
39145  * @cfg {String} title          The title for this panel
39146  * @cfg {Array} adjustments     Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
39147  * @cfg {String} url            Calls {@link #setUrl} with this value
39148  * @cfg {String} region         (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
39149  * @cfg {String/Object} params  When used with {@link #url}, calls {@link #setUrl} with this value
39150  * @cfg {Boolean} loadOnce      When used with {@link #url}, calls {@link #setUrl} with this value
39151  * @cfg {String}    content        Raw content to fill content panel with (uses setContent on construction.)
39152  * @cfg {Boolean} badges render the badges
39153
39154  * @constructor
39155  * Create a new ContentPanel.
39156  * @param {String/HTMLElement/Roo.Element} el The container element for this panel
39157  * @param {String/Object} config A string to set only the title or a config object
39158  * @param {String} content (optional) Set the HTML content for this panel
39159  * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
39160  */
39161 Roo.bootstrap.panel.Content = function( config){
39162     
39163     this.tpl = config.tpl || false;
39164     
39165     var el = config.el;
39166     var content = config.content;
39167
39168     if(config.autoCreate){ // xtype is available if this is called from factory
39169         el = Roo.id();
39170     }
39171     this.el = Roo.get(el);
39172     if(!this.el && config && config.autoCreate){
39173         if(typeof config.autoCreate == "object"){
39174             if(!config.autoCreate.id){
39175                 config.autoCreate.id = config.id||el;
39176             }
39177             this.el = Roo.DomHelper.append(document.body,
39178                         config.autoCreate, true);
39179         }else{
39180             var elcfg =  {   tag: "div",
39181                             cls: "roo-layout-inactive-content",
39182                             id: config.id||el
39183                             };
39184             if (config.html) {
39185                 elcfg.html = config.html;
39186                 
39187             }
39188                         
39189             this.el = Roo.DomHelper.append(document.body, elcfg , true);
39190         }
39191     } 
39192     this.closable = false;
39193     this.loaded = false;
39194     this.active = false;
39195    
39196       
39197     if (config.toolbar && !config.toolbar.el && config.toolbar.xtype) {
39198         
39199         this.toolbar = new config.toolbar.xns[config.toolbar.xtype](config.toolbar);
39200         
39201         this.wrapEl = this.el; //this.el.wrap();
39202         var ti = [];
39203         if (config.toolbar.items) {
39204             ti = config.toolbar.items ;
39205             delete config.toolbar.items ;
39206         }
39207         
39208         var nitems = [];
39209         this.toolbar.render(this.wrapEl, 'before');
39210         for(var i =0;i < ti.length;i++) {
39211           //  Roo.log(['add child', items[i]]);
39212             nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
39213         }
39214         this.toolbar.items = nitems;
39215         this.toolbar.el.insertBefore(this.wrapEl.dom.firstChild);
39216         delete config.toolbar;
39217         
39218     }
39219     /*
39220     // xtype created footer. - not sure if will work as we normally have to render first..
39221     if (this.footer && !this.footer.el && this.footer.xtype) {
39222         if (!this.wrapEl) {
39223             this.wrapEl = this.el.wrap();
39224         }
39225     
39226         this.footer.container = this.wrapEl.createChild();
39227          
39228         this.footer = Roo.factory(this.footer, Roo);
39229         
39230     }
39231     */
39232     
39233      if(typeof config == "string"){
39234         this.title = config;
39235     }else{
39236         Roo.apply(this, config);
39237     }
39238     
39239     if(this.resizeEl){
39240         this.resizeEl = Roo.get(this.resizeEl, true);
39241     }else{
39242         this.resizeEl = this.el;
39243     }
39244     // handle view.xtype
39245     
39246  
39247     
39248     
39249     this.addEvents({
39250         /**
39251          * @event activate
39252          * Fires when this panel is activated. 
39253          * @param {Roo.ContentPanel} this
39254          */
39255         "activate" : true,
39256         /**
39257          * @event deactivate
39258          * Fires when this panel is activated. 
39259          * @param {Roo.ContentPanel} this
39260          */
39261         "deactivate" : true,
39262
39263         /**
39264          * @event resize
39265          * Fires when this panel is resized if fitToFrame is true.
39266          * @param {Roo.ContentPanel} this
39267          * @param {Number} width The width after any component adjustments
39268          * @param {Number} height The height after any component adjustments
39269          */
39270         "resize" : true,
39271         
39272          /**
39273          * @event render
39274          * Fires when this tab is created
39275          * @param {Roo.ContentPanel} this
39276          */
39277         "render" : true
39278         
39279         
39280         
39281     });
39282     
39283
39284     
39285     
39286     if(this.autoScroll){
39287         this.resizeEl.setStyle("overflow", "auto");
39288     } else {
39289         // fix randome scrolling
39290         //this.el.on('scroll', function() {
39291         //    Roo.log('fix random scolling');
39292         //    this.scrollTo('top',0); 
39293         //});
39294     }
39295     content = content || this.content;
39296     if(content){
39297         this.setContent(content);
39298     }
39299     if(config && config.url){
39300         this.setUrl(this.url, this.params, this.loadOnce);
39301     }
39302     
39303     
39304     
39305     Roo.bootstrap.panel.Content.superclass.constructor.call(this);
39306     
39307     if (this.view && typeof(this.view.xtype) != 'undefined') {
39308         this.view.el = this.el.appendChild(document.createElement("div"));
39309         this.view = Roo.factory(this.view); 
39310         this.view.render  &&  this.view.render(false, '');  
39311     }
39312     
39313     
39314     this.fireEvent('render', this);
39315 };
39316
39317 Roo.extend(Roo.bootstrap.panel.Content, Roo.bootstrap.Component, {
39318     
39319     tabTip : '',
39320     
39321     setRegion : function(region){
39322         this.region = region;
39323         this.setActiveClass(region && !this.background);
39324     },
39325     
39326     
39327     setActiveClass: function(state)
39328     {
39329         if(state){
39330            this.el.replaceClass("roo-layout-inactive-content", "roo-layout-active-content");
39331            this.el.setStyle('position','relative');
39332         }else{
39333            this.el.replaceClass("roo-layout-active-content", "roo-layout-inactive-content");
39334            this.el.setStyle('position', 'absolute');
39335         } 
39336     },
39337     
39338     /**
39339      * Returns the toolbar for this Panel if one was configured. 
39340      * @return {Roo.Toolbar} 
39341      */
39342     getToolbar : function(){
39343         return this.toolbar;
39344     },
39345     
39346     setActiveState : function(active)
39347     {
39348         this.active = active;
39349         this.setActiveClass(active);
39350         if(!active){
39351             if(this.fireEvent("deactivate", this) === false){
39352                 return false;
39353             }
39354             return true;
39355         }
39356         this.fireEvent("activate", this);
39357         return true;
39358     },
39359     /**
39360      * Updates this panel's element
39361      * @param {String} content The new content
39362      * @param {Boolean} loadScripts (optional) true to look for and process scripts
39363     */
39364     setContent : function(content, loadScripts){
39365         this.el.update(content, loadScripts);
39366     },
39367
39368     ignoreResize : function(w, h){
39369         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
39370             return true;
39371         }else{
39372             this.lastSize = {width: w, height: h};
39373             return false;
39374         }
39375     },
39376     /**
39377      * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
39378      * @return {Roo.UpdateManager} The UpdateManager
39379      */
39380     getUpdateManager : function(){
39381         return this.el.getUpdateManager();
39382     },
39383      /**
39384      * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
39385      * @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:
39386 <pre><code>
39387 panel.load({
39388     url: "your-url.php",
39389     params: {param1: "foo", param2: "bar"}, // or a URL encoded string
39390     callback: yourFunction,
39391     scope: yourObject, //(optional scope)
39392     discardUrl: false,
39393     nocache: false,
39394     text: "Loading...",
39395     timeout: 30,
39396     scripts: false
39397 });
39398 </code></pre>
39399      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
39400      * 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.
39401      * @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}
39402      * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
39403      * @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.
39404      * @return {Roo.ContentPanel} this
39405      */
39406     load : function(){
39407         var um = this.el.getUpdateManager();
39408         um.update.apply(um, arguments);
39409         return this;
39410     },
39411
39412
39413     /**
39414      * 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.
39415      * @param {String/Function} url The URL to load the content from or a function to call to get the URL
39416      * @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)
39417      * @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)
39418      * @return {Roo.UpdateManager} The UpdateManager
39419      */
39420     setUrl : function(url, params, loadOnce){
39421         if(this.refreshDelegate){
39422             this.removeListener("activate", this.refreshDelegate);
39423         }
39424         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
39425         this.on("activate", this.refreshDelegate);
39426         return this.el.getUpdateManager();
39427     },
39428     
39429     _handleRefresh : function(url, params, loadOnce){
39430         if(!loadOnce || !this.loaded){
39431             var updater = this.el.getUpdateManager();
39432             updater.update(url, params, this._setLoaded.createDelegate(this));
39433         }
39434     },
39435     
39436     _setLoaded : function(){
39437         this.loaded = true;
39438     }, 
39439     
39440     /**
39441      * Returns this panel's id
39442      * @return {String} 
39443      */
39444     getId : function(){
39445         return this.el.id;
39446     },
39447     
39448     /** 
39449      * Returns this panel's element - used by regiosn to add.
39450      * @return {Roo.Element} 
39451      */
39452     getEl : function(){
39453         return this.wrapEl || this.el;
39454     },
39455     
39456    
39457     
39458     adjustForComponents : function(width, height)
39459     {
39460         //Roo.log('adjustForComponents ');
39461         if(this.resizeEl != this.el){
39462             width -= this.el.getFrameWidth('lr');
39463             height -= this.el.getFrameWidth('tb');
39464         }
39465         if(this.toolbar){
39466             var te = this.toolbar.getEl();
39467             te.setWidth(width);
39468             height -= te.getHeight();
39469         }
39470         if(this.footer){
39471             var te = this.footer.getEl();
39472             te.setWidth(width);
39473             height -= te.getHeight();
39474         }
39475         
39476         
39477         if(this.adjustments){
39478             width += this.adjustments[0];
39479             height += this.adjustments[1];
39480         }
39481         return {"width": width, "height": height};
39482     },
39483     
39484     setSize : function(width, height){
39485         if(this.fitToFrame && !this.ignoreResize(width, height)){
39486             if(this.fitContainer && this.resizeEl != this.el){
39487                 this.el.setSize(width, height);
39488             }
39489             var size = this.adjustForComponents(width, height);
39490             this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
39491             this.fireEvent('resize', this, size.width, size.height);
39492         }
39493     },
39494     
39495     /**
39496      * Returns this panel's title
39497      * @return {String} 
39498      */
39499     getTitle : function(){
39500         
39501         if (typeof(this.title) != 'object') {
39502             return this.title;
39503         }
39504         
39505         var t = '';
39506         for (var k in this.title) {
39507             if (!this.title.hasOwnProperty(k)) {
39508                 continue;
39509             }
39510             
39511             if (k.indexOf('-') >= 0) {
39512                 var s = k.split('-');
39513                 for (var i = 0; i<s.length; i++) {
39514                     t += "<span class='visible-"+s[i]+"'>"+this.title[k]+"</span>";
39515                 }
39516             } else {
39517                 t += "<span class='visible-"+k+"'>"+this.title[k]+"</span>";
39518             }
39519         }
39520         return t;
39521     },
39522     
39523     /**
39524      * Set this panel's title
39525      * @param {String} title
39526      */
39527     setTitle : function(title){
39528         this.title = title;
39529         if(this.region){
39530             this.region.updatePanelTitle(this, title);
39531         }
39532     },
39533     
39534     /**
39535      * Returns true is this panel was configured to be closable
39536      * @return {Boolean} 
39537      */
39538     isClosable : function(){
39539         return this.closable;
39540     },
39541     
39542     beforeSlide : function(){
39543         this.el.clip();
39544         this.resizeEl.clip();
39545     },
39546     
39547     afterSlide : function(){
39548         this.el.unclip();
39549         this.resizeEl.unclip();
39550     },
39551     
39552     /**
39553      *   Force a content refresh from the URL specified in the {@link #setUrl} method.
39554      *   Will fail silently if the {@link #setUrl} method has not been called.
39555      *   This does not activate the panel, just updates its content.
39556      */
39557     refresh : function(){
39558         if(this.refreshDelegate){
39559            this.loaded = false;
39560            this.refreshDelegate();
39561         }
39562     },
39563     
39564     /**
39565      * Destroys this panel
39566      */
39567     destroy : function(){
39568         this.el.removeAllListeners();
39569         var tempEl = document.createElement("span");
39570         tempEl.appendChild(this.el.dom);
39571         tempEl.innerHTML = "";
39572         this.el.remove();
39573         this.el = null;
39574     },
39575     
39576     /**
39577      * form - if the content panel contains a form - this is a reference to it.
39578      * @type {Roo.form.Form}
39579      */
39580     form : false,
39581     /**
39582      * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
39583      *    This contains a reference to it.
39584      * @type {Roo.View}
39585      */
39586     view : false,
39587     
39588       /**
39589      * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
39590      * <pre><code>
39591
39592 layout.addxtype({
39593        xtype : 'Form',
39594        items: [ .... ]
39595    }
39596 );
39597
39598 </code></pre>
39599      * @param {Object} cfg Xtype definition of item to add.
39600      */
39601     
39602     
39603     getChildContainer: function () {
39604         return this.getEl();
39605     }
39606     
39607     
39608     /*
39609         var  ret = new Roo.factory(cfg);
39610         return ret;
39611         
39612         
39613         // add form..
39614         if (cfg.xtype.match(/^Form$/)) {
39615             
39616             var el;
39617             //if (this.footer) {
39618             //    el = this.footer.container.insertSibling(false, 'before');
39619             //} else {
39620                 el = this.el.createChild();
39621             //}
39622
39623             this.form = new  Roo.form.Form(cfg);
39624             
39625             
39626             if ( this.form.allItems.length) {
39627                 this.form.render(el.dom);
39628             }
39629             return this.form;
39630         }
39631         // should only have one of theses..
39632         if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
39633             // views.. should not be just added - used named prop 'view''
39634             
39635             cfg.el = this.el.appendChild(document.createElement("div"));
39636             // factory?
39637             
39638             var ret = new Roo.factory(cfg);
39639              
39640              ret.render && ret.render(false, ''); // render blank..
39641             this.view = ret;
39642             return ret;
39643         }
39644         return false;
39645     }
39646     \*/
39647 });
39648  
39649 /**
39650  * @class Roo.bootstrap.panel.Grid
39651  * @extends Roo.bootstrap.panel.Content
39652  * @constructor
39653  * Create a new GridPanel.
39654  * @cfg {Roo.bootstrap.Table} grid The grid for this panel
39655  * @param {Object} config A the config object
39656   
39657  */
39658
39659
39660
39661 Roo.bootstrap.panel.Grid = function(config)
39662 {
39663     
39664       
39665     this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
39666         {tag: "div", cls: "roo-layout-grid-wrapper roo-layout-inactive-content"}, true);
39667
39668     config.el = this.wrapper;
39669     //this.el = this.wrapper;
39670     
39671       if (config.container) {
39672         // ctor'ed from a Border/panel.grid
39673         
39674         
39675         this.wrapper.setStyle("overflow", "hidden");
39676         this.wrapper.addClass('roo-grid-container');
39677
39678     }
39679     
39680     
39681     if(config.toolbar){
39682         var tool_el = this.wrapper.createChild();    
39683         this.toolbar = Roo.factory(config.toolbar);
39684         var ti = [];
39685         if (config.toolbar.items) {
39686             ti = config.toolbar.items ;
39687             delete config.toolbar.items ;
39688         }
39689         
39690         var nitems = [];
39691         this.toolbar.render(tool_el);
39692         for(var i =0;i < ti.length;i++) {
39693           //  Roo.log(['add child', items[i]]);
39694             nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
39695         }
39696         this.toolbar.items = nitems;
39697         
39698         delete config.toolbar;
39699     }
39700     
39701     Roo.bootstrap.panel.Grid.superclass.constructor.call(this, config);
39702     config.grid.scrollBody = true;;
39703     config.grid.monitorWindowResize = false; // turn off autosizing
39704     config.grid.autoHeight = false;
39705     config.grid.autoWidth = false;
39706     
39707     this.grid = new config.grid.xns[config.grid.xtype](config.grid);
39708     
39709     if (config.background) {
39710         // render grid on panel activation (if panel background)
39711         this.on('activate', function(gp) {
39712             if (!gp.grid.rendered) {
39713                 gp.grid.render(this.wrapper);
39714                 gp.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");   
39715             }
39716         });
39717             
39718     } else {
39719         this.grid.render(this.wrapper);
39720         this.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");               
39721
39722     }
39723     //this.wrapper.dom.appendChild(config.grid.getGridEl().dom);
39724     // ??? needed ??? config.el = this.wrapper;
39725     
39726     
39727     
39728   
39729     // xtype created footer. - not sure if will work as we normally have to render first..
39730     if (this.footer && !this.footer.el && this.footer.xtype) {
39731         
39732         var ctr = this.grid.getView().getFooterPanel(true);
39733         this.footer.dataSource = this.grid.dataSource;
39734         this.footer = Roo.factory(this.footer, Roo);
39735         this.footer.render(ctr);
39736         
39737     }
39738     
39739     
39740     
39741     
39742      
39743 };
39744
39745 Roo.extend(Roo.bootstrap.panel.Grid, Roo.bootstrap.panel.Content, {
39746     getId : function(){
39747         return this.grid.id;
39748     },
39749     
39750     /**
39751      * Returns the grid for this panel
39752      * @return {Roo.bootstrap.Table} 
39753      */
39754     getGrid : function(){
39755         return this.grid;    
39756     },
39757     
39758     setSize : function(width, height){
39759         if(!this.ignoreResize(width, height)){
39760             var grid = this.grid;
39761             var size = this.adjustForComponents(width, height);
39762             // tfoot is not a footer?
39763           
39764             
39765             var gridel = grid.getGridEl();
39766             gridel.setSize(size.width, size.height);
39767             
39768             var tbd = grid.getGridEl().select('tbody', true).first();
39769             var thd = grid.getGridEl().select('thead',true).first();
39770             var tbf= grid.getGridEl().select('tfoot', true).first();
39771
39772             if (tbf) {
39773                 size.height -= thd.getHeight();
39774             }
39775             if (thd) {
39776                 size.height -= thd.getHeight();
39777             }
39778             
39779             tbd.setSize(size.width, size.height );
39780             // this is for the account management tab -seems to work there.
39781             var thd = grid.getGridEl().select('thead',true).first();
39782             //if (tbd) {
39783             //    tbd.setSize(size.width, size.height - thd.getHeight());
39784             //}
39785              
39786             grid.autoSize();
39787         }
39788     },
39789      
39790     
39791     
39792     beforeSlide : function(){
39793         this.grid.getView().scroller.clip();
39794     },
39795     
39796     afterSlide : function(){
39797         this.grid.getView().scroller.unclip();
39798     },
39799     
39800     destroy : function(){
39801         this.grid.destroy();
39802         delete this.grid;
39803         Roo.bootstrap.panel.Grid.superclass.destroy.call(this); 
39804     }
39805 });
39806
39807 /**
39808  * @class Roo.bootstrap.panel.Nest
39809  * @extends Roo.bootstrap.panel.Content
39810  * @constructor
39811  * Create a new Panel, that can contain a layout.Border.
39812  * 
39813  * 
39814  * @param {Roo.BorderLayout} layout The layout for this panel
39815  * @param {String/Object} config A string to set only the title or a config object
39816  */
39817 Roo.bootstrap.panel.Nest = function(config)
39818 {
39819     // construct with only one argument..
39820     /* FIXME - implement nicer consturctors
39821     if (layout.layout) {
39822         config = layout;
39823         layout = config.layout;
39824         delete config.layout;
39825     }
39826     if (layout.xtype && !layout.getEl) {
39827         // then layout needs constructing..
39828         layout = Roo.factory(layout, Roo);
39829     }
39830     */
39831     
39832     config.el =  config.layout.getEl();
39833     
39834     Roo.bootstrap.panel.Nest.superclass.constructor.call(this, config);
39835     
39836     config.layout.monitorWindowResize = false; // turn off autosizing
39837     this.layout = config.layout;
39838     this.layout.getEl().addClass("roo-layout-nested-layout");
39839     this.layout.parent = this;
39840     
39841     
39842     
39843     
39844 };
39845
39846 Roo.extend(Roo.bootstrap.panel.Nest, Roo.bootstrap.panel.Content, {
39847
39848     setSize : function(width, height){
39849         if(!this.ignoreResize(width, height)){
39850             var size = this.adjustForComponents(width, height);
39851             var el = this.layout.getEl();
39852             if (size.height < 1) {
39853                 el.setWidth(size.width);   
39854             } else {
39855                 el.setSize(size.width, size.height);
39856             }
39857             var touch = el.dom.offsetWidth;
39858             this.layout.layout();
39859             // ie requires a double layout on the first pass
39860             if(Roo.isIE && !this.initialized){
39861                 this.initialized = true;
39862                 this.layout.layout();
39863             }
39864         }
39865     },
39866     
39867     // activate all subpanels if not currently active..
39868     
39869     setActiveState : function(active){
39870         this.active = active;
39871         this.setActiveClass(active);
39872         
39873         if(!active){
39874             this.fireEvent("deactivate", this);
39875             return;
39876         }
39877         
39878         this.fireEvent("activate", this);
39879         // not sure if this should happen before or after..
39880         if (!this.layout) {
39881             return; // should not happen..
39882         }
39883         var reg = false;
39884         for (var r in this.layout.regions) {
39885             reg = this.layout.getRegion(r);
39886             if (reg.getActivePanel()) {
39887                 //reg.showPanel(reg.getActivePanel()); // force it to activate.. 
39888                 reg.setActivePanel(reg.getActivePanel());
39889                 continue;
39890             }
39891             if (!reg.panels.length) {
39892                 continue;
39893             }
39894             reg.showPanel(reg.getPanel(0));
39895         }
39896         
39897         
39898         
39899         
39900     },
39901     
39902     /**
39903      * Returns the nested BorderLayout for this panel
39904      * @return {Roo.BorderLayout} 
39905      */
39906     getLayout : function(){
39907         return this.layout;
39908     },
39909     
39910      /**
39911      * Adds a xtype elements to the layout of the nested panel
39912      * <pre><code>
39913
39914 panel.addxtype({
39915        xtype : 'ContentPanel',
39916        region: 'west',
39917        items: [ .... ]
39918    }
39919 );
39920
39921 panel.addxtype({
39922         xtype : 'NestedLayoutPanel',
39923         region: 'west',
39924         layout: {
39925            center: { },
39926            west: { }   
39927         },
39928         items : [ ... list of content panels or nested layout panels.. ]
39929    }
39930 );
39931 </code></pre>
39932      * @param {Object} cfg Xtype definition of item to add.
39933      */
39934     addxtype : function(cfg) {
39935         return this.layout.addxtype(cfg);
39936     
39937     }
39938 });/*
39939  * Based on:
39940  * Ext JS Library 1.1.1
39941  * Copyright(c) 2006-2007, Ext JS, LLC.
39942  *
39943  * Originally Released Under LGPL - original licence link has changed is not relivant.
39944  *
39945  * Fork - LGPL
39946  * <script type="text/javascript">
39947  */
39948 /**
39949  * @class Roo.TabPanel
39950  * @extends Roo.util.Observable
39951  * A lightweight tab container.
39952  * <br><br>
39953  * Usage:
39954  * <pre><code>
39955 // basic tabs 1, built from existing content
39956 var tabs = new Roo.TabPanel("tabs1");
39957 tabs.addTab("script", "View Script");
39958 tabs.addTab("markup", "View Markup");
39959 tabs.activate("script");
39960
39961 // more advanced tabs, built from javascript
39962 var jtabs = new Roo.TabPanel("jtabs");
39963 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
39964
39965 // set up the UpdateManager
39966 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
39967 var updater = tab2.getUpdateManager();
39968 updater.setDefaultUrl("ajax1.htm");
39969 tab2.on('activate', updater.refresh, updater, true);
39970
39971 // Use setUrl for Ajax loading
39972 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
39973 tab3.setUrl("ajax2.htm", null, true);
39974
39975 // Disabled tab
39976 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
39977 tab4.disable();
39978
39979 jtabs.activate("jtabs-1");
39980  * </code></pre>
39981  * @constructor
39982  * Create a new TabPanel.
39983  * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
39984  * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
39985  */
39986 Roo.bootstrap.panel.Tabs = function(config){
39987     /**
39988     * The container element for this TabPanel.
39989     * @type Roo.Element
39990     */
39991     this.el = Roo.get(config.el);
39992     delete config.el;
39993     if(config){
39994         if(typeof config == "boolean"){
39995             this.tabPosition = config ? "bottom" : "top";
39996         }else{
39997             Roo.apply(this, config);
39998         }
39999     }
40000     
40001     if(this.tabPosition == "bottom"){
40002         // if tabs are at the bottom = create the body first.
40003         this.bodyEl = Roo.get(this.createBody(this.el.dom));
40004         this.el.addClass("roo-tabs-bottom");
40005     }
40006     // next create the tabs holders
40007     
40008     if (this.tabPosition == "west"){
40009         
40010         var reg = this.region; // fake it..
40011         while (reg) {
40012             if (!reg.mgr.parent) {
40013                 break;
40014             }
40015             reg = reg.mgr.parent.region;
40016         }
40017         Roo.log("got nest?");
40018         Roo.log(reg);
40019         if (reg.mgr.getRegion('west')) {
40020             var ctrdom = reg.mgr.getRegion('west').bodyEl.dom;
40021             this.stripWrap = Roo.get(this.createStrip(ctrdom ), true);
40022             this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
40023             this.stripEl.setVisibilityMode(Roo.Element.DISPLAY);
40024             this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
40025         
40026             
40027         }
40028         
40029         
40030     } else {
40031      
40032         this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
40033         this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
40034         this.stripEl.setVisibilityMode(Roo.Element.DISPLAY);
40035         this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
40036     }
40037     
40038     
40039     if(Roo.isIE){
40040         Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
40041     }
40042     
40043     // finally - if tabs are at the top, then create the body last..
40044     if(this.tabPosition != "bottom"){
40045         /** The body element that contains {@link Roo.TabPanelItem} bodies. +
40046          * @type Roo.Element
40047          */
40048         this.bodyEl = Roo.get(this.createBody(this.el.dom));
40049         this.el.addClass("roo-tabs-top");
40050     }
40051     this.items = [];
40052
40053     this.bodyEl.setStyle("position", "relative");
40054
40055     this.active = null;
40056     this.activateDelegate = this.activate.createDelegate(this);
40057
40058     this.addEvents({
40059         /**
40060          * @event tabchange
40061          * Fires when the active tab changes
40062          * @param {Roo.TabPanel} this
40063          * @param {Roo.TabPanelItem} activePanel The new active tab
40064          */
40065         "tabchange": true,
40066         /**
40067          * @event beforetabchange
40068          * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
40069          * @param {Roo.TabPanel} this
40070          * @param {Object} e Set cancel to true on this object to cancel the tab change
40071          * @param {Roo.TabPanelItem} tab The tab being changed to
40072          */
40073         "beforetabchange" : true
40074     });
40075
40076     Roo.EventManager.onWindowResize(this.onResize, this);
40077     this.cpad = this.el.getPadding("lr");
40078     this.hiddenCount = 0;
40079
40080
40081     // toolbar on the tabbar support...
40082     if (this.toolbar) {
40083         alert("no toolbar support yet");
40084         this.toolbar  = false;
40085         /*
40086         var tcfg = this.toolbar;
40087         tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');  
40088         this.toolbar = new Roo.Toolbar(tcfg);
40089         if (Roo.isSafari) {
40090             var tbl = tcfg.container.child('table', true);
40091             tbl.setAttribute('width', '100%');
40092         }
40093         */
40094         
40095     }
40096    
40097
40098
40099     Roo.bootstrap.panel.Tabs.superclass.constructor.call(this);
40100 };
40101
40102 Roo.extend(Roo.bootstrap.panel.Tabs, Roo.util.Observable, {
40103     /*
40104      *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
40105      */
40106     tabPosition : "top",
40107     /*
40108      *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
40109      */
40110     currentTabWidth : 0,
40111     /*
40112      *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
40113      */
40114     minTabWidth : 40,
40115     /*
40116      *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
40117      */
40118     maxTabWidth : 250,
40119     /*
40120      *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
40121      */
40122     preferredTabWidth : 175,
40123     /*
40124      *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
40125      */
40126     resizeTabs : false,
40127     /*
40128      *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
40129      */
40130     monitorResize : true,
40131     /*
40132      *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar. 
40133      */
40134     toolbar : false,  // set by caller..
40135     
40136     region : false, /// set by caller
40137     
40138     disableTooltips : true, // not used yet...
40139
40140     /**
40141      * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
40142      * @param {String} id The id of the div to use <b>or create</b>
40143      * @param {String} text The text for the tab
40144      * @param {String} content (optional) Content to put in the TabPanelItem body
40145      * @param {Boolean} closable (optional) True to create a close icon on the tab
40146      * @return {Roo.TabPanelItem} The created TabPanelItem
40147      */
40148     addTab : function(id, text, content, closable, tpl)
40149     {
40150         var item = new Roo.bootstrap.panel.TabItem({
40151             panel: this,
40152             id : id,
40153             text : text,
40154             closable : closable,
40155             tpl : tpl
40156         });
40157         this.addTabItem(item);
40158         if(content){
40159             item.setContent(content);
40160         }
40161         return item;
40162     },
40163
40164     /**
40165      * Returns the {@link Roo.TabPanelItem} with the specified id/index
40166      * @param {String/Number} id The id or index of the TabPanelItem to fetch.
40167      * @return {Roo.TabPanelItem}
40168      */
40169     getTab : function(id){
40170         return this.items[id];
40171     },
40172
40173     /**
40174      * Hides the {@link Roo.TabPanelItem} with the specified id/index
40175      * @param {String/Number} id The id or index of the TabPanelItem to hide.
40176      */
40177     hideTab : function(id){
40178         var t = this.items[id];
40179         if(!t.isHidden()){
40180            t.setHidden(true);
40181            this.hiddenCount++;
40182            this.autoSizeTabs();
40183         }
40184     },
40185
40186     /**
40187      * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
40188      * @param {String/Number} id The id or index of the TabPanelItem to unhide.
40189      */
40190     unhideTab : function(id){
40191         var t = this.items[id];
40192         if(t.isHidden()){
40193            t.setHidden(false);
40194            this.hiddenCount--;
40195            this.autoSizeTabs();
40196         }
40197     },
40198
40199     /**
40200      * Adds an existing {@link Roo.TabPanelItem}.
40201      * @param {Roo.TabPanelItem} item The TabPanelItem to add
40202      */
40203     addTabItem : function(item)
40204     {
40205         this.items[item.id] = item;
40206         this.items.push(item);
40207         this.autoSizeTabs();
40208       //  if(this.resizeTabs){
40209     //       item.setWidth(this.currentTabWidth || this.preferredTabWidth);
40210   //         this.autoSizeTabs();
40211 //        }else{
40212 //            item.autoSize();
40213        // }
40214     },
40215
40216     /**
40217      * Removes a {@link Roo.TabPanelItem}.
40218      * @param {String/Number} id The id or index of the TabPanelItem to remove.
40219      */
40220     removeTab : function(id){
40221         var items = this.items;
40222         var tab = items[id];
40223         if(!tab) { return; }
40224         var index = items.indexOf(tab);
40225         if(this.active == tab && items.length > 1){
40226             var newTab = this.getNextAvailable(index);
40227             if(newTab) {
40228                 newTab.activate();
40229             }
40230         }
40231         this.stripEl.dom.removeChild(tab.pnode.dom);
40232         if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
40233             this.bodyEl.dom.removeChild(tab.bodyEl.dom);
40234         }
40235         items.splice(index, 1);
40236         delete this.items[tab.id];
40237         tab.fireEvent("close", tab);
40238         tab.purgeListeners();
40239         this.autoSizeTabs();
40240     },
40241
40242     getNextAvailable : function(start){
40243         var items = this.items;
40244         var index = start;
40245         // look for a next tab that will slide over to
40246         // replace the one being removed
40247         while(index < items.length){
40248             var item = items[++index];
40249             if(item && !item.isHidden()){
40250                 return item;
40251             }
40252         }
40253         // if one isn't found select the previous tab (on the left)
40254         index = start;
40255         while(index >= 0){
40256             var item = items[--index];
40257             if(item && !item.isHidden()){
40258                 return item;
40259             }
40260         }
40261         return null;
40262     },
40263
40264     /**
40265      * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
40266      * @param {String/Number} id The id or index of the TabPanelItem to disable.
40267      */
40268     disableTab : function(id){
40269         var tab = this.items[id];
40270         if(tab && this.active != tab){
40271             tab.disable();
40272         }
40273     },
40274
40275     /**
40276      * Enables a {@link Roo.TabPanelItem} that is disabled.
40277      * @param {String/Number} id The id or index of the TabPanelItem to enable.
40278      */
40279     enableTab : function(id){
40280         var tab = this.items[id];
40281         tab.enable();
40282     },
40283
40284     /**
40285      * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
40286      * @param {String/Number} id The id or index of the TabPanelItem to activate.
40287      * @return {Roo.TabPanelItem} The TabPanelItem.
40288      */
40289     activate : function(id)
40290     {
40291         //Roo.log('activite:'  + id);
40292         
40293         var tab = this.items[id];
40294         if(!tab){
40295             return null;
40296         }
40297         if(tab == this.active || tab.disabled){
40298             return tab;
40299         }
40300         var e = {};
40301         this.fireEvent("beforetabchange", this, e, tab);
40302         if(e.cancel !== true && !tab.disabled){
40303             if(this.active){
40304                 this.active.hide();
40305             }
40306             this.active = this.items[id];
40307             this.active.show();
40308             this.fireEvent("tabchange", this, this.active);
40309         }
40310         return tab;
40311     },
40312
40313     /**
40314      * Gets the active {@link Roo.TabPanelItem}.
40315      * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
40316      */
40317     getActiveTab : function(){
40318         return this.active;
40319     },
40320
40321     /**
40322      * Updates the tab body element to fit the height of the container element
40323      * for overflow scrolling
40324      * @param {Number} targetHeight (optional) Override the starting height from the elements height
40325      */
40326     syncHeight : function(targetHeight){
40327         var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
40328         var bm = this.bodyEl.getMargins();
40329         var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
40330         this.bodyEl.setHeight(newHeight);
40331         return newHeight;
40332     },
40333
40334     onResize : function(){
40335         if(this.monitorResize){
40336             this.autoSizeTabs();
40337         }
40338     },
40339
40340     /**
40341      * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
40342      */
40343     beginUpdate : function(){
40344         this.updating = true;
40345     },
40346
40347     /**
40348      * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
40349      */
40350     endUpdate : function(){
40351         this.updating = false;
40352         this.autoSizeTabs();
40353     },
40354
40355     /**
40356      * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
40357      */
40358     autoSizeTabs : function()
40359     {
40360         var count = this.items.length;
40361         var vcount = count - this.hiddenCount;
40362         
40363         if (vcount < 2) {
40364             this.stripEl.hide();
40365         } else {
40366             this.stripEl.show();
40367         }
40368         
40369         if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) {
40370             return;
40371         }
40372         
40373         
40374         var w = Math.max(this.el.getWidth() - this.cpad, 10);
40375         var availWidth = Math.floor(w / vcount);
40376         var b = this.stripBody;
40377         if(b.getWidth() > w){
40378             var tabs = this.items;
40379             this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
40380             if(availWidth < this.minTabWidth){
40381                 /*if(!this.sleft){    // incomplete scrolling code
40382                     this.createScrollButtons();
40383                 }
40384                 this.showScroll();
40385                 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
40386             }
40387         }else{
40388             if(this.currentTabWidth < this.preferredTabWidth){
40389                 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
40390             }
40391         }
40392     },
40393
40394     /**
40395      * Returns the number of tabs in this TabPanel.
40396      * @return {Number}
40397      */
40398      getCount : function(){
40399          return this.items.length;
40400      },
40401
40402     /**
40403      * Resizes all the tabs to the passed width
40404      * @param {Number} The new width
40405      */
40406     setTabWidth : function(width){
40407         this.currentTabWidth = width;
40408         for(var i = 0, len = this.items.length; i < len; i++) {
40409                 if(!this.items[i].isHidden()) {
40410                 this.items[i].setWidth(width);
40411             }
40412         }
40413     },
40414
40415     /**
40416      * Destroys this TabPanel
40417      * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
40418      */
40419     destroy : function(removeEl){
40420         Roo.EventManager.removeResizeListener(this.onResize, this);
40421         for(var i = 0, len = this.items.length; i < len; i++){
40422             this.items[i].purgeListeners();
40423         }
40424         if(removeEl === true){
40425             this.el.update("");
40426             this.el.remove();
40427         }
40428     },
40429     
40430     createStrip : function(container)
40431     {
40432         var strip = document.createElement("nav");
40433         strip.className = Roo.bootstrap.version == 4 ?
40434             "navbar-light bg-light" : 
40435             "navbar navbar-default"; //"x-tabs-wrap";
40436         container.appendChild(strip);
40437         return strip;
40438     },
40439     
40440     createStripList : function(strip)
40441     {
40442         // div wrapper for retard IE
40443         // returns the "tr" element.
40444         strip.innerHTML = '<ul class="nav nav-tabs" role="tablist"></ul>';
40445         //'<div class="x-tabs-strip-wrap">'+
40446           //  '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
40447           //  '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
40448         return strip.firstChild; //.firstChild.firstChild.firstChild;
40449     },
40450     createBody : function(container)
40451     {
40452         var body = document.createElement("div");
40453         Roo.id(body, "tab-body");
40454         //Roo.fly(body).addClass("x-tabs-body");
40455         Roo.fly(body).addClass("tab-content");
40456         container.appendChild(body);
40457         return body;
40458     },
40459     createItemBody :function(bodyEl, id){
40460         var body = Roo.getDom(id);
40461         if(!body){
40462             body = document.createElement("div");
40463             body.id = id;
40464         }
40465         //Roo.fly(body).addClass("x-tabs-item-body");
40466         Roo.fly(body).addClass("tab-pane");
40467          bodyEl.insertBefore(body, bodyEl.firstChild);
40468         return body;
40469     },
40470     /** @private */
40471     createStripElements :  function(stripEl, text, closable, tpl)
40472     {
40473         var td = document.createElement("li"); // was td..
40474         td.className = 'nav-item';
40475         
40476         //stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
40477         
40478         
40479         stripEl.appendChild(td);
40480         /*if(closable){
40481             td.className = "x-tabs-closable";
40482             if(!this.closeTpl){
40483                 this.closeTpl = new Roo.Template(
40484                    '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
40485                    '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
40486                    '<div unselectable="on" class="close-icon">&#160;</div></em></span></a>'
40487                 );
40488             }
40489             var el = this.closeTpl.overwrite(td, {"text": text});
40490             var close = el.getElementsByTagName("div")[0];
40491             var inner = el.getElementsByTagName("em")[0];
40492             return {"el": el, "close": close, "inner": inner};
40493         } else {
40494         */
40495         // not sure what this is..
40496 //            if(!this.tabTpl){
40497                 //this.tabTpl = new Roo.Template(
40498                 //   '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
40499                 //   '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
40500                 //);
40501 //                this.tabTpl = new Roo.Template(
40502 //                   '<a href="#">' +
40503 //                   '<span unselectable="on"' +
40504 //                            (this.disableTooltips ? '' : ' title="{text}"') +
40505 //                            ' >{text}</span></a>'
40506 //                );
40507 //                
40508 //            }
40509
40510
40511             var template = tpl || this.tabTpl || false;
40512             
40513             if(!template){
40514                 template =  new Roo.Template(
40515                         Roo.bootstrap.version == 4 ? 
40516                             (
40517                                 '<a class="nav-link" href="#" unselectable="on"' +
40518                                      (this.disableTooltips ? '' : ' title="{text}"') +
40519                                      ' >{text}</a>'
40520                             ) : (
40521                                 '<a class="nav-link" href="#">' +
40522                                 '<span unselectable="on"' +
40523                                          (this.disableTooltips ? '' : ' title="{text}"') +
40524                                     ' >{text}</span></a>'
40525                             )
40526                 );
40527             }
40528             
40529             switch (typeof(template)) {
40530                 case 'object' :
40531                     break;
40532                 case 'string' :
40533                     template = new Roo.Template(template);
40534                     break;
40535                 default :
40536                     break;
40537             }
40538             
40539             var el = template.overwrite(td, {"text": text});
40540             
40541             var inner = el.getElementsByTagName("span")[0];
40542             
40543             return {"el": el, "inner": inner};
40544             
40545     }
40546         
40547     
40548 });
40549
40550 /**
40551  * @class Roo.TabPanelItem
40552  * @extends Roo.util.Observable
40553  * Represents an individual item (tab plus body) in a TabPanel.
40554  * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
40555  * @param {String} id The id of this TabPanelItem
40556  * @param {String} text The text for the tab of this TabPanelItem
40557  * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
40558  */
40559 Roo.bootstrap.panel.TabItem = function(config){
40560     /**
40561      * The {@link Roo.TabPanel} this TabPanelItem belongs to
40562      * @type Roo.TabPanel
40563      */
40564     this.tabPanel = config.panel;
40565     /**
40566      * The id for this TabPanelItem
40567      * @type String
40568      */
40569     this.id = config.id;
40570     /** @private */
40571     this.disabled = false;
40572     /** @private */
40573     this.text = config.text;
40574     /** @private */
40575     this.loaded = false;
40576     this.closable = config.closable;
40577
40578     /**
40579      * The body element for this TabPanelItem.
40580      * @type Roo.Element
40581      */
40582     this.bodyEl = Roo.get(this.tabPanel.createItemBody(this.tabPanel.bodyEl.dom, config.id));
40583     this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
40584     this.bodyEl.setStyle("display", "block");
40585     this.bodyEl.setStyle("zoom", "1");
40586     //this.hideAction();
40587
40588     var els = this.tabPanel.createStripElements(this.tabPanel.stripEl.dom, config.text, config.closable, config.tpl);
40589     /** @private */
40590     this.el = Roo.get(els.el);
40591     this.inner = Roo.get(els.inner, true);
40592      this.textEl = Roo.bootstrap.version == 4 ?
40593         this.el : Roo.get(this.el.dom.firstChild, true);
40594
40595     this.pnode = this.linode = Roo.get(els.el.parentNode, true);
40596     this.status_node = Roo.bootstrap.version == 4 ? this.el : this.linode;
40597
40598     
40599 //    this.el.on("mousedown", this.onTabMouseDown, this);
40600     this.el.on("click", this.onTabClick, this);
40601     /** @private */
40602     if(config.closable){
40603         var c = Roo.get(els.close, true);
40604         c.dom.title = this.closeText;
40605         c.addClassOnOver("close-over");
40606         c.on("click", this.closeClick, this);
40607      }
40608
40609     this.addEvents({
40610          /**
40611          * @event activate
40612          * Fires when this tab becomes the active tab.
40613          * @param {Roo.TabPanel} tabPanel The parent TabPanel
40614          * @param {Roo.TabPanelItem} this
40615          */
40616         "activate": true,
40617         /**
40618          * @event beforeclose
40619          * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
40620          * @param {Roo.TabPanelItem} this
40621          * @param {Object} e Set cancel to true on this object to cancel the close.
40622          */
40623         "beforeclose": true,
40624         /**
40625          * @event close
40626          * Fires when this tab is closed.
40627          * @param {Roo.TabPanelItem} this
40628          */
40629          "close": true,
40630         /**
40631          * @event deactivate
40632          * Fires when this tab is no longer the active tab.
40633          * @param {Roo.TabPanel} tabPanel The parent TabPanel
40634          * @param {Roo.TabPanelItem} this
40635          */
40636          "deactivate" : true
40637     });
40638     this.hidden = false;
40639
40640     Roo.bootstrap.panel.TabItem.superclass.constructor.call(this);
40641 };
40642
40643 Roo.extend(Roo.bootstrap.panel.TabItem, Roo.util.Observable,
40644            {
40645     purgeListeners : function(){
40646        Roo.util.Observable.prototype.purgeListeners.call(this);
40647        this.el.removeAllListeners();
40648     },
40649     /**
40650      * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
40651      */
40652     show : function(){
40653         this.status_node.addClass("active");
40654         this.showAction();
40655         if(Roo.isOpera){
40656             this.tabPanel.stripWrap.repaint();
40657         }
40658         this.fireEvent("activate", this.tabPanel, this);
40659     },
40660
40661     /**
40662      * Returns true if this tab is the active tab.
40663      * @return {Boolean}
40664      */
40665     isActive : function(){
40666         return this.tabPanel.getActiveTab() == this;
40667     },
40668
40669     /**
40670      * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
40671      */
40672     hide : function(){
40673         this.status_node.removeClass("active");
40674         this.hideAction();
40675         this.fireEvent("deactivate", this.tabPanel, this);
40676     },
40677
40678     hideAction : function(){
40679         this.bodyEl.hide();
40680         this.bodyEl.setStyle("position", "absolute");
40681         this.bodyEl.setLeft("-20000px");
40682         this.bodyEl.setTop("-20000px");
40683     },
40684
40685     showAction : function(){
40686         this.bodyEl.setStyle("position", "relative");
40687         this.bodyEl.setTop("");
40688         this.bodyEl.setLeft("");
40689         this.bodyEl.show();
40690     },
40691
40692     /**
40693      * Set the tooltip for the tab.
40694      * @param {String} tooltip The tab's tooltip
40695      */
40696     setTooltip : function(text){
40697         if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
40698             this.textEl.dom.qtip = text;
40699             this.textEl.dom.removeAttribute('title');
40700         }else{
40701             this.textEl.dom.title = text;
40702         }
40703     },
40704
40705     onTabClick : function(e){
40706         e.preventDefault();
40707         this.tabPanel.activate(this.id);
40708     },
40709
40710     onTabMouseDown : function(e){
40711         e.preventDefault();
40712         this.tabPanel.activate(this.id);
40713     },
40714 /*
40715     getWidth : function(){
40716         return this.inner.getWidth();
40717     },
40718
40719     setWidth : function(width){
40720         var iwidth = width - this.linode.getPadding("lr");
40721         this.inner.setWidth(iwidth);
40722         this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
40723         this.linode.setWidth(width);
40724     },
40725 */
40726     /**
40727      * Show or hide the tab
40728      * @param {Boolean} hidden True to hide or false to show.
40729      */
40730     setHidden : function(hidden){
40731         this.hidden = hidden;
40732         this.linode.setStyle("display", hidden ? "none" : "");
40733     },
40734
40735     /**
40736      * Returns true if this tab is "hidden"
40737      * @return {Boolean}
40738      */
40739     isHidden : function(){
40740         return this.hidden;
40741     },
40742
40743     /**
40744      * Returns the text for this tab
40745      * @return {String}
40746      */
40747     getText : function(){
40748         return this.text;
40749     },
40750     /*
40751     autoSize : function(){
40752         //this.el.beginMeasure();
40753         this.textEl.setWidth(1);
40754         /*
40755          *  #2804 [new] Tabs in Roojs
40756          *  increase the width by 2-4 pixels to prevent the ellipssis showing in chrome
40757          */
40758         //this.setWidth(this.textEl.dom.scrollWidth+this.linode.getPadding("lr")+this.inner.getPadding("lr") + 2);
40759         //this.el.endMeasure();
40760     //},
40761
40762     /**
40763      * Sets the text for the tab (Note: this also sets the tooltip text)
40764      * @param {String} text The tab's text and tooltip
40765      */
40766     setText : function(text){
40767         this.text = text;
40768         this.textEl.update(text);
40769         this.setTooltip(text);
40770         //if(!this.tabPanel.resizeTabs){
40771         //    this.autoSize();
40772         //}
40773     },
40774     /**
40775      * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
40776      */
40777     activate : function(){
40778         this.tabPanel.activate(this.id);
40779     },
40780
40781     /**
40782      * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
40783      */
40784     disable : function(){
40785         if(this.tabPanel.active != this){
40786             this.disabled = true;
40787             this.status_node.addClass("disabled");
40788         }
40789     },
40790
40791     /**
40792      * Enables this TabPanelItem if it was previously disabled.
40793      */
40794     enable : function(){
40795         this.disabled = false;
40796         this.status_node.removeClass("disabled");
40797     },
40798
40799     /**
40800      * Sets the content for this TabPanelItem.
40801      * @param {String} content The content
40802      * @param {Boolean} loadScripts true to look for and load scripts
40803      */
40804     setContent : function(content, loadScripts){
40805         this.bodyEl.update(content, loadScripts);
40806     },
40807
40808     /**
40809      * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
40810      * @return {Roo.UpdateManager} The UpdateManager
40811      */
40812     getUpdateManager : function(){
40813         return this.bodyEl.getUpdateManager();
40814     },
40815
40816     /**
40817      * Set a URL to be used to load the content for this TabPanelItem.
40818      * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
40819      * @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)
40820      * @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)
40821      * @return {Roo.UpdateManager} The UpdateManager
40822      */
40823     setUrl : function(url, params, loadOnce){
40824         if(this.refreshDelegate){
40825             this.un('activate', this.refreshDelegate);
40826         }
40827         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
40828         this.on("activate", this.refreshDelegate);
40829         return this.bodyEl.getUpdateManager();
40830     },
40831
40832     /** @private */
40833     _handleRefresh : function(url, params, loadOnce){
40834         if(!loadOnce || !this.loaded){
40835             var updater = this.bodyEl.getUpdateManager();
40836             updater.update(url, params, this._setLoaded.createDelegate(this));
40837         }
40838     },
40839
40840     /**
40841      *   Forces a content refresh from the URL specified in the {@link #setUrl} method.
40842      *   Will fail silently if the setUrl method has not been called.
40843      *   This does not activate the panel, just updates its content.
40844      */
40845     refresh : function(){
40846         if(this.refreshDelegate){
40847            this.loaded = false;
40848            this.refreshDelegate();
40849         }
40850     },
40851
40852     /** @private */
40853     _setLoaded : function(){
40854         this.loaded = true;
40855     },
40856
40857     /** @private */
40858     closeClick : function(e){
40859         var o = {};
40860         e.stopEvent();
40861         this.fireEvent("beforeclose", this, o);
40862         if(o.cancel !== true){
40863             this.tabPanel.removeTab(this.id);
40864         }
40865     },
40866     /**
40867      * The text displayed in the tooltip for the close icon.
40868      * @type String
40869      */
40870     closeText : "Close this tab"
40871 });
40872 /**
40873 *    This script refer to:
40874 *    Title: International Telephone Input
40875 *    Author: Jack O'Connor
40876 *    Code version:  v12.1.12
40877 *    Availability: https://github.com/jackocnr/intl-tel-input.git
40878 **/
40879
40880 Roo.bootstrap.PhoneInputData = function() {
40881     var d = [
40882       [
40883         "Afghanistan (‫افغانستان‬‎)",
40884         "af",
40885         "93"
40886       ],
40887       [
40888         "Albania (Shqipëri)",
40889         "al",
40890         "355"
40891       ],
40892       [
40893         "Algeria (‫الجزائر‬‎)",
40894         "dz",
40895         "213"
40896       ],
40897       [
40898         "American Samoa",
40899         "as",
40900         "1684"
40901       ],
40902       [
40903         "Andorra",
40904         "ad",
40905         "376"
40906       ],
40907       [
40908         "Angola",
40909         "ao",
40910         "244"
40911       ],
40912       [
40913         "Anguilla",
40914         "ai",
40915         "1264"
40916       ],
40917       [
40918         "Antigua and Barbuda",
40919         "ag",
40920         "1268"
40921       ],
40922       [
40923         "Argentina",
40924         "ar",
40925         "54"
40926       ],
40927       [
40928         "Armenia (Հայաստան)",
40929         "am",
40930         "374"
40931       ],
40932       [
40933         "Aruba",
40934         "aw",
40935         "297"
40936       ],
40937       [
40938         "Australia",
40939         "au",
40940         "61",
40941         0
40942       ],
40943       [
40944         "Austria (Österreich)",
40945         "at",
40946         "43"
40947       ],
40948       [
40949         "Azerbaijan (Azərbaycan)",
40950         "az",
40951         "994"
40952       ],
40953       [
40954         "Bahamas",
40955         "bs",
40956         "1242"
40957       ],
40958       [
40959         "Bahrain (‫البحرين‬‎)",
40960         "bh",
40961         "973"
40962       ],
40963       [
40964         "Bangladesh (বাংলাদেশ)",
40965         "bd",
40966         "880"
40967       ],
40968       [
40969         "Barbados",
40970         "bb",
40971         "1246"
40972       ],
40973       [
40974         "Belarus (Беларусь)",
40975         "by",
40976         "375"
40977       ],
40978       [
40979         "Belgium (België)",
40980         "be",
40981         "32"
40982       ],
40983       [
40984         "Belize",
40985         "bz",
40986         "501"
40987       ],
40988       [
40989         "Benin (Bénin)",
40990         "bj",
40991         "229"
40992       ],
40993       [
40994         "Bermuda",
40995         "bm",
40996         "1441"
40997       ],
40998       [
40999         "Bhutan (འབྲུག)",
41000         "bt",
41001         "975"
41002       ],
41003       [
41004         "Bolivia",
41005         "bo",
41006         "591"
41007       ],
41008       [
41009         "Bosnia and Herzegovina (Босна и Херцеговина)",
41010         "ba",
41011         "387"
41012       ],
41013       [
41014         "Botswana",
41015         "bw",
41016         "267"
41017       ],
41018       [
41019         "Brazil (Brasil)",
41020         "br",
41021         "55"
41022       ],
41023       [
41024         "British Indian Ocean Territory",
41025         "io",
41026         "246"
41027       ],
41028       [
41029         "British Virgin Islands",
41030         "vg",
41031         "1284"
41032       ],
41033       [
41034         "Brunei",
41035         "bn",
41036         "673"
41037       ],
41038       [
41039         "Bulgaria (България)",
41040         "bg",
41041         "359"
41042       ],
41043       [
41044         "Burkina Faso",
41045         "bf",
41046         "226"
41047       ],
41048       [
41049         "Burundi (Uburundi)",
41050         "bi",
41051         "257"
41052       ],
41053       [
41054         "Cambodia (កម្ពុជា)",
41055         "kh",
41056         "855"
41057       ],
41058       [
41059         "Cameroon (Cameroun)",
41060         "cm",
41061         "237"
41062       ],
41063       [
41064         "Canada",
41065         "ca",
41066         "1",
41067         1,
41068         ["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"]
41069       ],
41070       [
41071         "Cape Verde (Kabu Verdi)",
41072         "cv",
41073         "238"
41074       ],
41075       [
41076         "Caribbean Netherlands",
41077         "bq",
41078         "599",
41079         1
41080       ],
41081       [
41082         "Cayman Islands",
41083         "ky",
41084         "1345"
41085       ],
41086       [
41087         "Central African Republic (République centrafricaine)",
41088         "cf",
41089         "236"
41090       ],
41091       [
41092         "Chad (Tchad)",
41093         "td",
41094         "235"
41095       ],
41096       [
41097         "Chile",
41098         "cl",
41099         "56"
41100       ],
41101       [
41102         "China (中国)",
41103         "cn",
41104         "86"
41105       ],
41106       [
41107         "Christmas Island",
41108         "cx",
41109         "61",
41110         2
41111       ],
41112       [
41113         "Cocos (Keeling) Islands",
41114         "cc",
41115         "61",
41116         1
41117       ],
41118       [
41119         "Colombia",
41120         "co",
41121         "57"
41122       ],
41123       [
41124         "Comoros (‫جزر القمر‬‎)",
41125         "km",
41126         "269"
41127       ],
41128       [
41129         "Congo (DRC) (Jamhuri ya Kidemokrasia ya Kongo)",
41130         "cd",
41131         "243"
41132       ],
41133       [
41134         "Congo (Republic) (Congo-Brazzaville)",
41135         "cg",
41136         "242"
41137       ],
41138       [
41139         "Cook Islands",
41140         "ck",
41141         "682"
41142       ],
41143       [
41144         "Costa Rica",
41145         "cr",
41146         "506"
41147       ],
41148       [
41149         "Côte d’Ivoire",
41150         "ci",
41151         "225"
41152       ],
41153       [
41154         "Croatia (Hrvatska)",
41155         "hr",
41156         "385"
41157       ],
41158       [
41159         "Cuba",
41160         "cu",
41161         "53"
41162       ],
41163       [
41164         "Curaçao",
41165         "cw",
41166         "599",
41167         0
41168       ],
41169       [
41170         "Cyprus (Κύπρος)",
41171         "cy",
41172         "357"
41173       ],
41174       [
41175         "Czech Republic (Česká republika)",
41176         "cz",
41177         "420"
41178       ],
41179       [
41180         "Denmark (Danmark)",
41181         "dk",
41182         "45"
41183       ],
41184       [
41185         "Djibouti",
41186         "dj",
41187         "253"
41188       ],
41189       [
41190         "Dominica",
41191         "dm",
41192         "1767"
41193       ],
41194       [
41195         "Dominican Republic (República Dominicana)",
41196         "do",
41197         "1",
41198         2,
41199         ["809", "829", "849"]
41200       ],
41201       [
41202         "Ecuador",
41203         "ec",
41204         "593"
41205       ],
41206       [
41207         "Egypt (‫مصر‬‎)",
41208         "eg",
41209         "20"
41210       ],
41211       [
41212         "El Salvador",
41213         "sv",
41214         "503"
41215       ],
41216       [
41217         "Equatorial Guinea (Guinea Ecuatorial)",
41218         "gq",
41219         "240"
41220       ],
41221       [
41222         "Eritrea",
41223         "er",
41224         "291"
41225       ],
41226       [
41227         "Estonia (Eesti)",
41228         "ee",
41229         "372"
41230       ],
41231       [
41232         "Ethiopia",
41233         "et",
41234         "251"
41235       ],
41236       [
41237         "Falkland Islands (Islas Malvinas)",
41238         "fk",
41239         "500"
41240       ],
41241       [
41242         "Faroe Islands (Føroyar)",
41243         "fo",
41244         "298"
41245       ],
41246       [
41247         "Fiji",
41248         "fj",
41249         "679"
41250       ],
41251       [
41252         "Finland (Suomi)",
41253         "fi",
41254         "358",
41255         0
41256       ],
41257       [
41258         "France",
41259         "fr",
41260         "33"
41261       ],
41262       [
41263         "French Guiana (Guyane française)",
41264         "gf",
41265         "594"
41266       ],
41267       [
41268         "French Polynesia (Polynésie française)",
41269         "pf",
41270         "689"
41271       ],
41272       [
41273         "Gabon",
41274         "ga",
41275         "241"
41276       ],
41277       [
41278         "Gambia",
41279         "gm",
41280         "220"
41281       ],
41282       [
41283         "Georgia (საქართველო)",
41284         "ge",
41285         "995"
41286       ],
41287       [
41288         "Germany (Deutschland)",
41289         "de",
41290         "49"
41291       ],
41292       [
41293         "Ghana (Gaana)",
41294         "gh",
41295         "233"
41296       ],
41297       [
41298         "Gibraltar",
41299         "gi",
41300         "350"
41301       ],
41302       [
41303         "Greece (Ελλάδα)",
41304         "gr",
41305         "30"
41306       ],
41307       [
41308         "Greenland (Kalaallit Nunaat)",
41309         "gl",
41310         "299"
41311       ],
41312       [
41313         "Grenada",
41314         "gd",
41315         "1473"
41316       ],
41317       [
41318         "Guadeloupe",
41319         "gp",
41320         "590",
41321         0
41322       ],
41323       [
41324         "Guam",
41325         "gu",
41326         "1671"
41327       ],
41328       [
41329         "Guatemala",
41330         "gt",
41331         "502"
41332       ],
41333       [
41334         "Guernsey",
41335         "gg",
41336         "44",
41337         1
41338       ],
41339       [
41340         "Guinea (Guinée)",
41341         "gn",
41342         "224"
41343       ],
41344       [
41345         "Guinea-Bissau (Guiné Bissau)",
41346         "gw",
41347         "245"
41348       ],
41349       [
41350         "Guyana",
41351         "gy",
41352         "592"
41353       ],
41354       [
41355         "Haiti",
41356         "ht",
41357         "509"
41358       ],
41359       [
41360         "Honduras",
41361         "hn",
41362         "504"
41363       ],
41364       [
41365         "Hong Kong (香港)",
41366         "hk",
41367         "852"
41368       ],
41369       [
41370         "Hungary (Magyarország)",
41371         "hu",
41372         "36"
41373       ],
41374       [
41375         "Iceland (Ísland)",
41376         "is",
41377         "354"
41378       ],
41379       [
41380         "India (भारत)",
41381         "in",
41382         "91"
41383       ],
41384       [
41385         "Indonesia",
41386         "id",
41387         "62"
41388       ],
41389       [
41390         "Iran (‫ایران‬‎)",
41391         "ir",
41392         "98"
41393       ],
41394       [
41395         "Iraq (‫العراق‬‎)",
41396         "iq",
41397         "964"
41398       ],
41399       [
41400         "Ireland",
41401         "ie",
41402         "353"
41403       ],
41404       [
41405         "Isle of Man",
41406         "im",
41407         "44",
41408         2
41409       ],
41410       [
41411         "Israel (‫ישראל‬‎)",
41412         "il",
41413         "972"
41414       ],
41415       [
41416         "Italy (Italia)",
41417         "it",
41418         "39",
41419         0
41420       ],
41421       [
41422         "Jamaica",
41423         "jm",
41424         "1876"
41425       ],
41426       [
41427         "Japan (日本)",
41428         "jp",
41429         "81"
41430       ],
41431       [
41432         "Jersey",
41433         "je",
41434         "44",
41435         3
41436       ],
41437       [
41438         "Jordan (‫الأردن‬‎)",
41439         "jo",
41440         "962"
41441       ],
41442       [
41443         "Kazakhstan (Казахстан)",
41444         "kz",
41445         "7",
41446         1
41447       ],
41448       [
41449         "Kenya",
41450         "ke",
41451         "254"
41452       ],
41453       [
41454         "Kiribati",
41455         "ki",
41456         "686"
41457       ],
41458       [
41459         "Kosovo",
41460         "xk",
41461         "383"
41462       ],
41463       [
41464         "Kuwait (‫الكويت‬‎)",
41465         "kw",
41466         "965"
41467       ],
41468       [
41469         "Kyrgyzstan (Кыргызстан)",
41470         "kg",
41471         "996"
41472       ],
41473       [
41474         "Laos (ລາວ)",
41475         "la",
41476         "856"
41477       ],
41478       [
41479         "Latvia (Latvija)",
41480         "lv",
41481         "371"
41482       ],
41483       [
41484         "Lebanon (‫لبنان‬‎)",
41485         "lb",
41486         "961"
41487       ],
41488       [
41489         "Lesotho",
41490         "ls",
41491         "266"
41492       ],
41493       [
41494         "Liberia",
41495         "lr",
41496         "231"
41497       ],
41498       [
41499         "Libya (‫ليبيا‬‎)",
41500         "ly",
41501         "218"
41502       ],
41503       [
41504         "Liechtenstein",
41505         "li",
41506         "423"
41507       ],
41508       [
41509         "Lithuania (Lietuva)",
41510         "lt",
41511         "370"
41512       ],
41513       [
41514         "Luxembourg",
41515         "lu",
41516         "352"
41517       ],
41518       [
41519         "Macau (澳門)",
41520         "mo",
41521         "853"
41522       ],
41523       [
41524         "Macedonia (FYROM) (Македонија)",
41525         "mk",
41526         "389"
41527       ],
41528       [
41529         "Madagascar (Madagasikara)",
41530         "mg",
41531         "261"
41532       ],
41533       [
41534         "Malawi",
41535         "mw",
41536         "265"
41537       ],
41538       [
41539         "Malaysia",
41540         "my",
41541         "60"
41542       ],
41543       [
41544         "Maldives",
41545         "mv",
41546         "960"
41547       ],
41548       [
41549         "Mali",
41550         "ml",
41551         "223"
41552       ],
41553       [
41554         "Malta",
41555         "mt",
41556         "356"
41557       ],
41558       [
41559         "Marshall Islands",
41560         "mh",
41561         "692"
41562       ],
41563       [
41564         "Martinique",
41565         "mq",
41566         "596"
41567       ],
41568       [
41569         "Mauritania (‫موريتانيا‬‎)",
41570         "mr",
41571         "222"
41572       ],
41573       [
41574         "Mauritius (Moris)",
41575         "mu",
41576         "230"
41577       ],
41578       [
41579         "Mayotte",
41580         "yt",
41581         "262",
41582         1
41583       ],
41584       [
41585         "Mexico (México)",
41586         "mx",
41587         "52"
41588       ],
41589       [
41590         "Micronesia",
41591         "fm",
41592         "691"
41593       ],
41594       [
41595         "Moldova (Republica Moldova)",
41596         "md",
41597         "373"
41598       ],
41599       [
41600         "Monaco",
41601         "mc",
41602         "377"
41603       ],
41604       [
41605         "Mongolia (Монгол)",
41606         "mn",
41607         "976"
41608       ],
41609       [
41610         "Montenegro (Crna Gora)",
41611         "me",
41612         "382"
41613       ],
41614       [
41615         "Montserrat",
41616         "ms",
41617         "1664"
41618       ],
41619       [
41620         "Morocco (‫المغرب‬‎)",
41621         "ma",
41622         "212",
41623         0
41624       ],
41625       [
41626         "Mozambique (Moçambique)",
41627         "mz",
41628         "258"
41629       ],
41630       [
41631         "Myanmar (Burma) (မြန်မာ)",
41632         "mm",
41633         "95"
41634       ],
41635       [
41636         "Namibia (Namibië)",
41637         "na",
41638         "264"
41639       ],
41640       [
41641         "Nauru",
41642         "nr",
41643         "674"
41644       ],
41645       [
41646         "Nepal (नेपाल)",
41647         "np",
41648         "977"
41649       ],
41650       [
41651         "Netherlands (Nederland)",
41652         "nl",
41653         "31"
41654       ],
41655       [
41656         "New Caledonia (Nouvelle-Calédonie)",
41657         "nc",
41658         "687"
41659       ],
41660       [
41661         "New Zealand",
41662         "nz",
41663         "64"
41664       ],
41665       [
41666         "Nicaragua",
41667         "ni",
41668         "505"
41669       ],
41670       [
41671         "Niger (Nijar)",
41672         "ne",
41673         "227"
41674       ],
41675       [
41676         "Nigeria",
41677         "ng",
41678         "234"
41679       ],
41680       [
41681         "Niue",
41682         "nu",
41683         "683"
41684       ],
41685       [
41686         "Norfolk Island",
41687         "nf",
41688         "672"
41689       ],
41690       [
41691         "North Korea (조선 민주주의 인민 공화국)",
41692         "kp",
41693         "850"
41694       ],
41695       [
41696         "Northern Mariana Islands",
41697         "mp",
41698         "1670"
41699       ],
41700       [
41701         "Norway (Norge)",
41702         "no",
41703         "47",
41704         0
41705       ],
41706       [
41707         "Oman (‫عُمان‬‎)",
41708         "om",
41709         "968"
41710       ],
41711       [
41712         "Pakistan (‫پاکستان‬‎)",
41713         "pk",
41714         "92"
41715       ],
41716       [
41717         "Palau",
41718         "pw",
41719         "680"
41720       ],
41721       [
41722         "Palestine (‫فلسطين‬‎)",
41723         "ps",
41724         "970"
41725       ],
41726       [
41727         "Panama (Panamá)",
41728         "pa",
41729         "507"
41730       ],
41731       [
41732         "Papua New Guinea",
41733         "pg",
41734         "675"
41735       ],
41736       [
41737         "Paraguay",
41738         "py",
41739         "595"
41740       ],
41741       [
41742         "Peru (Perú)",
41743         "pe",
41744         "51"
41745       ],
41746       [
41747         "Philippines",
41748         "ph",
41749         "63"
41750       ],
41751       [
41752         "Poland (Polska)",
41753         "pl",
41754         "48"
41755       ],
41756       [
41757         "Portugal",
41758         "pt",
41759         "351"
41760       ],
41761       [
41762         "Puerto Rico",
41763         "pr",
41764         "1",
41765         3,
41766         ["787", "939"]
41767       ],
41768       [
41769         "Qatar (‫قطر‬‎)",
41770         "qa",
41771         "974"
41772       ],
41773       [
41774         "Réunion (La Réunion)",
41775         "re",
41776         "262",
41777         0
41778       ],
41779       [
41780         "Romania (România)",
41781         "ro",
41782         "40"
41783       ],
41784       [
41785         "Russia (Россия)",
41786         "ru",
41787         "7",
41788         0
41789       ],
41790       [
41791         "Rwanda",
41792         "rw",
41793         "250"
41794       ],
41795       [
41796         "Saint Barthélemy",
41797         "bl",
41798         "590",
41799         1
41800       ],
41801       [
41802         "Saint Helena",
41803         "sh",
41804         "290"
41805       ],
41806       [
41807         "Saint Kitts and Nevis",
41808         "kn",
41809         "1869"
41810       ],
41811       [
41812         "Saint Lucia",
41813         "lc",
41814         "1758"
41815       ],
41816       [
41817         "Saint Martin (Saint-Martin (partie française))",
41818         "mf",
41819         "590",
41820         2
41821       ],
41822       [
41823         "Saint Pierre and Miquelon (Saint-Pierre-et-Miquelon)",
41824         "pm",
41825         "508"
41826       ],
41827       [
41828         "Saint Vincent and the Grenadines",
41829         "vc",
41830         "1784"
41831       ],
41832       [
41833         "Samoa",
41834         "ws",
41835         "685"
41836       ],
41837       [
41838         "San Marino",
41839         "sm",
41840         "378"
41841       ],
41842       [
41843         "São Tomé and Príncipe (São Tomé e Príncipe)",
41844         "st",
41845         "239"
41846       ],
41847       [
41848         "Saudi Arabia (‫المملكة العربية السعودية‬‎)",
41849         "sa",
41850         "966"
41851       ],
41852       [
41853         "Senegal (Sénégal)",
41854         "sn",
41855         "221"
41856       ],
41857       [
41858         "Serbia (Србија)",
41859         "rs",
41860         "381"
41861       ],
41862       [
41863         "Seychelles",
41864         "sc",
41865         "248"
41866       ],
41867       [
41868         "Sierra Leone",
41869         "sl",
41870         "232"
41871       ],
41872       [
41873         "Singapore",
41874         "sg",
41875         "65"
41876       ],
41877       [
41878         "Sint Maarten",
41879         "sx",
41880         "1721"
41881       ],
41882       [
41883         "Slovakia (Slovensko)",
41884         "sk",
41885         "421"
41886       ],
41887       [
41888         "Slovenia (Slovenija)",
41889         "si",
41890         "386"
41891       ],
41892       [
41893         "Solomon Islands",
41894         "sb",
41895         "677"
41896       ],
41897       [
41898         "Somalia (Soomaaliya)",
41899         "so",
41900         "252"
41901       ],
41902       [
41903         "South Africa",
41904         "za",
41905         "27"
41906       ],
41907       [
41908         "South Korea (대한민국)",
41909         "kr",
41910         "82"
41911       ],
41912       [
41913         "South Sudan (‫جنوب السودان‬‎)",
41914         "ss",
41915         "211"
41916       ],
41917       [
41918         "Spain (España)",
41919         "es",
41920         "34"
41921       ],
41922       [
41923         "Sri Lanka (ශ්‍රී ලංකාව)",
41924         "lk",
41925         "94"
41926       ],
41927       [
41928         "Sudan (‫السودان‬‎)",
41929         "sd",
41930         "249"
41931       ],
41932       [
41933         "Suriname",
41934         "sr",
41935         "597"
41936       ],
41937       [
41938         "Svalbard and Jan Mayen",
41939         "sj",
41940         "47",
41941         1
41942       ],
41943       [
41944         "Swaziland",
41945         "sz",
41946         "268"
41947       ],
41948       [
41949         "Sweden (Sverige)",
41950         "se",
41951         "46"
41952       ],
41953       [
41954         "Switzerland (Schweiz)",
41955         "ch",
41956         "41"
41957       ],
41958       [
41959         "Syria (‫سوريا‬‎)",
41960         "sy",
41961         "963"
41962       ],
41963       [
41964         "Taiwan (台灣)",
41965         "tw",
41966         "886"
41967       ],
41968       [
41969         "Tajikistan",
41970         "tj",
41971         "992"
41972       ],
41973       [
41974         "Tanzania",
41975         "tz",
41976         "255"
41977       ],
41978       [
41979         "Thailand (ไทย)",
41980         "th",
41981         "66"
41982       ],
41983       [
41984         "Timor-Leste",
41985         "tl",
41986         "670"
41987       ],
41988       [
41989         "Togo",
41990         "tg",
41991         "228"
41992       ],
41993       [
41994         "Tokelau",
41995         "tk",
41996         "690"
41997       ],
41998       [
41999         "Tonga",
42000         "to",
42001         "676"
42002       ],
42003       [
42004         "Trinidad and Tobago",
42005         "tt",
42006         "1868"
42007       ],
42008       [
42009         "Tunisia (‫تونس‬‎)",
42010         "tn",
42011         "216"
42012       ],
42013       [
42014         "Turkey (Türkiye)",
42015         "tr",
42016         "90"
42017       ],
42018       [
42019         "Turkmenistan",
42020         "tm",
42021         "993"
42022       ],
42023       [
42024         "Turks and Caicos Islands",
42025         "tc",
42026         "1649"
42027       ],
42028       [
42029         "Tuvalu",
42030         "tv",
42031         "688"
42032       ],
42033       [
42034         "U.S. Virgin Islands",
42035         "vi",
42036         "1340"
42037       ],
42038       [
42039         "Uganda",
42040         "ug",
42041         "256"
42042       ],
42043       [
42044         "Ukraine (Україна)",
42045         "ua",
42046         "380"
42047       ],
42048       [
42049         "United Arab Emirates (‫الإمارات العربية المتحدة‬‎)",
42050         "ae",
42051         "971"
42052       ],
42053       [
42054         "United Kingdom",
42055         "gb",
42056         "44",
42057         0
42058       ],
42059       [
42060         "United States",
42061         "us",
42062         "1",
42063         0
42064       ],
42065       [
42066         "Uruguay",
42067         "uy",
42068         "598"
42069       ],
42070       [
42071         "Uzbekistan (Oʻzbekiston)",
42072         "uz",
42073         "998"
42074       ],
42075       [
42076         "Vanuatu",
42077         "vu",
42078         "678"
42079       ],
42080       [
42081         "Vatican City (Città del Vaticano)",
42082         "va",
42083         "39",
42084         1
42085       ],
42086       [
42087         "Venezuela",
42088         "ve",
42089         "58"
42090       ],
42091       [
42092         "Vietnam (Việt Nam)",
42093         "vn",
42094         "84"
42095       ],
42096       [
42097         "Wallis and Futuna (Wallis-et-Futuna)",
42098         "wf",
42099         "681"
42100       ],
42101       [
42102         "Western Sahara (‫الصحراء الغربية‬‎)",
42103         "eh",
42104         "212",
42105         1
42106       ],
42107       [
42108         "Yemen (‫اليمن‬‎)",
42109         "ye",
42110         "967"
42111       ],
42112       [
42113         "Zambia",
42114         "zm",
42115         "260"
42116       ],
42117       [
42118         "Zimbabwe",
42119         "zw",
42120         "263"
42121       ],
42122       [
42123         "Åland Islands",
42124         "ax",
42125         "358",
42126         1
42127       ]
42128   ];
42129   
42130   return d;
42131 }/**
42132 *    This script refer to:
42133 *    Title: International Telephone Input
42134 *    Author: Jack O'Connor
42135 *    Code version:  v12.1.12
42136 *    Availability: https://github.com/jackocnr/intl-tel-input.git
42137 **/
42138
42139 /**
42140  * @class Roo.bootstrap.PhoneInput
42141  * @extends Roo.bootstrap.TriggerField
42142  * An input with International dial-code selection
42143  
42144  * @cfg {String} defaultDialCode default '+852'
42145  * @cfg {Array} preferedCountries default []
42146   
42147  * @constructor
42148  * Create a new PhoneInput.
42149  * @param {Object} config Configuration options
42150  */
42151
42152 Roo.bootstrap.PhoneInput = function(config) {
42153     Roo.bootstrap.PhoneInput.superclass.constructor.call(this, config);
42154 };
42155
42156 Roo.extend(Roo.bootstrap.PhoneInput, Roo.bootstrap.TriggerField, {
42157         
42158         listWidth: undefined,
42159         
42160         selectedClass: 'active',
42161         
42162         invalidClass : "has-warning",
42163         
42164         validClass: 'has-success',
42165         
42166         allowed: '0123456789',
42167         
42168         max_length: 15,
42169         
42170         /**
42171          * @cfg {String} defaultDialCode The default dial code when initializing the input
42172          */
42173         defaultDialCode: '+852',
42174         
42175         /**
42176          * @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
42177          */
42178         preferedCountries: false,
42179         
42180         getAutoCreate : function()
42181         {
42182             var data = Roo.bootstrap.PhoneInputData();
42183             var align = this.labelAlign || this.parentLabelAlign();
42184             var id = Roo.id();
42185             
42186             this.allCountries = [];
42187             this.dialCodeMapping = [];
42188             
42189             for (var i = 0; i < data.length; i++) {
42190               var c = data[i];
42191               this.allCountries[i] = {
42192                 name: c[0],
42193                 iso2: c[1],
42194                 dialCode: c[2],
42195                 priority: c[3] || 0,
42196                 areaCodes: c[4] || null
42197               };
42198               this.dialCodeMapping[c[2]] = {
42199                   name: c[0],
42200                   iso2: c[1],
42201                   priority: c[3] || 0,
42202                   areaCodes: c[4] || null
42203               };
42204             }
42205             
42206             var cfg = {
42207                 cls: 'form-group',
42208                 cn: []
42209             };
42210             
42211             var input =  {
42212                 tag: 'input',
42213                 id : id,
42214                 // type: 'number', -- do not use number - we get the flaky up/down arrows.
42215                 maxlength: this.max_length,
42216                 cls : 'form-control tel-input',
42217                 autocomplete: 'new-password'
42218             };
42219             
42220             var hiddenInput = {
42221                 tag: 'input',
42222                 type: 'hidden',
42223                 cls: 'hidden-tel-input'
42224             };
42225             
42226             if (this.name) {
42227                 hiddenInput.name = this.name;
42228             }
42229             
42230             if (this.disabled) {
42231                 input.disabled = true;
42232             }
42233             
42234             var flag_container = {
42235                 tag: 'div',
42236                 cls: 'flag-box',
42237                 cn: [
42238                     {
42239                         tag: 'div',
42240                         cls: 'flag'
42241                     },
42242                     {
42243                         tag: 'div',
42244                         cls: 'caret'
42245                     }
42246                 ]
42247             };
42248             
42249             var box = {
42250                 tag: 'div',
42251                 cls: this.hasFeedback ? 'has-feedback' : '',
42252                 cn: [
42253                     hiddenInput,
42254                     input,
42255                     {
42256                         tag: 'input',
42257                         cls: 'dial-code-holder',
42258                         disabled: true
42259                     }
42260                 ]
42261             };
42262             
42263             var container = {
42264                 cls: 'roo-select2-container input-group',
42265                 cn: [
42266                     flag_container,
42267                     box
42268                 ]
42269             };
42270             
42271             if (this.fieldLabel.length) {
42272                 var indicator = {
42273                     tag: 'i',
42274                     tooltip: 'This field is required'
42275                 };
42276                 
42277                 var label = {
42278                     tag: 'label',
42279                     'for':  id,
42280                     cls: 'control-label',
42281                     cn: []
42282                 };
42283                 
42284                 var label_text = {
42285                     tag: 'span',
42286                     html: this.fieldLabel
42287                 };
42288                 
42289                 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
42290                 label.cn = [
42291                     indicator,
42292                     label_text
42293                 ];
42294                 
42295                 if(this.indicatorpos == 'right') {
42296                     indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
42297                     label.cn = [
42298                         label_text,
42299                         indicator
42300                     ];
42301                 }
42302                 
42303                 if(align == 'left') {
42304                     container = {
42305                         tag: 'div',
42306                         cn: [
42307                             container
42308                         ]
42309                     };
42310                     
42311                     if(this.labelWidth > 12){
42312                         label.style = "width: " + this.labelWidth + 'px';
42313                     }
42314                     if(this.labelWidth < 13 && this.labelmd == 0){
42315                         this.labelmd = this.labelWidth;
42316                     }
42317                     if(this.labellg > 0){
42318                         label.cls += ' col-lg-' + this.labellg;
42319                         input.cls += ' col-lg-' + (12 - this.labellg);
42320                     }
42321                     if(this.labelmd > 0){
42322                         label.cls += ' col-md-' + this.labelmd;
42323                         container.cls += ' col-md-' + (12 - this.labelmd);
42324                     }
42325                     if(this.labelsm > 0){
42326                         label.cls += ' col-sm-' + this.labelsm;
42327                         container.cls += ' col-sm-' + (12 - this.labelsm);
42328                     }
42329                     if(this.labelxs > 0){
42330                         label.cls += ' col-xs-' + this.labelxs;
42331                         container.cls += ' col-xs-' + (12 - this.labelxs);
42332                     }
42333                 }
42334             }
42335             
42336             cfg.cn = [
42337                 label,
42338                 container
42339             ];
42340             
42341             var settings = this;
42342             
42343             ['xs','sm','md','lg'].map(function(size){
42344                 if (settings[size]) {
42345                     cfg.cls += ' col-' + size + '-' + settings[size];
42346                 }
42347             });
42348             
42349             this.store = new Roo.data.Store({
42350                 proxy : new Roo.data.MemoryProxy({}),
42351                 reader : new Roo.data.JsonReader({
42352                     fields : [
42353                         {
42354                             'name' : 'name',
42355                             'type' : 'string'
42356                         },
42357                         {
42358                             'name' : 'iso2',
42359                             'type' : 'string'
42360                         },
42361                         {
42362                             'name' : 'dialCode',
42363                             'type' : 'string'
42364                         },
42365                         {
42366                             'name' : 'priority',
42367                             'type' : 'string'
42368                         },
42369                         {
42370                             'name' : 'areaCodes',
42371                             'type' : 'string'
42372                         }
42373                     ]
42374                 })
42375             });
42376             
42377             if(!this.preferedCountries) {
42378                 this.preferedCountries = [
42379                     'hk',
42380                     'gb',
42381                     'us'
42382                 ];
42383             }
42384             
42385             var p = this.preferedCountries.reverse();
42386             
42387             if(p) {
42388                 for (var i = 0; i < p.length; i++) {
42389                     for (var j = 0; j < this.allCountries.length; j++) {
42390                         if(this.allCountries[j].iso2 == p[i]) {
42391                             var t = this.allCountries[j];
42392                             this.allCountries.splice(j,1);
42393                             this.allCountries.unshift(t);
42394                         }
42395                     } 
42396                 }
42397             }
42398             
42399             this.store.proxy.data = {
42400                 success: true,
42401                 data: this.allCountries
42402             };
42403             
42404             return cfg;
42405         },
42406         
42407         initEvents : function()
42408         {
42409             this.createList();
42410             Roo.bootstrap.PhoneInput.superclass.initEvents.call(this);
42411             
42412             this.indicator = this.indicatorEl();
42413             this.flag = this.flagEl();
42414             this.dialCodeHolder = this.dialCodeHolderEl();
42415             
42416             this.trigger = this.el.select('div.flag-box',true).first();
42417             this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
42418             
42419             var _this = this;
42420             
42421             (function(){
42422                 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
42423                 _this.list.setWidth(lw);
42424             }).defer(100);
42425             
42426             this.list.on('mouseover', this.onViewOver, this);
42427             this.list.on('mousemove', this.onViewMove, this);
42428             this.inputEl().on("keyup", this.onKeyUp, this);
42429             this.inputEl().on("keypress", this.onKeyPress, this);
42430             
42431             this.tpl = '<li><a href="#"><div class="flag {iso2}"></div>{name} <span class="dial-code">+{dialCode}</span></a></li>';
42432
42433             this.view = new Roo.View(this.list, this.tpl, {
42434                 singleSelect:true, store: this.store, selectedClass: this.selectedClass
42435             });
42436             
42437             this.view.on('click', this.onViewClick, this);
42438             this.setValue(this.defaultDialCode);
42439         },
42440         
42441         onTriggerClick : function(e)
42442         {
42443             Roo.log('trigger click');
42444             if(this.disabled){
42445                 return;
42446             }
42447             
42448             if(this.isExpanded()){
42449                 this.collapse();
42450                 this.hasFocus = false;
42451             }else {
42452                 this.store.load({});
42453                 this.hasFocus = true;
42454                 this.expand();
42455             }
42456         },
42457         
42458         isExpanded : function()
42459         {
42460             return this.list.isVisible();
42461         },
42462         
42463         collapse : function()
42464         {
42465             if(!this.isExpanded()){
42466                 return;
42467             }
42468             this.list.hide();
42469             Roo.get(document).un('mousedown', this.collapseIf, this);
42470             Roo.get(document).un('mousewheel', this.collapseIf, this);
42471             this.fireEvent('collapse', this);
42472             this.validate();
42473         },
42474         
42475         expand : function()
42476         {
42477             Roo.log('expand');
42478
42479             if(this.isExpanded() || !this.hasFocus){
42480                 return;
42481             }
42482             
42483             var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
42484             this.list.setWidth(lw);
42485             
42486             this.list.show();
42487             this.restrictHeight();
42488             
42489             Roo.get(document).on('mousedown', this.collapseIf, this);
42490             Roo.get(document).on('mousewheel', this.collapseIf, this);
42491             
42492             this.fireEvent('expand', this);
42493         },
42494         
42495         restrictHeight : function()
42496         {
42497             this.list.alignTo(this.inputEl(), this.listAlign);
42498             this.list.alignTo(this.inputEl(), this.listAlign);
42499         },
42500         
42501         onViewOver : function(e, t)
42502         {
42503             if(this.inKeyMode){
42504                 return;
42505             }
42506             var item = this.view.findItemFromChild(t);
42507             
42508             if(item){
42509                 var index = this.view.indexOf(item);
42510                 this.select(index, false);
42511             }
42512         },
42513
42514         // private
42515         onViewClick : function(view, doFocus, el, e)
42516         {
42517             var index = this.view.getSelectedIndexes()[0];
42518             
42519             var r = this.store.getAt(index);
42520             
42521             if(r){
42522                 this.onSelect(r, index);
42523             }
42524             if(doFocus !== false && !this.blockFocus){
42525                 this.inputEl().focus();
42526             }
42527         },
42528         
42529         onViewMove : function(e, t)
42530         {
42531             this.inKeyMode = false;
42532         },
42533         
42534         select : function(index, scrollIntoView)
42535         {
42536             this.selectedIndex = index;
42537             this.view.select(index);
42538             if(scrollIntoView !== false){
42539                 var el = this.view.getNode(index);
42540                 if(el){
42541                     this.list.scrollChildIntoView(el, false);
42542                 }
42543             }
42544         },
42545         
42546         createList : function()
42547         {
42548             this.list = Roo.get(document.body).createChild({
42549                 tag: 'ul',
42550                 cls: 'typeahead typeahead-long dropdown-menu tel-list',
42551                 style: 'display:none'
42552             });
42553             
42554             this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
42555         },
42556         
42557         collapseIf : function(e)
42558         {
42559             var in_combo  = e.within(this.el);
42560             var in_list =  e.within(this.list);
42561             var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
42562             
42563             if (in_combo || in_list || is_list) {
42564                 return;
42565             }
42566             this.collapse();
42567         },
42568         
42569         onSelect : function(record, index)
42570         {
42571             if(this.fireEvent('beforeselect', this, record, index) !== false){
42572                 
42573                 this.setFlagClass(record.data.iso2);
42574                 this.setDialCode(record.data.dialCode);
42575                 this.hasFocus = false;
42576                 this.collapse();
42577                 this.fireEvent('select', this, record, index);
42578             }
42579         },
42580         
42581         flagEl : function()
42582         {
42583             var flag = this.el.select('div.flag',true).first();
42584             if(!flag){
42585                 return false;
42586             }
42587             return flag;
42588         },
42589         
42590         dialCodeHolderEl : function()
42591         {
42592             var d = this.el.select('input.dial-code-holder',true).first();
42593             if(!d){
42594                 return false;
42595             }
42596             return d;
42597         },
42598         
42599         setDialCode : function(v)
42600         {
42601             this.dialCodeHolder.dom.value = '+'+v;
42602         },
42603         
42604         setFlagClass : function(n)
42605         {
42606             this.flag.dom.className = 'flag '+n;
42607         },
42608         
42609         getValue : function()
42610         {
42611             var v = this.inputEl().getValue();
42612             if(this.dialCodeHolder) {
42613                 v = this.dialCodeHolder.dom.value+this.inputEl().getValue();
42614             }
42615             return v;
42616         },
42617         
42618         setValue : function(v)
42619         {
42620             var d = this.getDialCode(v);
42621             
42622             //invalid dial code
42623             if(v.length == 0 || !d || d.length == 0) {
42624                 if(this.rendered){
42625                     this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
42626                     this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
42627                 }
42628                 return;
42629             }
42630             
42631             //valid dial code
42632             this.setFlagClass(this.dialCodeMapping[d].iso2);
42633             this.setDialCode(d);
42634             this.inputEl().dom.value = v.replace('+'+d,'');
42635             this.hiddenEl().dom.value = this.getValue();
42636             
42637             this.validate();
42638         },
42639         
42640         getDialCode : function(v)
42641         {
42642             v = v ||  '';
42643             
42644             if (v.length == 0) {
42645                 return this.dialCodeHolder.dom.value;
42646             }
42647             
42648             var dialCode = "";
42649             if (v.charAt(0) != "+") {
42650                 return false;
42651             }
42652             var numericChars = "";
42653             for (var i = 1; i < v.length; i++) {
42654               var c = v.charAt(i);
42655               if (!isNaN(c)) {
42656                 numericChars += c;
42657                 if (this.dialCodeMapping[numericChars]) {
42658                   dialCode = v.substr(1, i);
42659                 }
42660                 if (numericChars.length == 4) {
42661                   break;
42662                 }
42663               }
42664             }
42665             return dialCode;
42666         },
42667         
42668         reset : function()
42669         {
42670             this.setValue(this.defaultDialCode);
42671             this.validate();
42672         },
42673         
42674         hiddenEl : function()
42675         {
42676             return this.el.select('input.hidden-tel-input',true).first();
42677         },
42678         
42679         // after setting val
42680         onKeyUp : function(e){
42681             this.setValue(this.getValue());
42682         },
42683         
42684         onKeyPress : function(e){
42685             if(this.allowed.indexOf(String.fromCharCode(e.getCharCode())) === -1){
42686                 e.stopEvent();
42687             }
42688         }
42689         
42690 });
42691 /**
42692  * @class Roo.bootstrap.MoneyField
42693  * @extends Roo.bootstrap.ComboBox
42694  * Bootstrap MoneyField class
42695  * 
42696  * @constructor
42697  * Create a new MoneyField.
42698  * @param {Object} config Configuration options
42699  */
42700
42701 Roo.bootstrap.MoneyField = function(config) {
42702     
42703     Roo.bootstrap.MoneyField.superclass.constructor.call(this, config);
42704     
42705 };
42706
42707 Roo.extend(Roo.bootstrap.MoneyField, Roo.bootstrap.ComboBox, {
42708     
42709     /**
42710      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
42711      */
42712     allowDecimals : true,
42713     /**
42714      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
42715      */
42716     decimalSeparator : ".",
42717     /**
42718      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
42719      */
42720     decimalPrecision : 0,
42721     /**
42722      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
42723      */
42724     allowNegative : true,
42725     /**
42726      * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
42727      */
42728     allowZero: true,
42729     /**
42730      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
42731      */
42732     minValue : Number.NEGATIVE_INFINITY,
42733     /**
42734      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
42735      */
42736     maxValue : Number.MAX_VALUE,
42737     /**
42738      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
42739      */
42740     minText : "The minimum value for this field is {0}",
42741     /**
42742      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
42743      */
42744     maxText : "The maximum value for this field is {0}",
42745     /**
42746      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
42747      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
42748      */
42749     nanText : "{0} is not a valid number",
42750     /**
42751      * @cfg {Boolean} castInt (true|false) cast int if true (defalut true)
42752      */
42753     castInt : true,
42754     /**
42755      * @cfg {String} defaults currency of the MoneyField
42756      * value should be in lkey
42757      */
42758     defaultCurrency : false,
42759     /**
42760      * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
42761      */
42762     thousandsDelimiter : false,
42763     /**
42764      * @cfg {Number} max_length Maximum input field length allowed (defaults to Number.MAX_VALUE)
42765      */
42766     max_length: false,
42767     
42768     inputlg : 9,
42769     inputmd : 9,
42770     inputsm : 9,
42771     inputxs : 6,
42772     
42773     store : false,
42774     
42775     getAutoCreate : function()
42776     {
42777         var align = this.labelAlign || this.parentLabelAlign();
42778         
42779         var id = Roo.id();
42780
42781         var cfg = {
42782             cls: 'form-group',
42783             cn: []
42784         };
42785
42786         var input =  {
42787             tag: 'input',
42788             id : id,
42789             cls : 'form-control roo-money-amount-input',
42790             autocomplete: 'new-password'
42791         };
42792         
42793         var hiddenInput = {
42794             tag: 'input',
42795             type: 'hidden',
42796             id: Roo.id(),
42797             cls: 'hidden-number-input'
42798         };
42799         
42800         if(this.max_length) {
42801             input.maxlength = this.max_length; 
42802         }
42803         
42804         if (this.name) {
42805             hiddenInput.name = this.name;
42806         }
42807
42808         if (this.disabled) {
42809             input.disabled = true;
42810         }
42811
42812         var clg = 12 - this.inputlg;
42813         var cmd = 12 - this.inputmd;
42814         var csm = 12 - this.inputsm;
42815         var cxs = 12 - this.inputxs;
42816         
42817         var container = {
42818             tag : 'div',
42819             cls : 'row roo-money-field',
42820             cn : [
42821                 {
42822                     tag : 'div',
42823                     cls : 'roo-money-currency column col-lg-' + clg + ' col-md-' + cmd + ' col-sm-' + csm + ' col-xs-' + cxs,
42824                     cn : [
42825                         {
42826                             tag : 'div',
42827                             cls: 'roo-select2-container input-group',
42828                             cn: [
42829                                 {
42830                                     tag : 'input',
42831                                     cls : 'form-control roo-money-currency-input',
42832                                     autocomplete: 'new-password',
42833                                     readOnly : 1,
42834                                     name : this.currencyName
42835                                 },
42836                                 {
42837                                     tag :'span',
42838                                     cls : 'input-group-addon',
42839                                     cn : [
42840                                         {
42841                                             tag: 'span',
42842                                             cls: 'caret'
42843                                         }
42844                                     ]
42845                                 }
42846                             ]
42847                         }
42848                     ]
42849                 },
42850                 {
42851                     tag : 'div',
42852                     cls : 'roo-money-amount column col-lg-' + this.inputlg + ' col-md-' + this.inputmd + ' col-sm-' + this.inputsm + ' col-xs-' + this.inputxs,
42853                     cn : [
42854                         {
42855                             tag: 'div',
42856                             cls: this.hasFeedback ? 'has-feedback' : '',
42857                             cn: [
42858                                 input
42859                             ]
42860                         }
42861                     ]
42862                 }
42863             ]
42864             
42865         };
42866         
42867         if (this.fieldLabel.length) {
42868             var indicator = {
42869                 tag: 'i',
42870                 tooltip: 'This field is required'
42871             };
42872
42873             var label = {
42874                 tag: 'label',
42875                 'for':  id,
42876                 cls: 'control-label',
42877                 cn: []
42878             };
42879
42880             var label_text = {
42881                 tag: 'span',
42882                 html: this.fieldLabel
42883             };
42884
42885             indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
42886             label.cn = [
42887                 indicator,
42888                 label_text
42889             ];
42890
42891             if(this.indicatorpos == 'right') {
42892                 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
42893                 label.cn = [
42894                     label_text,
42895                     indicator
42896                 ];
42897             }
42898
42899             if(align == 'left') {
42900                 container = {
42901                     tag: 'div',
42902                     cn: [
42903                         container
42904                     ]
42905                 };
42906
42907                 if(this.labelWidth > 12){
42908                     label.style = "width: " + this.labelWidth + 'px';
42909                 }
42910                 if(this.labelWidth < 13 && this.labelmd == 0){
42911                     this.labelmd = this.labelWidth;
42912                 }
42913                 if(this.labellg > 0){
42914                     label.cls += ' col-lg-' + this.labellg;
42915                     input.cls += ' col-lg-' + (12 - this.labellg);
42916                 }
42917                 if(this.labelmd > 0){
42918                     label.cls += ' col-md-' + this.labelmd;
42919                     container.cls += ' col-md-' + (12 - this.labelmd);
42920                 }
42921                 if(this.labelsm > 0){
42922                     label.cls += ' col-sm-' + this.labelsm;
42923                     container.cls += ' col-sm-' + (12 - this.labelsm);
42924                 }
42925                 if(this.labelxs > 0){
42926                     label.cls += ' col-xs-' + this.labelxs;
42927                     container.cls += ' col-xs-' + (12 - this.labelxs);
42928                 }
42929             }
42930         }
42931
42932         cfg.cn = [
42933             label,
42934             container,
42935             hiddenInput
42936         ];
42937         
42938         var settings = this;
42939
42940         ['xs','sm','md','lg'].map(function(size){
42941             if (settings[size]) {
42942                 cfg.cls += ' col-' + size + '-' + settings[size];
42943             }
42944         });
42945         
42946         return cfg;
42947     },
42948     
42949     initEvents : function()
42950     {
42951         this.indicator = this.indicatorEl();
42952         
42953         this.initCurrencyEvent();
42954         
42955         this.initNumberEvent();
42956     },
42957     
42958     initCurrencyEvent : function()
42959     {
42960         if (!this.store) {
42961             throw "can not find store for combo";
42962         }
42963         
42964         this.store = Roo.factory(this.store, Roo.data);
42965         this.store.parent = this;
42966         
42967         this.createList();
42968         
42969         this.triggerEl = this.el.select('.input-group-addon', true).first();
42970         
42971         this.triggerEl.on("click", this.onTriggerClick, this, { preventDefault : true });
42972         
42973         var _this = this;
42974         
42975         (function(){
42976             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
42977             _this.list.setWidth(lw);
42978         }).defer(100);
42979         
42980         this.list.on('mouseover', this.onViewOver, this);
42981         this.list.on('mousemove', this.onViewMove, this);
42982         this.list.on('scroll', this.onViewScroll, this);
42983         
42984         if(!this.tpl){
42985             this.tpl = '<li><a href="#">{' + this.currencyField + '}</a></li>';
42986         }
42987         
42988         this.view = new Roo.View(this.list, this.tpl, {
42989             singleSelect:true, store: this.store, selectedClass: this.selectedClass
42990         });
42991         
42992         this.view.on('click', this.onViewClick, this);
42993         
42994         this.store.on('beforeload', this.onBeforeLoad, this);
42995         this.store.on('load', this.onLoad, this);
42996         this.store.on('loadexception', this.onLoadException, this);
42997         
42998         this.keyNav = new Roo.KeyNav(this.currencyEl(), {
42999             "up" : function(e){
43000                 this.inKeyMode = true;
43001                 this.selectPrev();
43002             },
43003
43004             "down" : function(e){
43005                 if(!this.isExpanded()){
43006                     this.onTriggerClick();
43007                 }else{
43008                     this.inKeyMode = true;
43009                     this.selectNext();
43010                 }
43011             },
43012
43013             "enter" : function(e){
43014                 this.collapse();
43015                 
43016                 if(this.fireEvent("specialkey", this, e)){
43017                     this.onViewClick(false);
43018                 }
43019                 
43020                 return true;
43021             },
43022
43023             "esc" : function(e){
43024                 this.collapse();
43025             },
43026
43027             "tab" : function(e){
43028                 this.collapse();
43029                 
43030                 if(this.fireEvent("specialkey", this, e)){
43031                     this.onViewClick(false);
43032                 }
43033                 
43034                 return true;
43035             },
43036
43037             scope : this,
43038
43039             doRelay : function(foo, bar, hname){
43040                 if(hname == 'down' || this.scope.isExpanded()){
43041                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
43042                 }
43043                 return true;
43044             },
43045
43046             forceKeyDown: true
43047         });
43048         
43049         this.currencyEl().on("click", this.onTriggerClick, this, { preventDefault : true });
43050         
43051     },
43052     
43053     initNumberEvent : function(e)
43054     {
43055         this.inputEl().on("keydown" , this.fireKey,  this);
43056         this.inputEl().on("focus", this.onFocus,  this);
43057         this.inputEl().on("blur", this.onBlur,  this);
43058         
43059         this.inputEl().relayEvent('keyup', this);
43060         
43061         if(this.indicator){
43062             this.indicator.addClass('invisible');
43063         }
43064  
43065         this.originalValue = this.getValue();
43066         
43067         if(this.validationEvent == 'keyup'){
43068             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
43069             this.inputEl().on('keyup', this.filterValidation, this);
43070         }
43071         else if(this.validationEvent !== false){
43072             this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
43073         }
43074         
43075         if(this.selectOnFocus){
43076             this.on("focus", this.preFocus, this);
43077             
43078         }
43079         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
43080             this.inputEl().on("keypress", this.filterKeys, this);
43081         } else {
43082             this.inputEl().relayEvent('keypress', this);
43083         }
43084         
43085         var allowed = "0123456789";
43086         
43087         if(this.allowDecimals){
43088             allowed += this.decimalSeparator;
43089         }
43090         
43091         if(this.allowNegative){
43092             allowed += "-";
43093         }
43094         
43095         if(this.thousandsDelimiter) {
43096             allowed += ",";
43097         }
43098         
43099         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
43100         
43101         var keyPress = function(e){
43102             
43103             var k = e.getKey();
43104             
43105             var c = e.getCharCode();
43106             
43107             if(
43108                     (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
43109                     allowed.indexOf(String.fromCharCode(c)) === -1
43110             ){
43111                 e.stopEvent();
43112                 return;
43113             }
43114             
43115             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
43116                 return;
43117             }
43118             
43119             if(allowed.indexOf(String.fromCharCode(c)) === -1){
43120                 e.stopEvent();
43121             }
43122         };
43123         
43124         this.inputEl().on("keypress", keyPress, this);
43125         
43126     },
43127     
43128     onTriggerClick : function(e)
43129     {   
43130         if(this.disabled){
43131             return;
43132         }
43133         
43134         this.page = 0;
43135         this.loadNext = false;
43136         
43137         if(this.isExpanded()){
43138             this.collapse();
43139             return;
43140         }
43141         
43142         this.hasFocus = true;
43143         
43144         if(this.triggerAction == 'all') {
43145             this.doQuery(this.allQuery, true);
43146             return;
43147         }
43148         
43149         this.doQuery(this.getRawValue());
43150     },
43151     
43152     getCurrency : function()
43153     {   
43154         var v = this.currencyEl().getValue();
43155         
43156         return v;
43157     },
43158     
43159     restrictHeight : function()
43160     {
43161         this.list.alignTo(this.currencyEl(), this.listAlign);
43162         this.list.alignTo(this.currencyEl(), this.listAlign);
43163     },
43164     
43165     onViewClick : function(view, doFocus, el, e)
43166     {
43167         var index = this.view.getSelectedIndexes()[0];
43168         
43169         var r = this.store.getAt(index);
43170         
43171         if(r){
43172             this.onSelect(r, index);
43173         }
43174     },
43175     
43176     onSelect : function(record, index){
43177         
43178         if(this.fireEvent('beforeselect', this, record, index) !== false){
43179         
43180             this.setFromCurrencyData(index > -1 ? record.data : false);
43181             
43182             this.collapse();
43183             
43184             this.fireEvent('select', this, record, index);
43185         }
43186     },
43187     
43188     setFromCurrencyData : function(o)
43189     {
43190         var currency = '';
43191         
43192         this.lastCurrency = o;
43193         
43194         if (this.currencyField) {
43195             currency = !o || typeof(o[this.currencyField]) == 'undefined' ? '' : o[this.currencyField];
43196         } else {
43197             Roo.log('no  currencyField value set for '+ (this.name ? this.name : this.id));
43198         }
43199         
43200         this.lastSelectionText = currency;
43201         
43202         //setting default currency
43203         if(o[this.currencyField] * 1 == 0 && this.defaultCurrency) {
43204             this.setCurrency(this.defaultCurrency);
43205             return;
43206         }
43207         
43208         this.setCurrency(currency);
43209     },
43210     
43211     setFromData : function(o)
43212     {
43213         var c = {};
43214         
43215         c[this.currencyField] = !o || typeof(o[this.currencyName]) == 'undefined' ? '' : o[this.currencyName];
43216         
43217         this.setFromCurrencyData(c);
43218         
43219         var value = '';
43220         
43221         if (this.name) {
43222             value = !o || typeof(o[this.name]) == 'undefined' ? '' : o[this.name];
43223         } else {
43224             Roo.log('no value set for '+ (this.name ? this.name : this.id));
43225         }
43226         
43227         this.setValue(value);
43228         
43229     },
43230     
43231     setCurrency : function(v)
43232     {   
43233         this.currencyValue = v;
43234         
43235         if(this.rendered){
43236             this.currencyEl().dom.value = (v === null || v === undefined ? '' : v);
43237             this.validate();
43238         }
43239     },
43240     
43241     setValue : function(v)
43242     {
43243         v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
43244         
43245         this.value = v;
43246         
43247         if(this.rendered){
43248             
43249             this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
43250             
43251             this.inputEl().dom.value = (v == '') ? '' :
43252                 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
43253             
43254             if(!this.allowZero && v === '0') {
43255                 this.hiddenEl().dom.value = '';
43256                 this.inputEl().dom.value = '';
43257             }
43258             
43259             this.validate();
43260         }
43261     },
43262     
43263     getRawValue : function()
43264     {
43265         var v = this.inputEl().getValue();
43266         
43267         return v;
43268     },
43269     
43270     getValue : function()
43271     {
43272         return this.fixPrecision(this.parseValue(this.getRawValue()));
43273     },
43274     
43275     parseValue : function(value)
43276     {
43277         if(this.thousandsDelimiter) {
43278             value += "";
43279             r = new RegExp(",", "g");
43280             value = value.replace(r, "");
43281         }
43282         
43283         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
43284         return isNaN(value) ? '' : value;
43285         
43286     },
43287     
43288     fixPrecision : function(value)
43289     {
43290         if(this.thousandsDelimiter) {
43291             value += "";
43292             r = new RegExp(",", "g");
43293             value = value.replace(r, "");
43294         }
43295         
43296         var nan = isNaN(value);
43297         
43298         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
43299             return nan ? '' : value;
43300         }
43301         return parseFloat(value).toFixed(this.decimalPrecision);
43302     },
43303     
43304     decimalPrecisionFcn : function(v)
43305     {
43306         return Math.floor(v);
43307     },
43308     
43309     validateValue : function(value)
43310     {
43311         if(!Roo.bootstrap.MoneyField.superclass.validateValue.call(this, value)){
43312             return false;
43313         }
43314         
43315         var num = this.parseValue(value);
43316         
43317         if(isNaN(num)){
43318             this.markInvalid(String.format(this.nanText, value));
43319             return false;
43320         }
43321         
43322         if(num < this.minValue){
43323             this.markInvalid(String.format(this.minText, this.minValue));
43324             return false;
43325         }
43326         
43327         if(num > this.maxValue){
43328             this.markInvalid(String.format(this.maxText, this.maxValue));
43329             return false;
43330         }
43331         
43332         return true;
43333     },
43334     
43335     validate : function()
43336     {
43337         if(this.disabled || this.allowBlank){
43338             this.markValid();
43339             return true;
43340         }
43341         
43342         var currency = this.getCurrency();
43343         
43344         if(this.validateValue(this.getRawValue()) && currency.length){
43345             this.markValid();
43346             return true;
43347         }
43348         
43349         this.markInvalid();
43350         return false;
43351     },
43352     
43353     getName: function()
43354     {
43355         return this.name;
43356     },
43357     
43358     beforeBlur : function()
43359     {
43360         if(!this.castInt){
43361             return;
43362         }
43363         
43364         var v = this.parseValue(this.getRawValue());
43365         
43366         if(v || v == 0){
43367             this.setValue(v);
43368         }
43369     },
43370     
43371     onBlur : function()
43372     {
43373         this.beforeBlur();
43374         
43375         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
43376             //this.el.removeClass(this.focusClass);
43377         }
43378         
43379         this.hasFocus = false;
43380         
43381         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
43382             this.validate();
43383         }
43384         
43385         var v = this.getValue();
43386         
43387         if(String(v) !== String(this.startValue)){
43388             this.fireEvent('change', this, v, this.startValue);
43389         }
43390         
43391         this.fireEvent("blur", this);
43392     },
43393     
43394     inputEl : function()
43395     {
43396         return this.el.select('.roo-money-amount-input', true).first();
43397     },
43398     
43399     currencyEl : function()
43400     {
43401         return this.el.select('.roo-money-currency-input', true).first();
43402     },
43403     
43404     hiddenEl : function()
43405     {
43406         return this.el.select('input.hidden-number-input',true).first();
43407     }
43408     
43409 });/**
43410  * @class Roo.bootstrap.BezierSignature
43411  * @extends Roo.bootstrap.Component
43412  * Bootstrap BezierSignature class
43413  * This script refer to:
43414  *    Title: Signature Pad
43415  *    Author: szimek
43416  *    Availability: https://github.com/szimek/signature_pad
43417  *
43418  * @constructor
43419  * Create a new BezierSignature
43420  * @param {Object} config The config object
43421  */
43422
43423 Roo.bootstrap.BezierSignature = function(config){
43424     Roo.bootstrap.BezierSignature.superclass.constructor.call(this, config);
43425     this.addEvents({
43426         "resize" : true
43427     });
43428 };
43429
43430 Roo.extend(Roo.bootstrap.BezierSignature, Roo.bootstrap.Component,
43431 {
43432      
43433     curve_data: [],
43434     
43435     is_empty: true,
43436     
43437     mouse_btn_down: true,
43438     
43439     /**
43440      * @cfg {int} canvas height
43441      */
43442     canvas_height: '200px',
43443     
43444     /**
43445      * @cfg {float|function} Radius of a single dot.
43446      */ 
43447     dot_size: false,
43448     
43449     /**
43450      * @cfg {float} Minimum width of a line. Defaults to 0.5.
43451      */
43452     min_width: 0.5,
43453     
43454     /**
43455      * @cfg {float} Maximum width of a line. Defaults to 2.5.
43456      */
43457     max_width: 2.5,
43458     
43459     /**
43460      * @cfg {integer} Draw the next point at most once per every x milliseconds. Set it to 0 to turn off throttling. Defaults to 16.
43461      */
43462     throttle: 16,
43463     
43464     /**
43465      * @cfg {integer} Add the next point only if the previous one is farther than x pixels. Defaults to 5.
43466      */
43467     min_distance: 5,
43468     
43469     /**
43470      * @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.
43471      */
43472     bg_color: 'rgba(0, 0, 0, 0)',
43473     
43474     /**
43475      * @cfg {string} Color used to draw the lines. Can be any color format accepted by context.fillStyle. Defaults to "black".
43476      */
43477     dot_color: 'black',
43478     
43479     /**
43480      * @cfg {float} Weight used to modify new velocity based on the previous velocity. Defaults to 0.7.
43481      */ 
43482     velocity_filter_weight: 0.7,
43483     
43484     /**
43485      * @cfg {function} Callback when stroke begin. 
43486      */
43487     onBegin: false,
43488     
43489     /**
43490      * @cfg {function} Callback when stroke end.
43491      */
43492     onEnd: false,
43493     
43494     getAutoCreate : function()
43495     {
43496         var cls = 'roo-signature column';
43497         
43498         if(this.cls){
43499             cls += ' ' + this.cls;
43500         }
43501         
43502         var col_sizes = [
43503             'lg',
43504             'md',
43505             'sm',
43506             'xs'
43507         ];
43508         
43509         for(var i = 0; i < col_sizes.length; i++) {
43510             if(this[col_sizes[i]]) {
43511                 cls += " col-"+col_sizes[i]+"-"+this[col_sizes[i]];
43512             }
43513         }
43514         
43515         var cfg = {
43516             tag: 'div',
43517             cls: cls,
43518             cn: [
43519                 {
43520                     tag: 'div',
43521                     cls: 'roo-signature-body',
43522                     cn: [
43523                         {
43524                             tag: 'canvas',
43525                             cls: 'roo-signature-body-canvas',
43526                             height: this.canvas_height,
43527                             width: this.canvas_width
43528                         }
43529                     ]
43530                 },
43531                 {
43532                     tag: 'input',
43533                     type: 'file',
43534                     style: 'display: none'
43535                 }
43536             ]
43537         };
43538         
43539         return cfg;
43540     },
43541     
43542     initEvents: function() 
43543     {
43544         Roo.bootstrap.BezierSignature.superclass.initEvents.call(this);
43545         
43546         var canvas = this.canvasEl();
43547         
43548         // mouse && touch event swapping...
43549         canvas.dom.style.touchAction = 'none';
43550         canvas.dom.style.msTouchAction = 'none';
43551         
43552         this.mouse_btn_down = false;
43553         canvas.on('mousedown', this._handleMouseDown, this);
43554         canvas.on('mousemove', this._handleMouseMove, this);
43555         Roo.select('html').first().on('mouseup', this._handleMouseUp, this);
43556         
43557         if (window.PointerEvent) {
43558             canvas.on('pointerdown', this._handleMouseDown, this);
43559             canvas.on('pointermove', this._handleMouseMove, this);
43560             Roo.select('html').first().on('pointerup', this._handleMouseUp, this);
43561         }
43562         
43563         if ('ontouchstart' in window) {
43564             canvas.on('touchstart', this._handleTouchStart, this);
43565             canvas.on('touchmove', this._handleTouchMove, this);
43566             canvas.on('touchend', this._handleTouchEnd, this);
43567         }
43568         
43569         Roo.EventManager.onWindowResize(this.resize, this, true);
43570         
43571         // file input event
43572         this.fileEl().on('change', this.uploadImage, this);
43573         
43574         this.clear();
43575         
43576         this.resize();
43577     },
43578     
43579     resize: function(){
43580         
43581         var canvas = this.canvasEl().dom;
43582         var ctx = this.canvasElCtx();
43583         var img_data = false;
43584         
43585         if(canvas.width > 0) {
43586             var img_data = ctx.getImageData(0, 0, canvas.width, canvas.height);
43587         }
43588         // setting canvas width will clean img data
43589         canvas.width = 0;
43590         
43591         var style = window.getComputedStyle ? 
43592             getComputedStyle(this.el.dom, null) : this.el.dom.currentStyle;
43593             
43594         var padding_left = parseInt(style.paddingLeft) || 0;
43595         var padding_right = parseInt(style.paddingRight) || 0;
43596         
43597         canvas.width = this.el.dom.clientWidth - padding_left - padding_right;
43598         
43599         if(img_data) {
43600             ctx.putImageData(img_data, 0, 0);
43601         }
43602     },
43603     
43604     _handleMouseDown: function(e)
43605     {
43606         if (e.browserEvent.which === 1) {
43607             this.mouse_btn_down = true;
43608             this.strokeBegin(e);
43609         }
43610     },
43611     
43612     _handleMouseMove: function (e)
43613     {
43614         if (this.mouse_btn_down) {
43615             this.strokeMoveUpdate(e);
43616         }
43617     },
43618     
43619     _handleMouseUp: function (e)
43620     {
43621         if (e.browserEvent.which === 1 && this.mouse_btn_down) {
43622             this.mouse_btn_down = false;
43623             this.strokeEnd(e);
43624         }
43625     },
43626     
43627     _handleTouchStart: function (e) {
43628         
43629         e.preventDefault();
43630         if (e.browserEvent.targetTouches.length === 1) {
43631             // var touch = e.browserEvent.changedTouches[0];
43632             // this.strokeBegin(touch);
43633             
43634              this.strokeBegin(e); // assume e catching the correct xy...
43635         }
43636     },
43637     
43638     _handleTouchMove: function (e) {
43639         e.preventDefault();
43640         // var touch = event.targetTouches[0];
43641         // _this._strokeMoveUpdate(touch);
43642         this.strokeMoveUpdate(e);
43643     },
43644     
43645     _handleTouchEnd: function (e) {
43646         var wasCanvasTouched = e.target === this.canvasEl().dom;
43647         if (wasCanvasTouched) {
43648             e.preventDefault();
43649             // var touch = event.changedTouches[0];
43650             // _this._strokeEnd(touch);
43651             this.strokeEnd(e);
43652         }
43653     },
43654     
43655     reset: function () {
43656         this._lastPoints = [];
43657         this._lastVelocity = 0;
43658         this._lastWidth = (this.min_width + this.max_width) / 2;
43659         this.canvasElCtx().fillStyle = this.dot_color;
43660     },
43661     
43662     strokeMoveUpdate: function(e)
43663     {
43664         this.strokeUpdate(e);
43665         
43666         if (this.throttle) {
43667             this.throttleStroke(this.strokeUpdate, this.throttle);
43668         }
43669         else {
43670             this.strokeUpdate(e);
43671         }
43672     },
43673     
43674     strokeBegin: function(e)
43675     {
43676         var newPointGroup = {
43677             color: this.dot_color,
43678             points: []
43679         };
43680         
43681         if (typeof this.onBegin === 'function') {
43682             this.onBegin(e);
43683         }
43684         
43685         this.curve_data.push(newPointGroup);
43686         this.reset();
43687         this.strokeUpdate(e);
43688     },
43689     
43690     strokeUpdate: function(e)
43691     {
43692         var rect = this.canvasEl().dom.getBoundingClientRect();
43693         var point = new this.Point(e.xy[0] - rect.left, e.xy[1] - rect.top, new Date().getTime());
43694         var lastPointGroup = this.curve_data[this.curve_data.length - 1];
43695         var lastPoints = lastPointGroup.points;
43696         var lastPoint = lastPoints.length > 0 && lastPoints[lastPoints.length - 1];
43697         var isLastPointTooClose = lastPoint
43698             ? point.distanceTo(lastPoint) <= this.min_distance
43699             : false;
43700         var color = lastPointGroup.color;
43701         if (!lastPoint || !(lastPoint && isLastPointTooClose)) {
43702             var curve = this.addPoint(point);
43703             if (!lastPoint) {
43704                 this.drawDot({color: color, point: point});
43705             }
43706             else if (curve) {
43707                 this.drawCurve({color: color, curve: curve});
43708             }
43709             lastPoints.push({
43710                 time: point.time,
43711                 x: point.x,
43712                 y: point.y
43713             });
43714         }
43715     },
43716     
43717     strokeEnd: function(e)
43718     {
43719         this.strokeUpdate(e);
43720         if (typeof this.onEnd === 'function') {
43721             this.onEnd(e);
43722         }
43723     },
43724     
43725     addPoint:  function (point) {
43726         var _lastPoints = this._lastPoints;
43727         _lastPoints.push(point);
43728         if (_lastPoints.length > 2) {
43729             if (_lastPoints.length === 3) {
43730                 _lastPoints.unshift(_lastPoints[0]);
43731             }
43732             var widths = this.calculateCurveWidths(_lastPoints[1], _lastPoints[2]);
43733             var curve = this.Bezier.fromPoints(_lastPoints, widths, this);
43734             _lastPoints.shift();
43735             return curve;
43736         }
43737         return null;
43738     },
43739     
43740     calculateCurveWidths: function (startPoint, endPoint) {
43741         var velocity = this.velocity_filter_weight * endPoint.velocityFrom(startPoint) +
43742             (1 - this.velocity_filter_weight) * this._lastVelocity;
43743
43744         var newWidth = Math.max(this.max_width / (velocity + 1), this.min_width);
43745         var widths = {
43746             end: newWidth,
43747             start: this._lastWidth
43748         };
43749         
43750         this._lastVelocity = velocity;
43751         this._lastWidth = newWidth;
43752         return widths;
43753     },
43754     
43755     drawDot: function (_a) {
43756         var color = _a.color, point = _a.point;
43757         var ctx = this.canvasElCtx();
43758         var width = typeof this.dot_size === 'function' ? this.dot_size() : this.dot_size;
43759         ctx.beginPath();
43760         this.drawCurveSegment(point.x, point.y, width);
43761         ctx.closePath();
43762         ctx.fillStyle = color;
43763         ctx.fill();
43764     },
43765     
43766     drawCurve: function (_a) {
43767         var color = _a.color, curve = _a.curve;
43768         var ctx = this.canvasElCtx();
43769         var widthDelta = curve.endWidth - curve.startWidth;
43770         var drawSteps = Math.floor(curve.length()) * 2;
43771         ctx.beginPath();
43772         ctx.fillStyle = color;
43773         for (var i = 0; i < drawSteps; i += 1) {
43774         var t = i / drawSteps;
43775         var tt = t * t;
43776         var ttt = tt * t;
43777         var u = 1 - t;
43778         var uu = u * u;
43779         var uuu = uu * u;
43780         var x = uuu * curve.startPoint.x;
43781         x += 3 * uu * t * curve.control1.x;
43782         x += 3 * u * tt * curve.control2.x;
43783         x += ttt * curve.endPoint.x;
43784         var y = uuu * curve.startPoint.y;
43785         y += 3 * uu * t * curve.control1.y;
43786         y += 3 * u * tt * curve.control2.y;
43787         y += ttt * curve.endPoint.y;
43788         var width = curve.startWidth + ttt * widthDelta;
43789         this.drawCurveSegment(x, y, width);
43790         }
43791         ctx.closePath();
43792         ctx.fill();
43793     },
43794     
43795     drawCurveSegment: function (x, y, width) {
43796         var ctx = this.canvasElCtx();
43797         ctx.moveTo(x, y);
43798         ctx.arc(x, y, width, 0, 2 * Math.PI, false);
43799         this.is_empty = false;
43800     },
43801     
43802     clear: function()
43803     {
43804         var ctx = this.canvasElCtx();
43805         var canvas = this.canvasEl().dom;
43806         ctx.fillStyle = this.bg_color;
43807         ctx.clearRect(0, 0, canvas.width, canvas.height);
43808         ctx.fillRect(0, 0, canvas.width, canvas.height);
43809         this.curve_data = [];
43810         this.reset();
43811         this.is_empty = true;
43812     },
43813     
43814     fileEl: function()
43815     {
43816         return  this.el.select('input',true).first();
43817     },
43818     
43819     canvasEl: function()
43820     {
43821         return this.el.select('canvas',true).first();
43822     },
43823     
43824     canvasElCtx: function()
43825     {
43826         return this.el.select('canvas',true).first().dom.getContext('2d');
43827     },
43828     
43829     getImage: function(type)
43830     {
43831         if(this.is_empty) {
43832             return false;
43833         }
43834         
43835         // encryption ?
43836         return this.canvasEl().dom.toDataURL('image/'+type, 1);
43837     },
43838     
43839     drawFromImage: function(img_src)
43840     {
43841         var img = new Image();
43842         
43843         img.onload = function(){
43844             this.canvasElCtx().drawImage(img, 0, 0);
43845         }.bind(this);
43846         
43847         img.src = img_src;
43848         
43849         this.is_empty = false;
43850     },
43851     
43852     selectImage: function()
43853     {
43854         this.fileEl().dom.click();
43855     },
43856     
43857     uploadImage: function(e)
43858     {
43859         var reader = new FileReader();
43860         
43861         reader.onload = function(e){
43862             var img = new Image();
43863             img.onload = function(){
43864                 this.reset();
43865                 this.canvasElCtx().drawImage(img, 0, 0);
43866             }.bind(this);
43867             img.src = e.target.result;
43868         }.bind(this);
43869         
43870         reader.readAsDataURL(e.target.files[0]);
43871     },
43872     
43873     // Bezier Point Constructor
43874     Point: (function () {
43875         function Point(x, y, time) {
43876             this.x = x;
43877             this.y = y;
43878             this.time = time || Date.now();
43879         }
43880         Point.prototype.distanceTo = function (start) {
43881             return Math.sqrt(Math.pow(this.x - start.x, 2) + Math.pow(this.y - start.y, 2));
43882         };
43883         Point.prototype.equals = function (other) {
43884             return this.x === other.x && this.y === other.y && this.time === other.time;
43885         };
43886         Point.prototype.velocityFrom = function (start) {
43887             return this.time !== start.time
43888             ? this.distanceTo(start) / (this.time - start.time)
43889             : 0;
43890         };
43891         return Point;
43892     }()),
43893     
43894     
43895     // Bezier Constructor
43896     Bezier: (function () {
43897         function Bezier(startPoint, control2, control1, endPoint, startWidth, endWidth) {
43898             this.startPoint = startPoint;
43899             this.control2 = control2;
43900             this.control1 = control1;
43901             this.endPoint = endPoint;
43902             this.startWidth = startWidth;
43903             this.endWidth = endWidth;
43904         }
43905         Bezier.fromPoints = function (points, widths, scope) {
43906             var c2 = this.calculateControlPoints(points[0], points[1], points[2], scope).c2;
43907             var c3 = this.calculateControlPoints(points[1], points[2], points[3], scope).c1;
43908             return new Bezier(points[1], c2, c3, points[2], widths.start, widths.end);
43909         };
43910         Bezier.calculateControlPoints = function (s1, s2, s3, scope) {
43911             var dx1 = s1.x - s2.x;
43912             var dy1 = s1.y - s2.y;
43913             var dx2 = s2.x - s3.x;
43914             var dy2 = s2.y - s3.y;
43915             var m1 = { x: (s1.x + s2.x) / 2.0, y: (s1.y + s2.y) / 2.0 };
43916             var m2 = { x: (s2.x + s3.x) / 2.0, y: (s2.y + s3.y) / 2.0 };
43917             var l1 = Math.sqrt(dx1 * dx1 + dy1 * dy1);
43918             var l2 = Math.sqrt(dx2 * dx2 + dy2 * dy2);
43919             var dxm = m1.x - m2.x;
43920             var dym = m1.y - m2.y;
43921             var k = l2 / (l1 + l2);
43922             var cm = { x: m2.x + dxm * k, y: m2.y + dym * k };
43923             var tx = s2.x - cm.x;
43924             var ty = s2.y - cm.y;
43925             return {
43926                 c1: new scope.Point(m1.x + tx, m1.y + ty),
43927                 c2: new scope.Point(m2.x + tx, m2.y + ty)
43928             };
43929         };
43930         Bezier.prototype.length = function () {
43931             var steps = 10;
43932             var length = 0;
43933             var px;
43934             var py;
43935             for (var i = 0; i <= steps; i += 1) {
43936                 var t = i / steps;
43937                 var cx = this.point(t, this.startPoint.x, this.control1.x, this.control2.x, this.endPoint.x);
43938                 var cy = this.point(t, this.startPoint.y, this.control1.y, this.control2.y, this.endPoint.y);
43939                 if (i > 0) {
43940                     var xdiff = cx - px;
43941                     var ydiff = cy - py;
43942                     length += Math.sqrt(xdiff * xdiff + ydiff * ydiff);
43943                 }
43944                 px = cx;
43945                 py = cy;
43946             }
43947             return length;
43948         };
43949         Bezier.prototype.point = function (t, start, c1, c2, end) {
43950             return (start * (1.0 - t) * (1.0 - t) * (1.0 - t))
43951             + (3.0 * c1 * (1.0 - t) * (1.0 - t) * t)
43952             + (3.0 * c2 * (1.0 - t) * t * t)
43953             + (end * t * t * t);
43954         };
43955         return Bezier;
43956     }()),
43957     
43958     throttleStroke: function(fn, wait) {
43959       if (wait === void 0) { wait = 250; }
43960       var previous = 0;
43961       var timeout = null;
43962       var result;
43963       var storedContext;
43964       var storedArgs;
43965       var later = function () {
43966           previous = Date.now();
43967           timeout = null;
43968           result = fn.apply(storedContext, storedArgs);
43969           if (!timeout) {
43970               storedContext = null;
43971               storedArgs = [];
43972           }
43973       };
43974       return function wrapper() {
43975           var args = [];
43976           for (var _i = 0; _i < arguments.length; _i++) {
43977               args[_i] = arguments[_i];
43978           }
43979           var now = Date.now();
43980           var remaining = wait - (now - previous);
43981           storedContext = this;
43982           storedArgs = args;
43983           if (remaining <= 0 || remaining > wait) {
43984               if (timeout) {
43985                   clearTimeout(timeout);
43986                   timeout = null;
43987               }
43988               previous = now;
43989               result = fn.apply(storedContext, storedArgs);
43990               if (!timeout) {
43991                   storedContext = null;
43992                   storedArgs = [];
43993               }
43994           }
43995           else if (!timeout) {
43996               timeout = window.setTimeout(later, remaining);
43997           }
43998           return result;
43999       };
44000   }
44001   
44002 });
44003
44004  
44005
44006